98 lines
3.1 KiB
SQL
98 lines
3.1 KiB
SQL
CREATE TABLE IF NOT EXISTS market_data_snapshots (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
symbol TEXT NOT NULL,
|
|
asset_class TEXT NOT NULL DEFAULT 'us_equity',
|
|
feed TEXT NOT NULL DEFAULT 'iex',
|
|
metric TEXT NOT NULL,
|
|
price NUMERIC,
|
|
volume NUMERIC,
|
|
as_of TIMESTAMPTZ NOT NULL,
|
|
raw JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS market_data_snapshots_symbol_as_of_idx
|
|
ON market_data_snapshots (symbol, as_of DESC);
|
|
|
|
CREATE TABLE IF NOT EXISTS trading_config_templates (
|
|
name TEXT PRIMARY KEY,
|
|
config JSONB NOT NULL,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_trading_config (
|
|
firebase_uid TEXT PRIMARY KEY REFERENCES users (firebase_uid) ON DELETE CASCADE,
|
|
template_name TEXT REFERENCES trading_config_templates (name),
|
|
config JSONB NOT NULL DEFAULT '{}',
|
|
enabled BOOLEAN NOT NULL DEFAULT false,
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_trading_state (
|
|
firebase_uid TEXT PRIMARY KEY REFERENCES users (firebase_uid) ON DELETE CASCADE,
|
|
last_ingest_at TIMESTAMPTZ,
|
|
last_eval_at TIMESTAMPTZ,
|
|
context JSONB NOT NULL DEFAULT '{}',
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS trade_orders (
|
|
id UUID PRIMARY KEY,
|
|
firebase_uid TEXT NOT NULL REFERENCES users (firebase_uid) ON DELETE CASCADE,
|
|
client_order_id TEXT NOT NULL UNIQUE,
|
|
alpaca_order_id TEXT,
|
|
symbol TEXT NOT NULL,
|
|
side TEXT NOT NULL,
|
|
order_type TEXT NOT NULL,
|
|
notional_usd NUMERIC,
|
|
qty NUMERIC,
|
|
status TEXT NOT NULL,
|
|
question_id UUID REFERENCES questions (id),
|
|
rule_id TEXT,
|
|
submitted_at TIMESTAMPTZ,
|
|
filled_at TIMESTAMPTZ,
|
|
raw JSONB,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
INSERT INTO trading_config_templates (name, config)
|
|
VALUES (
|
|
'default_paper_watchlist',
|
|
'{
|
|
"version": 1,
|
|
"enabled": true,
|
|
"mode": "paper",
|
|
"data_inputs": [
|
|
{
|
|
"id": "primary_watchlist",
|
|
"source": "alpaca",
|
|
"asset_class": "us_equity",
|
|
"symbols": ["AAPL", "MSFT", "SPY"],
|
|
"feed": "iex",
|
|
"poll_interval_seconds": 60,
|
|
"metrics": ["last_trade", "daily_bar", "prev_close"]
|
|
}
|
|
],
|
|
"rules": [
|
|
{
|
|
"id": "dip_confirm",
|
|
"type": "price_below_pct_of_ref",
|
|
"symbol": "SPY",
|
|
"ref_metric": "prev_close",
|
|
"threshold_pct": -1.5,
|
|
"question_template": "SPY is down {{pct}}% vs yesterday. Swipe +10 to approve a small buy, -10 to skip.",
|
|
"on_answer_match": { "action": "propose_order", "side": "buy", "notional_usd": 10 }
|
|
}
|
|
],
|
|
"guardrails": {
|
|
"max_orders_per_day": 3,
|
|
"max_notional_usd_per_4h": 100,
|
|
"require_question_before_order": true,
|
|
"symbols_blocklist": []
|
|
}
|
|
}'::jsonb
|
|
)
|
|
ON CONFLICT (name) DO UPDATE SET
|
|
config = EXCLUDED.config,
|
|
updated_at = now();
|