-- ========================================= -- Extensions (optional but useful) -- ========================================= CREATE EXTENSION IF NOT EXISTS pgcrypto; -- for gen_random_uuid() if you want it later -- ========================================= -- 1) Identity & Sessions -- ========================================= CREATE TABLE IF NOT EXISTS app_user ( id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS app_session ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES app_user(id) ON DELETE CASCADE, created_at TIMESTAMPTZ NOT NULL, last_seen_at TIMESTAMPTZ, expires_at TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS idx_app_session_user_id ON app_session(user_id); CREATE INDEX IF NOT EXISTS idx_app_session_expires_at ON app_session(expires_at); -- ========================================= -- 2) Broker links + Zerodha auth artifacts -- Mirrors: user_brokers.json, zerodha_sessions.json, zerodha_request_tokens.json -- ========================================= -- Current connected broker record per user (plus "pending" fields) CREATE TABLE IF NOT EXISTS user_broker ( user_id TEXT PRIMARY KEY REFERENCES app_user(id) ON DELETE CASCADE, broker TEXT, connected BOOLEAN NOT NULL DEFAULT FALSE, access_token TEXT, connected_at TIMESTAMPTZ, api_key TEXT, user_name TEXT, broker_user_id TEXT, -- pending fields (as you described) pending_broker TEXT, pending_api_key TEXT, pending_api_secret TEXT, pending_started_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_user_broker_broker ON user_broker(broker); CREATE INDEX IF NOT EXISTS idx_user_broker_connected ON user_broker(connected); -- Zerodha session history (can be multiple per user) CREATE TABLE IF NOT EXISTS zerodha_session ( id BIGSERIAL PRIMARY KEY, user_id TEXT NOT NULL REFERENCES app_user(id) ON DELETE CASCADE, linked_at TIMESTAMPTZ NOT NULL, api_key TEXT, access_token TEXT, request_token TEXT, user_name TEXT, broker_user_id TEXT ); CREATE INDEX IF NOT EXISTS idx_zerodha_session_user_id ON zerodha_session(user_id); CREATE INDEX IF NOT EXISTS idx_zerodha_session_linked_at ON zerodha_session(linked_at); -- Latest/active request token per user (mirrors zerodha_request_tokens.json) CREATE TABLE IF NOT EXISTS zerodha_request_token ( user_id TEXT PRIMARY KEY REFERENCES app_user(id) ON DELETE CASCADE, request_token TEXT NOT NULL ); -- ========================================= -- 3) Strategy config + engine status -- Mirrors: strategy_config.json, engine_status.json -- ========================================= -- Keep it simple: single-row table (config "current") CREATE TABLE IF NOT EXISTS strategy_config ( id SMALLINT PRIMARY KEY DEFAULT 1, strategy TEXT, sip_amount NUMERIC, sip_frequency_value INTEGER, sip_frequency_unit TEXT, mode TEXT, -- paper/live broker TEXT, -- legacy/optional fields active BOOLEAN, frequency TEXT, frequency_days INTEGER, unit TEXT, next_run TIMESTAMPTZ ); -- single-row engine status CREATE TABLE IF NOT EXISTS engine_status ( id SMALLINT PRIMARY KEY DEFAULT 1, status TEXT NOT NULL, last_updated TIMESTAMPTZ NOT NULL ); -- ========================================= -- 4) Logs / event streams -- Mirrors: strategy_logs.json, engine.log (JSONL) -- ========================================= -- strategy_logs.json CREATE TABLE IF NOT EXISTS strategy_log ( seq BIGINT PRIMARY KEY, ts TIMESTAMPTZ NOT NULL, level TEXT, category TEXT, event TEXT, message TEXT, run_id TEXT, meta JSONB ); CREATE INDEX IF NOT EXISTS idx_strategy_log_ts ON strategy_log(ts); CREATE INDEX IF NOT EXISTS idx_strategy_log_run_id ON strategy_log(run_id); CREATE INDEX IF NOT EXISTS idx_strategy_log_event ON strategy_log(event); -- engine.log JSONL CREATE TABLE IF NOT EXISTS engine_event ( id BIGSERIAL PRIMARY KEY, ts TIMESTAMPTZ NOT NULL, event TEXT, data JSONB, message TEXT, meta JSONB ); CREATE INDEX IF NOT EXISTS idx_engine_event_ts ON engine_event(ts); CREATE INDEX IF NOT EXISTS idx_engine_event_event ON engine_event(event); -- ========================================= -- 5) Engine state (paper + derived) -- Mirrors: state_paper.json, state.json -- ========================================= -- state_paper.json CREATE TABLE IF NOT EXISTS engine_state_paper ( id SMALLINT PRIMARY KEY DEFAULT 1, initial_cash NUMERIC, cash NUMERIC, total_invested NUMERIC, nifty_units NUMERIC, gold_units NUMERIC, last_sip_ts TIMESTAMPTZ, last_run TIMESTAMPTZ, sip_frequency_value INTEGER, sip_frequency_unit TEXT ); -- state.json (lighter) CREATE TABLE IF NOT EXISTS engine_state ( id SMALLINT PRIMARY KEY DEFAULT 1, total_invested NUMERIC, nifty_units NUMERIC, gold_units NUMERIC, last_sip_ts TIMESTAMPTZ, last_run TIMESTAMPTZ ); -- ========================================= -- 6) Paper broker (positions, orders, trades, equity curve) -- Mirrors: paper_broker.json -- ========================================= -- overall cash snapshot (paper_broker.json top-level cash) CREATE TABLE IF NOT EXISTS paper_broker_account ( id SMALLINT PRIMARY KEY DEFAULT 1, cash NUMERIC NOT NULL ); -- positions map: symbol -> qty, avg_price, last_price CREATE TABLE IF NOT EXISTS paper_position ( symbol TEXT PRIMARY KEY, qty NUMERIC NOT NULL, avg_price NUMERIC, last_price NUMERIC, updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- orders list CREATE TABLE IF NOT EXISTS paper_order ( id TEXT PRIMARY KEY, symbol TEXT NOT NULL, side TEXT NOT NULL, -- buy/sell qty NUMERIC NOT NULL, price NUMERIC, status TEXT NOT NULL, -- new/filled/cancelled/rejected etc timestamp TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS idx_paper_order_ts ON paper_order(timestamp); CREATE INDEX IF NOT EXISTS idx_paper_order_symbol ON paper_order(symbol); CREATE INDEX IF NOT EXISTS idx_paper_order_status ON paper_order(status); -- trades list CREATE TABLE IF NOT EXISTS paper_trade ( id TEXT PRIMARY KEY, order_id TEXT REFERENCES paper_order(id) ON DELETE SET NULL, symbol TEXT NOT NULL, side TEXT NOT NULL, qty NUMERIC NOT NULL, price NUMERIC NOT NULL, timestamp TIMESTAMPTZ NOT NULL ); CREATE INDEX IF NOT EXISTS idx_paper_trade_ts ON paper_trade(timestamp); CREATE INDEX IF NOT EXISTS idx_paper_trade_symbol ON paper_trade(symbol); -- equity_curve list CREATE TABLE IF NOT EXISTS paper_equity_curve ( timestamp TIMESTAMPTZ PRIMARY KEY, equity NUMERIC NOT NULL, pnl NUMERIC ); CREATE INDEX IF NOT EXISTS idx_paper_equity_curve_ts ON paper_equity_curve(timestamp); -- ========================================= -- 7) MTM ledger + event ledger -- Mirrors: mtm_ledger.csv, ledger.csv -- ========================================= CREATE TABLE IF NOT EXISTS mtm_ledger ( timestamp TIMESTAMPTZ PRIMARY KEY, nifty_units NUMERIC, gold_units NUMERIC, nifty_price NUMERIC, gold_price NUMERIC, nifty_value NUMERIC, gold_value NUMERIC, portfolio_value NUMERIC, total_invested NUMERIC, pnl NUMERIC ); CREATE INDEX IF NOT EXISTS idx_mtm_ledger_ts ON mtm_ledger(timestamp); CREATE TABLE IF NOT EXISTS event_ledger ( id BIGSERIAL PRIMARY KEY, timestamp TIMESTAMPTZ NOT NULL, event TEXT NOT NULL, nifty_units NUMERIC, gold_units NUMERIC, nifty_price NUMERIC, gold_price NUMERIC, amount NUMERIC ); CREATE INDEX IF NOT EXISTS idx_event_ledger_ts ON event_ledger(timestamp); CREATE INDEX IF NOT EXISTS idx_event_ledger_event ON event_ledger(event); -- ========================================= -- 8) Market data cache (History Cache CSVs) -- Mirrors: SYMBOL.csv {Date, Close} -- ========================================= CREATE TABLE IF NOT EXISTS market_close ( symbol TEXT NOT NULL, date DATE NOT NULL, close NUMERIC NOT NULL, PRIMARY KEY (symbol, date) ); CREATE INDEX IF NOT EXISTS idx_market_close_symbol ON market_close(symbol); CREATE INDEX IF NOT EXISTS idx_market_close_date ON market_close(date);