支持手动指定品种和日期进行打分
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ app = FastAPI(title="期货数据采集与打分服务")
|
||||
class RunRequest(BaseModel):
|
||||
ts_code: Optional[str] = None
|
||||
symbol: str = "FG"
|
||||
trade_date: Optional[str] = None
|
||||
|
||||
|
||||
class RunResponse(BaseModel):
|
||||
@@ -44,7 +45,7 @@ def run_pipeline(req: RunRequest):
|
||||
|
||||
df = fetcher.fetch_contract(ts_code)
|
||||
storage.save_candles(df)
|
||||
result = scorer.score_daily(df)
|
||||
result = scorer.score_daily(df, req.trade_date)
|
||||
storage.save_score(result)
|
||||
|
||||
push_title = f"{result.ts_code.split('.')[0]} {result.trade_date}"
|
||||
|
||||
@@ -4,18 +4,18 @@ import sys
|
||||
from . import contracts, fetcher, notifier, scorer, storage
|
||||
|
||||
|
||||
def run(ts_code: str) -> int:
|
||||
def run(ts_code: str, trade_date: Optional[str] = None) -> int:
|
||||
storage.init_db()
|
||||
|
||||
print(f"[1/4] 拉取 {ts_code} 数据...")
|
||||
df = fetcher.fetch_contract(ts_code)
|
||||
print(f" 返回 {len(df)} 行")
|
||||
|
||||
print(f"[2/4] 写入/更新 SQLite...")
|
||||
print(f"[2/4] 写入/更新 PostgreSQL...")
|
||||
storage.save_candles(df)
|
||||
|
||||
print(f"[3/4] 计算打分...")
|
||||
result = scorer.score_daily(df)
|
||||
result = scorer.score_daily(df, trade_date)
|
||||
|
||||
print(f"[4/4] 保存打分结果...")
|
||||
storage.save_score(result)
|
||||
@@ -61,7 +61,7 @@ def run(ts_code: str) -> int:
|
||||
print(f" 30日前持仓量: {ld['oi_before']:,.0f}")
|
||||
print(f" 持仓变化幅度: {ld['change_pct']:+.2f}%")
|
||||
|
||||
print(f"\n[OK] 数据已持久化到 SQLite")
|
||||
print(f"\n[OK] 数据已持久化到 PostgreSQL")
|
||||
|
||||
push_title = f"{result.ts_code.split('.')[0]} {result.trade_date}"
|
||||
push_body = (
|
||||
@@ -86,12 +86,18 @@ def main() -> int:
|
||||
default="FG",
|
||||
help="品种代号,在未传 ts_code 时按 contracts.ROLLOVER_RULES 选当月主力,默认 FG",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--date",
|
||||
help="指定打分日期,格式 YYYYMMDD,不传则对最新日期打分",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
ts_code = args.ts_code or contracts.active_contract(args.symbol)
|
||||
if not args.ts_code:
|
||||
print(f"[AUTO] {args.symbol} 当月主力 -> {ts_code}")
|
||||
return run(ts_code)
|
||||
if args.date:
|
||||
print(f"[DATE] 指定打分日期: {args.date}")
|
||||
return run(ts_code, args.date)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from .models import ScoreDetail, ScoreResult
|
||||
@@ -116,11 +118,21 @@ def _interpret(composite: float) -> str:
|
||||
return "强烈看空区域 — 资金主动且持续地打压价格"
|
||||
|
||||
|
||||
def score_daily(df: pd.DataFrame) -> ScoreResult:
|
||||
"""对 DataFrame 中最新一条记录打分。"""
|
||||
def score_daily(df: pd.DataFrame, trade_date: Optional[str] = None) -> ScoreResult:
|
||||
"""对 DataFrame 中指定日期或最新一条记录打分。"""
|
||||
if len(df) < 31:
|
||||
raise ValueError(f"数据量不足(仅 {len(df)} 行),需要至少 31 行")
|
||||
|
||||
if trade_date:
|
||||
trade_date_str = str(trade_date)
|
||||
mask = df["trade_date"].astype(str) == trade_date_str
|
||||
if not mask.any():
|
||||
raise ValueError(f"指定日期 {trade_date_str} 不在数据中")
|
||||
pos = mask.idxmax()
|
||||
df = df.iloc[:pos + 1].copy()
|
||||
if len(df) < 31:
|
||||
raise ValueError(f"指定日期 {trade_date_str} 之前数据不足(仅 {len(df)} 行),需要至少 31 行")
|
||||
|
||||
latest = df.iloc[-1]
|
||||
|
||||
short, short_details = calc_short_term(df, 7)
|
||||
|
||||
Reference in New Issue
Block a user