feat: cycle

This commit is contained in:
OpenSquared
2026-07-15 14:59:11 +02:00
parent 2d474c9194
commit 16ccc7c2c7
6 changed files with 466 additions and 45 deletions

View File

@@ -169,6 +169,23 @@ def init_db():
"ALTER TABLE wavelet_watchlist_signals ADD COLUMN energy REAL",
"ALTER TABLE wavelet_watchlist_signals ADD COLUMN ridge_period_days REAL",
"ALTER TABLE wavelet_watchlist_signals ADD COLUMN params_json TEXT",
# Cycle Actions — standalone "refresh-price-data" action, OHLCV cache
"""CREATE TABLE IF NOT EXISTS price_data_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ticker TEXT NOT NULL,
date TEXT NOT NULL,
open REAL, high REAL, low REAL, close REAL, volume REAL,
cached_at TEXT DEFAULT (datetime('now')),
UNIQUE(ticker, date)
)""",
# Cycle Actions — standalone "compute-indicators" action, one row per call per ticker
"""CREATE TABLE IF NOT EXISTS instrument_indicators (
id INTEGER PRIMARY KEY AUTOINCREMENT,
ticker TEXT NOT NULL,
computed_at TEXT DEFAULT (datetime('now')),
horizon_days INTEGER,
indicators_json TEXT DEFAULT '{}'
)""",
]:
try:
c.execute(_sql)
@@ -183,6 +200,14 @@ def init_db():
c.execute("CREATE INDEX IF NOT EXISTS idx_atp_session_status ON ai_trade_proposals(session_id, status)")
except Exception:
pass
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_pdc_ticker_date ON price_data_cache(ticker, date DESC)")
except Exception:
pass
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_ii_ticker_date ON instrument_indicators(ticker, computed_at DESC)")
except Exception:
pass
try:
c.execute("CREATE INDEX IF NOT EXISTS idx_chat_session_date ON ai_chat_messages(session_id, created_at)")
@@ -3441,6 +3466,72 @@ def resolve_ai_trade_proposal(proposal_id: str, status: str, portfolio_id: Optio
conn.close()
# ── Cycle Actions — standalone price data cache + indicator snapshots ──────────
def upsert_price_data(ticker: str, rows: List[Dict[str, Any]]) -> int:
"""rows: [{date, open, high, low, close, volume}, ...]. Returns rows written."""
if not rows:
return 0
conn = get_conn()
n = 0
for r in rows:
conn.execute(
"""INSERT INTO price_data_cache (ticker, date, open, high, low, close, volume, cached_at)
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'))
ON CONFLICT(ticker, date) DO UPDATE SET
open=excluded.open, high=excluded.high, low=excluded.low,
close=excluded.close, volume=excluded.volume, cached_at=excluded.cached_at""",
(ticker.upper(), r.get("date"), r.get("open"), r.get("high"), r.get("low"), r.get("close"), r.get("volume")),
)
n += 1
conn.commit()
conn.close()
return n
def get_cached_price_data(ticker: str, limit: int = 90) -> List[Dict[str, Any]]:
conn = get_conn()
rows = conn.execute(
"SELECT * FROM price_data_cache WHERE ticker=? ORDER BY date DESC LIMIT ?",
(ticker.upper(), limit),
).fetchall()
conn.close()
return [dict(r) for r in rows]
def save_instrument_indicators(ticker: str, horizon_days: int, indicators: Dict[str, Any]) -> None:
conn = get_conn()
conn.execute(
"INSERT INTO instrument_indicators (ticker, horizon_days, indicators_json) VALUES (?, ?, ?)",
(ticker.upper(), horizon_days, json.dumps(indicators)),
)
conn.commit()
conn.close()
def get_latest_instrument_indicators() -> List[Dict[str, Any]]:
"""Most recent indicators row per ticker."""
conn = get_conn()
rows = conn.execute(
"""SELECT i.* FROM instrument_indicators i
INNER JOIN (
SELECT ticker, MAX(computed_at) AS max_computed_at
FROM instrument_indicators GROUP BY ticker
) latest ON i.ticker = latest.ticker AND i.computed_at = latest.max_computed_at
ORDER BY i.ticker"""
).fetchall()
conn.close()
out = []
for r in rows:
d = dict(r)
try:
d["indicators"] = json.loads(d.pop("indicators_json") or "{}")
except Exception:
d["indicators"] = {}
out.append(d)
return out
# ── System Logs ───────────────────────────────────────────────────────────────
def log_system_event(