From 17a48014507864ded1b8b171169e0e62f24e06a9 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Tue, 7 Jul 2026 15:03:57 +0800 Subject: [PATCH] =?UTF-8?q?fix(trader):=20vnpy=20=E6=A1=A5=E6=8E=A5(am/tra?= =?UTF-8?q?ding/cancel=5Fall/=5F=5Fgetattr=5F=5F=E5=85=9C=E5=BA=95)=20+=20?= =?UTF-8?q?create=20=E5=BC=82=E6=AD=A5=E5=9B=9E=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sanguo_api/routes_paper.py | 28 +++++++++++++++++++--------- sanguo_trader/cta_adapter.py | 9 +++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/sanguo_api/routes_paper.py b/sanguo_api/routes_paper.py index fee67c9..13aede8 100644 --- a/sanguo_api/routes_paper.py +++ b/sanguo_api/routes_paper.py @@ -55,20 +55,23 @@ class PaperCreateRequest(BaseModel): @router.post("/paper/create", dependencies=[Depends(verify_token)]) def create_paper(req: PaperCreateRequest): - from sanguo_trader.persistence import init_db, save_account, update_account_status + from sanguo_trader.persistence import init_db, save_account + import threading db = _db_path["path"] or ":memory:" init_db(db) aid = save_account(db, req.model_dump()) status = "created" - if req.mode == "replay": # 回放模式:建 account 后同步跑 engine.run - try: - _run_replay(db, aid, req) - status = "done" - update_account_status(db, aid, "done") - except Exception as e: # noqa: BLE001 — 回放失败不阻塞 create,状态记 failed - update_account_status(db, aid, "failed", str(e)) - status = "failed" + if req.mode == "replay": # 回放后台线程跑,create 立即返回(避免阻塞 worker 502) + def _bg(): + from sanguo_trader.persistence import update_account_status + try: + _run_replay(db, aid, req) + update_account_status(db, aid, "done") + except Exception as e: # noqa: BLE001 + update_account_status(db, aid, "failed", str(e)) + threading.Thread(target=_bg, daemon=True).start() + status = "running" return {"account_id": aid, "status": status} @@ -146,6 +149,13 @@ def _run_replay(db, aid, req: PaperCreateRequest): listing_days=s.listing_days) vt_symbol = f"{s.symbol}.{guess_exchange(s.symbol).value}" strat = cls(cta, s.name, vt_symbol, s.params) # CtaTemplate(cta_engine, name, vt_symbol, setting) + strat.trading = True # 允许 send_order(等价 on_start) + try: + from vnpy.trader.utility import ArrayManager + if not hasattr(strat, "am"): + strat.am = ArrayManager(100) + except Exception: + pass cta.set_strategy(strat) runners.append(StrategyRunner(s.name, strategy=strat, paper_cta_engine=cta, symbol=s.symbol)) diff --git a/sanguo_trader/cta_adapter.py b/sanguo_trader/cta_adapter.py index 2e6a81d..3adc1f9 100644 --- a/sanguo_trader/cta_adapter.py +++ b/sanguo_trader/cta_adapter.py @@ -67,6 +67,15 @@ class PaperCtaEngine: else: self._cancel_one(vt_orderid) + def cancel_all(self, vt_symbol: str | None = None) -> None: + """撤销所有挂单(纸面:清 pending;CtaTemplate.on_bar 开头常调)。""" + self.pending_orders = [] + + def __getattr__(self, name: str): + """兜底未实现的 cta_engine 方法(load_bar/put_event/write_log/send_email + /get_data/load_tick 等)返回 no-op,避免 CtaTemplate 调用时 AttributeError。""" + return lambda *a, **kw: None + def _cancel_one(self, vt_orderid: str) -> None: try: idx = int(vt_orderid.rsplit(".", 1)[-1]) - 1