113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
|
|
from backend.database.migrations.runner import Migration
|
|
|
|
|
|
def upgrade(connection: sqlite3.Connection) -> None:
|
|
connection.execute("ALTER TABLE watchlist_entries ADD COLUMN remark TEXT NOT NULL DEFAULT ''")
|
|
statements = (
|
|
"""
|
|
CREATE TABLE review_notes (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
code TEXT NOT NULL DEFAULT '',
|
|
stock_name TEXT NOT NULL DEFAULT '',
|
|
trade_date TEXT NOT NULL,
|
|
summary TEXT NOT NULL DEFAULT '',
|
|
content TEXT NOT NULL DEFAULT '',
|
|
plan TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
UNIQUE(user_id, code, trade_date)
|
|
)
|
|
""",
|
|
"""
|
|
CREATE TABLE trade_entries (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
trade_date TEXT NOT NULL,
|
|
code TEXT NOT NULL,
|
|
name TEXT NOT NULL,
|
|
action TEXT NOT NULL CHECK (action IN ('buy','sell','add','trim','watch')),
|
|
price REAL NOT NULL CHECK (price >= 0),
|
|
quantity INTEGER NOT NULL DEFAULT 0 CHECK (quantity >= 0),
|
|
position_pct REAL CHECK (position_pct BETWEEN 0 AND 100),
|
|
pnl_amount REAL,
|
|
pnl_pct REAL,
|
|
emotion TEXT NOT NULL CHECK (
|
|
emotion IN ('calm','confident','hesitant','anxious','impulsive')
|
|
),
|
|
tags_json TEXT NOT NULL DEFAULT '[]',
|
|
thesis TEXT NOT NULL DEFAULT '',
|
|
execution TEXT NOT NULL DEFAULT '',
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL
|
|
)
|
|
""",
|
|
"""
|
|
CREATE INDEX trade_entries_user_date_idx
|
|
ON trade_entries(user_id, trade_date DESC, id DESC)
|
|
""",
|
|
"""
|
|
CREATE TABLE alerts (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
kind TEXT NOT NULL CHECK (kind IN ('manual','strategy_t1','strategy_t5')),
|
|
title TEXT NOT NULL,
|
|
content TEXT NOT NULL DEFAULT '',
|
|
available_date TEXT NOT NULL,
|
|
code TEXT NOT NULL DEFAULT '',
|
|
dedupe_key TEXT NOT NULL,
|
|
is_read INTEGER NOT NULL DEFAULT 0 CHECK (is_read IN (0,1)),
|
|
read_at TEXT,
|
|
created_at TEXT NOT NULL,
|
|
updated_at TEXT NOT NULL,
|
|
UNIQUE(user_id, dedupe_key)
|
|
)
|
|
""",
|
|
"""
|
|
CREATE INDEX alerts_user_due_idx
|
|
ON alerts(user_id, available_date, is_read, id DESC)
|
|
""",
|
|
"""
|
|
CREATE TABLE review_assistant_messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL CHECK (role IN ('user','assistant')),
|
|
content TEXT NOT NULL,
|
|
context_date TEXT NOT NULL,
|
|
request_id TEXT REFERENCES llm_requests(id) ON DELETE SET NULL,
|
|
status TEXT NOT NULL CHECK (status IN ('complete','stopped','error')),
|
|
created_at TEXT NOT NULL
|
|
)
|
|
""",
|
|
"""
|
|
CREATE INDEX review_assistant_user_idx
|
|
ON review_assistant_messages(user_id, id DESC)
|
|
""",
|
|
)
|
|
for statement in statements:
|
|
connection.execute(statement)
|
|
|
|
|
|
def downgrade(connection: sqlite3.Connection) -> None:
|
|
for table in (
|
|
"review_assistant_messages",
|
|
"alerts",
|
|
"trade_entries",
|
|
"review_notes",
|
|
):
|
|
connection.execute(f"DROP TABLE {table}")
|
|
connection.execute("ALTER TABLE watchlist_entries DROP COLUMN remark")
|
|
|
|
|
|
MIGRATION = Migration(
|
|
version=10,
|
|
name="create_private_review_domain",
|
|
signature="review:v1:notes-trades-alerts-assistant",
|
|
upgrade=upgrade,
|
|
downgrade=downgrade,
|
|
)
|