2026-02-01 14:14:57 +00:00

291 lines
8.4 KiB
SQL

-- =========================================
-- 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);