mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-01 17:17:24 -04:00
Major improvements: - Fixed all 42 broken tests (database connection leaks) - Added db_connection() context manager for proper cleanup - Created comprehensive test suites for undertested modules New test coverage: - tools/general_tools.py: 26 tests (97% coverage) - tools/price_tools.py: 11 tests (validates NASDAQ symbols, date handling) - api/price_data_manager.py: 12 tests (85% coverage) - api/routes/results_v2.py: 3 tests (98% coverage) - agent/reasoning_summarizer.py: 2 tests (87% coverage) - api/routes/period_metrics.py: 2 edge case tests (100% coverage) - agent/mock_provider: 1 test (100% coverage) Database fixes: - Added db_connection() context manager to prevent leaks - Updated 16+ test files to use context managers - Fixed drop_all_tables() to match new schema - Added CHECK constraint for action_type - Added ON DELETE CASCADE to trading_days foreign key Test improvements: - Updated SQL INSERT statements with all required fields - Fixed date parameter handling in API integration tests - Added edge case tests for validation functions - Fixed import errors across test suite Results: - Total coverage: 84.81% (was 61%) - Tests passing: 406 (was 364 with 42 failures) - Total lines covered: 6364 of 7504 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
132 lines
4.0 KiB
Python
132 lines
4.0 KiB
Python
"""Migration: Create trading_days, holdings, and actions tables."""
|
|
|
|
import sqlite3
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from api.database import Database
|
|
|
|
|
|
def create_trading_days_schema(db: "Database") -> None:
|
|
"""Create new schema for day-centric trading results.
|
|
|
|
Args:
|
|
db: Database instance to apply migration to
|
|
"""
|
|
# Enable foreign key constraint enforcement
|
|
db.connection.execute("PRAGMA foreign_keys = ON")
|
|
|
|
# Create jobs table if it doesn't exist (prerequisite for foreign key)
|
|
db.connection.execute("""
|
|
CREATE TABLE IF NOT EXISTS jobs (
|
|
job_id TEXT PRIMARY KEY,
|
|
config_path TEXT NOT NULL,
|
|
status TEXT NOT NULL CHECK(status IN ('pending', 'downloading_data', 'running', 'completed', 'partial', 'failed')),
|
|
date_range TEXT NOT NULL,
|
|
models TEXT NOT NULL,
|
|
created_at TEXT NOT NULL,
|
|
started_at TEXT,
|
|
updated_at TEXT,
|
|
completed_at TEXT,
|
|
total_duration_seconds REAL,
|
|
error TEXT,
|
|
warnings TEXT
|
|
)
|
|
""")
|
|
|
|
# Create trading_days table
|
|
db.connection.execute("""
|
|
CREATE TABLE IF NOT EXISTS trading_days (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
job_id TEXT NOT NULL,
|
|
model TEXT NOT NULL,
|
|
date TEXT NOT NULL,
|
|
|
|
-- Starting position (cash only, holdings from previous day)
|
|
starting_cash REAL NOT NULL,
|
|
starting_portfolio_value REAL NOT NULL,
|
|
|
|
-- Daily performance metrics
|
|
daily_profit REAL NOT NULL,
|
|
daily_return_pct REAL NOT NULL,
|
|
|
|
-- Ending state (cash only, holdings in separate table)
|
|
ending_cash REAL NOT NULL,
|
|
ending_portfolio_value REAL NOT NULL,
|
|
|
|
-- Reasoning
|
|
reasoning_summary TEXT,
|
|
reasoning_full TEXT,
|
|
|
|
-- Metadata
|
|
total_actions INTEGER DEFAULT 0,
|
|
session_duration_seconds REAL,
|
|
days_since_last_trading INTEGER DEFAULT 1,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
completed_at TIMESTAMP,
|
|
|
|
UNIQUE(job_id, model, date),
|
|
FOREIGN KEY (job_id) REFERENCES jobs(job_id) ON DELETE CASCADE
|
|
)
|
|
""")
|
|
|
|
# Create index for lookups
|
|
db.connection.execute("""
|
|
CREATE INDEX IF NOT EXISTS idx_trading_days_lookup
|
|
ON trading_days(job_id, model, date)
|
|
""")
|
|
|
|
# Create holdings table (ending positions only)
|
|
db.connection.execute("""
|
|
CREATE TABLE IF NOT EXISTS holdings (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trading_day_id INTEGER NOT NULL,
|
|
symbol TEXT NOT NULL,
|
|
quantity INTEGER NOT NULL,
|
|
|
|
FOREIGN KEY (trading_day_id) REFERENCES trading_days(id) ON DELETE CASCADE,
|
|
UNIQUE(trading_day_id, symbol)
|
|
)
|
|
""")
|
|
|
|
# Create index for holdings lookups
|
|
db.connection.execute("""
|
|
CREATE INDEX IF NOT EXISTS idx_holdings_day
|
|
ON holdings(trading_day_id)
|
|
""")
|
|
|
|
# Create actions table (trade ledger)
|
|
db.connection.execute("""
|
|
CREATE TABLE IF NOT EXISTS actions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
trading_day_id INTEGER NOT NULL,
|
|
|
|
action_type TEXT NOT NULL CHECK(action_type IN ('buy', 'sell', 'hold')),
|
|
symbol TEXT,
|
|
quantity INTEGER,
|
|
price REAL,
|
|
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
FOREIGN KEY (trading_day_id) REFERENCES trading_days(id) ON DELETE CASCADE
|
|
)
|
|
""")
|
|
|
|
# Create index for actions lookups
|
|
db.connection.execute("""
|
|
CREATE INDEX IF NOT EXISTS idx_actions_day
|
|
ON actions(trading_day_id)
|
|
""")
|
|
|
|
db.connection.commit()
|
|
|
|
|
|
def drop_old_positions_table(db: "Database") -> None:
|
|
"""Drop deprecated positions table after migration complete.
|
|
|
|
Args:
|
|
db: Database instance
|
|
"""
|
|
db.connection.execute("DROP TABLE IF EXISTS positions")
|
|
db.connection.commit()
|