Files
A_Share_DP/src/ashare_dp/signals/store.py
T
jackyu66gitandClaude cc95bbb638 v10: Trading OS — 5-subsystem architecture + Signal Intelligence + Dashboard Command Center
Architecture:
- Restructure into 5 subsystems: data/, features/, market/, signals/, execution/, apps/
- Unified ts_code conversion in core/codes.py (idempotent, kills 4 duplicate copies)
- analytics_conn() + kline_glob() — zero hardcoded DB/Parquet paths
- Fixed double-suffix bug (.SZ.SZ) in backfill pipeline root cause

Signal Intelligence (the moat):
- 14 signal types: EMA52, Vegas, Chan, ORB, Gap, NR7, Inside Bar
- 640K+ historical signal instances across 8 backfilled types
- Multi-signal Expectancy Engine with breadth-similarity matching
- Signal backfill CLI: ashare-dp backfill signals

Market Intelligence:
- 8 engines: State, Leadership, Opportunity, Flow, Sentiment, Memory, Knowledge Graph, Recommendations
- Real limit-up/down sentiment via akshare (108 ZT, 19 DT, 52 broken board)
- Knowledge Graph: 8 themes × 30+ concepts with keyword matching
- Money-flow stock recommendations with entry/stop/target trade plans

Dashboard Command Center:
- Decision-first layout: COMMAND → WHERE → WHY → RISK → EXPECTANCY
- Multi-signal Expectancy comparison table (8 types ranked by WR)
- Theme Map visualization with rotation detection
- Intraday Replay infrastructure (30min state snapshots)
- RECOMMENDATIONS card with actionable trade plans

Trading Memory:
- trade_log table + POST/GET/PUT API for trade recording
- Performance stats aggregation

Code Quality:
- 0 hardcoded DB paths, 0 REPLACE hacks, 0 dead ts_code copies
- EMA52 screening deduplicated (CLI + scheduler share one function)
- read_parquet_sql() helper for 28 duplicate patterns
- 6 bugs fixed from code review (NR7 window, theme matching, column indices, etc.)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-06 12:11:42 +08:00

73 lines
2.7 KiB
Python

"""Signal Instance Store — CRUD for signal_instance table.
Separate from detection logic. Detection finds signals,
store persists them. Both used by backfill pipeline.
"""
from __future__ import annotations
import pandas as pd
from loguru import logger
from ashare_dp.data.store.database import get_db
def store_signals(df: pd.DataFrame) -> int:
"""Store detected signals in signal_instance table.
Uses ON CONFLICT DO NOTHING to safely handle re-runs.
Returns number of rows stored.
"""
if df.empty:
return 0
with get_db(read_only=False) as db:
stored = 0
for _, row in df.iterrows():
try:
db.execute(
"""
INSERT INTO signal_instance
(signal_type, ts_code, trade_date, signal_price,
state_breadth, state_volatility,
return_5d, return_10d, return_20d,
max_return_5d, max_drawdown_5d,
outcome_known)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, TRUE)
ON CONFLICT (signal_type, ts_code, trade_date) DO NOTHING
""",
(
row["signal_type"],
row["ts_code"],
row["trade_date"],
float(row["signal_price"]),
float(row["daily_advance_ratio"]) if pd.notna(row.get("daily_advance_ratio")) else 0.5,
float(row["daily_range"]) if pd.notna(row.get("daily_range")) else 0.02,
float(row["ret_5d"]) if pd.notna(row.get("ret_5d")) else None,
float(row["ret_10d"]) if pd.notna(row.get("ret_10d")) else None,
float(row["ret_20d"]) if pd.notna(row.get("ret_20d")) else None,
float(row["max_return_5d"]) if pd.notna(row.get("max_return_5d")) else None,
float(row["max_dd_5d"]) if pd.notna(row.get("max_dd_5d")) else None,
),
)
stored += 1
except Exception as e:
logger.debug(f"Signal store failed for {row.get('ts_code')}: {e}")
logger.info(f"Stored {stored} signal instances")
return stored
def count_signals(signal_type: str | None = None) -> int:
"""Count stored signal instances, optionally filtered by type."""
with get_db(read_only=True) as db:
if signal_type:
rows = db.query(
"SELECT COUNT(*) FROM signal_instance WHERE signal_type = ?",
(signal_type,),
)
else:
rows = db.query("SELECT COUNT(*) FROM signal_instance")
return int(rows[0][0]) if rows else 0