You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
6.1 KiB
Python
184 lines
6.1 KiB
Python
"""SQLite table definitions for the paper trading system."""
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
SCHEMA_SQL = """
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
session_id TEXT PRIMARY KEY,
|
|
session_name TEXT NOT NULL,
|
|
config_path TEXT NOT NULL,
|
|
initial_equity REAL NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
parking_preset TEXT,
|
|
idle_alpha_preset TEXT,
|
|
form4_sleeve_preset TEXT,
|
|
ownership_sleeve_preset TEXT,
|
|
risk_off_alpha_sleeve_preset TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS strategy_states (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
session_id TEXT NOT NULL REFERENCES sessions(session_id),
|
|
symbol TEXT NOT NULL,
|
|
event_id TEXT NOT NULL,
|
|
engine_id TEXT NOT NULL DEFAULT 'default',
|
|
order_id TEXT,
|
|
entry_date TEXT NOT NULL,
|
|
stop_price REAL NOT NULL,
|
|
target_price REAL NOT NULL,
|
|
current_stop REAL NOT NULL,
|
|
peak_price REAL NOT NULL,
|
|
days_held INTEGER NOT NULL DEFAULT 0,
|
|
trade_direction TEXT NOT NULL DEFAULT 'long',
|
|
candidate_json TEXT NOT NULL,
|
|
plan_json TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'open',
|
|
UNIQUE(session_id, symbol, status)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS session_state (
|
|
session_id TEXT PRIMARY KEY REFERENCES sessions(session_id),
|
|
consecutive_losses INTEGER NOT NULL DEFAULT 0,
|
|
cooldown_remaining INTEGER NOT NULL DEFAULT 0,
|
|
kill_switch_triggered INTEGER NOT NULL DEFAULT 0,
|
|
daily_new_risk_used REAL NOT NULL DEFAULT 0.0,
|
|
last_processed_date TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS processed_events (
|
|
session_id TEXT NOT NULL,
|
|
event_id TEXT NOT NULL,
|
|
processed_date TEXT NOT NULL,
|
|
action TEXT NOT NULL,
|
|
skip_reason TEXT,
|
|
PRIMARY KEY (session_id, event_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS trades (
|
|
trade_id TEXT PRIMARY KEY,
|
|
session_id TEXT NOT NULL,
|
|
symbol TEXT NOT NULL,
|
|
engine_id TEXT,
|
|
capital_bucket_id TEXT,
|
|
entry_date TEXT,
|
|
exit_date TEXT,
|
|
entry_price REAL,
|
|
exit_price REAL,
|
|
exit_reason TEXT,
|
|
shares INTEGER,
|
|
net_pnl REAL,
|
|
r_multiple REAL,
|
|
holding_days INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS daily_snapshots (
|
|
session_id TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
equity REAL NOT NULL,
|
|
cash REAL NOT NULL,
|
|
market_value REAL NOT NULL,
|
|
daily_pnl REAL,
|
|
total_pnl REAL,
|
|
drawdown_pct REAL,
|
|
open_position_count INTEGER,
|
|
PRIMARY KEY (session_id, date)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS processed_dates (
|
|
session_id TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
PRIMARY KEY (session_id, date)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS processed_phases (
|
|
session_id TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
phase TEXT NOT NULL,
|
|
PRIMARY KEY (session_id, date, phase)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS parking_state (
|
|
session_id TEXT PRIMARY KEY REFERENCES sessions(session_id),
|
|
symbol TEXT NOT NULL,
|
|
entry_date TEXT NOT NULL,
|
|
qty INTEGER NOT NULL,
|
|
avg_price REAL NOT NULL,
|
|
entry_value REAL NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
peak_price REAL NOT NULL DEFAULT 0,
|
|
gate_in_sgov INTEGER NOT NULL DEFAULT 0,
|
|
committed_target TEXT NOT NULL DEFAULT '',
|
|
pending_target TEXT NOT NULL DEFAULT '',
|
|
pending_days INTEGER NOT NULL DEFAULT 0,
|
|
sgov_entry_value REAL NOT NULL DEFAULT 0,
|
|
sold_today INTEGER NOT NULL DEFAULT 0,
|
|
overlay_brake_cooldown INTEGER NOT NULL DEFAULT 0,
|
|
overlay_hold_days INTEGER NOT NULL DEFAULT 0
|
|
);
|
|
"""
|
|
|
|
|
|
def create_schema(db_path: str | Path) -> None:
|
|
"""Initialize the SQLite database with the paper trading schema."""
|
|
conn = sqlite3.connect(str(db_path))
|
|
try:
|
|
conn.executescript(SCHEMA_SQL)
|
|
# Migration: add parking_preset column to existing databases
|
|
try:
|
|
conn.execute("ALTER TABLE sessions ADD COLUMN parking_preset TEXT")
|
|
conn.commit()
|
|
except Exception:
|
|
pass # column already exists
|
|
try:
|
|
conn.execute("ALTER TABLE sessions ADD COLUMN idle_alpha_preset TEXT")
|
|
conn.commit()
|
|
except Exception:
|
|
pass # column already exists
|
|
try:
|
|
conn.execute("ALTER TABLE sessions ADD COLUMN form4_sleeve_preset TEXT")
|
|
conn.commit()
|
|
except Exception:
|
|
pass # column already exists
|
|
try:
|
|
conn.execute("ALTER TABLE sessions ADD COLUMN ownership_sleeve_preset TEXT")
|
|
conn.commit()
|
|
except Exception:
|
|
pass # column already exists
|
|
try:
|
|
conn.execute("ALTER TABLE sessions ADD COLUMN risk_off_alpha_sleeve_preset TEXT")
|
|
conn.commit()
|
|
except Exception:
|
|
pass # column already exists
|
|
# Migration: add parking_state columns for v2 (top-up, hysteresis, etc.)
|
|
for col, dtype, default in [
|
|
("peak_price", "REAL", "0"),
|
|
("gate_in_sgov", "INTEGER", "0"),
|
|
("committed_target", "TEXT", "''"),
|
|
("pending_target", "TEXT", "''"),
|
|
("pending_days", "INTEGER", "0"),
|
|
("sgov_entry_value", "REAL", "0"),
|
|
("sold_today", "INTEGER", "0"),
|
|
("overlay_brake_cooldown", "INTEGER", "0"),
|
|
("overlay_hold_days", "INTEGER", "0"),
|
|
]:
|
|
try:
|
|
conn.execute(f"ALTER TABLE parking_state ADD COLUMN {col} {dtype} NOT NULL DEFAULT {default}")
|
|
conn.commit()
|
|
except Exception:
|
|
pass
|
|
for col, dtype in [
|
|
("engine_id", "TEXT"),
|
|
("capital_bucket_id", "TEXT"),
|
|
("entry_shares", "INTEGER"),
|
|
]:
|
|
try:
|
|
conn.execute(f"ALTER TABLE trades ADD COLUMN {col} {dtype}")
|
|
conn.commit()
|
|
except Exception:
|
|
pass
|
|
finally:
|
|
conn.close()
|