update
This commit is contained in:
parent
d88bf0375a
commit
8083c9ffab
3
.gitignore
vendored
3
.gitignore
vendored
@ -26,6 +26,9 @@ app/data/*.json
|
||||
# Streamlit temporary files
|
||||
.streamlit/
|
||||
|
||||
# Local references
|
||||
Refer-TradingAgents-CN
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
@ -259,8 +259,6 @@ class BacktestEngine:
|
||||
decision_callback(ts_code, trade_date, context, decision)
|
||||
except Exception: # noqa: BLE001
|
||||
LOGGER.exception("决策回调执行失败", extra=LOG_EXTRA)
|
||||
# TODO: translate decisions into fills, holdings, and NAV updates.
|
||||
_ = state
|
||||
return records
|
||||
|
||||
def record_agent_state(self, context: AgentContext, decision: Decision) -> None:
|
||||
@ -700,12 +698,156 @@ def run_backtest(
|
||||
) -> BacktestResult:
|
||||
engine = BacktestEngine(cfg)
|
||||
result = engine.run(decision_callback=decision_callback)
|
||||
with db_session() as conn:
|
||||
_ = conn
|
||||
# Implementation should persist bt_nav, bt_trades, and bt_report rows.
|
||||
_persist_backtest_results(cfg, result)
|
||||
return result
|
||||
|
||||
|
||||
def _persist_backtest_results(cfg: BtConfig, result: BacktestResult) -> None:
|
||||
"""Persist backtest configuration, NAV path, trades and summary metrics."""
|
||||
|
||||
nav_rows: List[tuple] = []
|
||||
trade_rows: List[tuple] = []
|
||||
summary_payload: Dict[str, object] = {}
|
||||
|
||||
if result.nav_series:
|
||||
first_nav = float(result.nav_series[0].get("nav", 0.0) or 0.0)
|
||||
peak_nav = first_nav
|
||||
prev_nav: Optional[float] = None
|
||||
max_drawdown = 0.0
|
||||
for entry in result.nav_series:
|
||||
trade_date = str(entry.get("trade_date", ""))
|
||||
nav_val = float(entry.get("nav", 0.0) or 0.0)
|
||||
cash = float(entry.get("cash", 0.0) or 0.0)
|
||||
market_value = float(entry.get("market_value", 0.0) or 0.0)
|
||||
realized = float(entry.get("realized_pnl", 0.0) or 0.0)
|
||||
unrealized = float(entry.get("unrealized_pnl", 0.0) or 0.0)
|
||||
|
||||
if nav_val > peak_nav:
|
||||
peak_nav = nav_val
|
||||
drawdown = (peak_nav - nav_val) / peak_nav if peak_nav else 0.0
|
||||
max_drawdown = max(max_drawdown, drawdown)
|
||||
|
||||
if prev_nav is None or prev_nav == 0.0:
|
||||
ret_val = 0.0
|
||||
else:
|
||||
ret_val = (nav_val / prev_nav) - 1.0
|
||||
prev_nav = nav_val
|
||||
|
||||
info_payload = {
|
||||
"cash": cash,
|
||||
"market_value": market_value,
|
||||
"realized_pnl": realized,
|
||||
"unrealized_pnl": unrealized,
|
||||
}
|
||||
nav_rows.append(
|
||||
(
|
||||
cfg.id,
|
||||
trade_date,
|
||||
nav_val,
|
||||
float(ret_val),
|
||||
None,
|
||||
None,
|
||||
float(drawdown),
|
||||
json.dumps(info_payload, ensure_ascii=False),
|
||||
)
|
||||
)
|
||||
|
||||
last_nav = float(result.nav_series[-1].get("nav", 0.0) or 0.0)
|
||||
total_return = (last_nav / first_nav - 1.0) if first_nav else 0.0
|
||||
summary_payload.update(
|
||||
{
|
||||
"start_nav": first_nav,
|
||||
"end_nav": last_nav,
|
||||
"total_return": total_return,
|
||||
"max_drawdown": max_drawdown,
|
||||
"days": len(result.nav_series),
|
||||
}
|
||||
)
|
||||
|
||||
if result.trades:
|
||||
for trade in result.trades:
|
||||
trade_date = str(trade.get("trade_date", ""))
|
||||
ts_code = str(trade.get("ts_code", ""))
|
||||
side = str(trade.get("action", "")).lower()
|
||||
price = float(trade.get("price", 0.0) or 0.0)
|
||||
qty = float(trade.get("quantity", 0.0) or 0.0)
|
||||
reason_payload = {
|
||||
"confidence": trade.get("confidence"),
|
||||
"target_weight": trade.get("target_weight"),
|
||||
"value": trade.get("value"),
|
||||
}
|
||||
trade_rows.append(
|
||||
(
|
||||
cfg.id,
|
||||
ts_code,
|
||||
trade_date,
|
||||
side,
|
||||
price,
|
||||
qty,
|
||||
json.dumps(reason_payload, ensure_ascii=False),
|
||||
)
|
||||
)
|
||||
summary_payload["trade_count"] = len(trade_rows)
|
||||
|
||||
cfg_payload = {
|
||||
"id": cfg.id,
|
||||
"name": cfg.name,
|
||||
"start_date": cfg.start_date.isoformat(),
|
||||
"end_date": cfg.end_date.isoformat(),
|
||||
"universe": cfg.universe,
|
||||
"params": cfg.params,
|
||||
"method": cfg.method,
|
||||
}
|
||||
|
||||
with db_session() as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO bt_config (id, name, start_date, end_date, universe, params)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
cfg.id,
|
||||
cfg.name,
|
||||
cfg.start_date.isoformat(),
|
||||
cfg.end_date.isoformat(),
|
||||
",".join(cfg.universe),
|
||||
json.dumps(cfg.params, ensure_ascii=False),
|
||||
),
|
||||
)
|
||||
|
||||
conn.execute("DELETE FROM bt_nav WHERE cfg_id = ?", (cfg.id,))
|
||||
conn.execute("DELETE FROM bt_trades WHERE cfg_id = ?", (cfg.id,))
|
||||
conn.execute("DELETE FROM bt_report WHERE cfg_id = ?", (cfg.id,))
|
||||
|
||||
if nav_rows:
|
||||
conn.executemany(
|
||||
"""
|
||||
INSERT INTO bt_nav (cfg_id, trade_date, nav, ret, pos_count, turnover, dd, info)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
nav_rows,
|
||||
)
|
||||
|
||||
if trade_rows:
|
||||
conn.executemany(
|
||||
"""
|
||||
INSERT INTO bt_trades (cfg_id, ts_code, trade_date, side, price, qty, reason)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
trade_rows,
|
||||
)
|
||||
|
||||
summary_payload.setdefault("universe", cfg.universe)
|
||||
summary_payload.setdefault("method", cfg.method)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO bt_report (cfg_id, summary)
|
||||
VALUES (?, ?)
|
||||
""",
|
||||
(cfg.id, json.dumps(summary_payload, ensure_ascii=False, default=str)),
|
||||
)
|
||||
|
||||
|
||||
def _candidate_status(action: AgentAction, requires_review: bool) -> str:
|
||||
mapping = {
|
||||
AgentAction.SELL: "exit",
|
||||
|
||||
35
docs/TODO.md
Normal file
35
docs/TODO.md
Normal file
@ -0,0 +1,35 @@
|
||||
# 项目待办清单
|
||||
|
||||
> 用于跟踪现阶段尚未完成或需要后续完善的工作,便于规划优先级。
|
||||
|
||||
## 1. UI 与日志增强
|
||||
- 今日计划页增加“一键重评估”入口,以及日志钻取 / 历史对比视图(对齐 README 中的架构目标)。
|
||||
- 回测页面支持多版本实验管理(对比不同提示/温度的收益曲线),与 `tuning_results` 记录联动。
|
||||
- Streamlit 聚焦监控场景,补充实时指标面板、异常日志钻取与“仅监控不干预”模式的一键复评策略。
|
||||
|
||||
## 2. 数据与特征层
|
||||
- 实现 `app/features/factors.py` 中的 `compute_factors()`,补齐因子计算与持久化流程。
|
||||
- 完成 `app/ingest/rss.py` 的 RSS 拉取与写库逻辑,打通新闻与情绪数据源。
|
||||
- 强化 `DataBroker` 的取数校验、缓存与回退策略,确保行情/特征补数统一自动化,减少人工兜底。
|
||||
- 围绕动量、估值、流动性等核心信号扩展轻量高质量因子集,全部由程序生成,满足端到端自动决策需求。
|
||||
|
||||
## 3. 决策优化与强化学习
|
||||
- 扩展 `DecisionEnv` 的动作空间(提示版本、部门温度、function 调用策略等),不仅限于代理权重调节。
|
||||
- 引入 Bandit / 贝叶斯优化或 RL 算法探索动作空间,并将 `portfolio_snapshots`、`portfolio_trades` 指标纳入奖励约束。
|
||||
- 构建实时持仓/成交数据写入链路,使线上监控与离线调参共用同一数据源。
|
||||
- 借鉴 TradingAgents-CN 的做法:拆分环境与策略、提供训练脚本/配置,并输出丰富的评估指标(如 Sharpe、Sortino、基准对比)。
|
||||
- 完善 `BacktestEngine` 的成交撮合、风险阈值与指标输出,让回测信号直接对接执行端,形成无人值守的自动闭环。
|
||||
|
||||
## 4. 测试与验证
|
||||
- 补充部门上下文构造、多模型调用、回测指标生成等核心路径的单元 / 集成测试。
|
||||
- 建立决策流程的回归测试用例,确保提示模板或配置调整后行为可复现。
|
||||
- 编写示例 Notebook / end-to-end 教程,参照 TradingAgents-CN 的教学方式,覆盖“数据→回测→调参→评估”全流程。
|
||||
- 针对数据摄取、策略主干与回测指标建立自动化验证管线,作为无人干预运行的质量护栏。
|
||||
|
||||
## 5. 文档同步
|
||||
- 随功能推进,更新 README 与讨论文档,确保描述与实际实现保持一致。
|
||||
|
||||
## 6. LLM 协同与配置
|
||||
- 精简 Provider 列表、强化 function-calling 架构,完善降级和重试策略,并用配置化的角色提示与数据 Scope 提高模型行为可控性。
|
||||
|
||||
(最后更新:2025-09-29)
|
||||
@ -33,7 +33,7 @@
|
||||
## 已完成的日志改进
|
||||
- `agent_utils` 表新增 `_telemetry` 与 `_department_telemetry` JSON 字段(存于 `utils` 列内部),记录每个部门的 provider、模型、温度、回合数、工具调用列表与 token 统计,可在 Streamlit “部门意见”详情页展开查看。
|
||||
- `app/data/logs/agent_*.log` 会追加 `telemetry` 行,保存每轮函数调用的摘要,方便离线分析提示版本与 LLM 配置对决策的影响。
|
||||
- Streamlit 侧边栏监听 `llm.metrics` 的实时事件,并以 ~0.75 秒节流频率刷新“系统监控”,既保证日志到达后快速更新,也避免刷屏造成 UI 闪烁。
|
||||
- Streamlit 侧边栏监听 `llm.metrics` 的实时事件,并使用原位组件刷新“系统监控”,避免增量追加或节流导致的失效,同时确保实时数据推送稳定。
|
||||
- 新增投资管理数据层:SQLite 中创建 `investment_pool`、`portfolio_positions`、`portfolio_trades`、`portfolio_snapshots` 四张表;`app/utils/portfolio.py` 提供访问接口,今日计划页可实时展示候选池、持仓与成交。
|
||||
- 回测引擎 `record_agent_state()` 现同步写入 `investment_pool`,将每日全局决策的置信度、部门标签与目标权重落库,作为后续提示参数调优与候选池管理的基础数据。
|
||||
- `app/backtest/decision_env.py` 引入 `DecisionEnv`,用单步 RL/Gym 风格接口封装回测:动作 → 权重映射 → 回测 → 奖励(收益 - 0.5×回撤),同时输出 NAV、交易与行动权重,方便与 Bandit/PPO 等算法对接。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user