Files
sanguo_vnpy_v2/tests/trader/test_engine.py
T
claude_dev c6b19f4244 feat(data): 恢复双源(task#79)—撮合raw+策略qfq, 分红除权准确
用户要模拟=回测准确: raw除权缺口致MA假信号, 必须双源。
- data_source: qfq→qfq_dir(干净qfq), raw→raw_dir; _check_adjust_cfg(cfg提供才校验)
- engine 双bar流: step(raw_bars,qfq_bars)撮合/盯市raw+策略on_bar qfq; run zip(raw,qfq)
- live_orchestrator: warmup用qfq(信号am); 去adjust参数(双源固定)
- raw_redownload --adjust(''raw/'qfq'); config qfq_dir
- 113/113通过
2026-07-08 07:21:33 +08:00

114 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""PaperEngine 主循环测试(mock data_source + mock 策略,spec §4)。"""
from types import SimpleNamespace
from sanguo_trader.account import Account
from sanguo_trader.cta_adapter import PaperCtaEngine
from sanguo_trader.engine import PaperEngine
from sanguo_trader.models import AccountConfig, MatchSession
from sanguo_trader.persistence import (
init_db, list_daily_balance, list_trades, save_account,
)
from sanguo_trader.strategy_runner import StrategyRunner
def _bar(date, o, h, l, c):
return SimpleNamespace(open_price=o, high_price=h, low_price=l,
close_price=c, datetime=date)
class _FakeDataSource:
def __init__(self, sections):
self.sections = sections
def iter_bars(self, symbols, start, end, interval, adjust="qfq", cfg=None):
del symbols, start, end, interval, adjust, cfg
for date, bars in self.sections:
yield date, bars
class _AlwaysBuyStrategy:
def __init__(self, engine, vt_symbol):
self.cta_engine = engine
self.vt_symbol = vt_symbol
def on_bar(self, bar):
self.cta_engine.send_order(self, "LONG", "OPEN", bar.close_price, 100)
def _build(tmp_path, sections, match_session=MatchSession.NEXT_OPEN):
db = str(tmp_path / "e.db")
init_db(db)
aid = save_account(db, {"name": "t", "initial_capital": 1_000_000})
cfg = AccountConfig(initial_capital=1_000_000)
account = Account(1_000_000)
cta = PaperCtaEngine("s1", match_session=match_session)
strat = _AlwaysBuyStrategy(cta, "600000.SSE")
cta.set_strategy(strat)
runner = StrategyRunner("s1", strategy=strat, paper_cta_engine=cta, symbol="600000")
pe = PaperEngine(account, [runner], _FakeDataSource(sections), cfg, db, aid,
symbols=["600000"], start="2024-01-01", end="2024-12-31")
return pe, db, aid, account, runner
def test_engine_next_open_fills_at_next_bar_open(tmp_path):
sections = [
("2024-01-01", {"600000": _bar("2024-01-01", 10.0, 10.5, 9.5, 10.0)}),
("2024-01-02", {"600000": _bar("2024-01-02", 10.5, 11.0, 10.0, 10.8)}),
("2024-01-03", {"600000": _bar("2024-01-03", 10.8, 11.5, 10.5, 11.0)}),
]
pe, db, aid, *_ = _build(tmp_path, sections)
pe.run()
fills = [t for t in list_trades(db, aid) if not t["rejected"]]
# day1 信号→day2 撮合@10.5(open)day2 信号→day3 撮合@10.8(open)day3 信号无day4
assert len(fills) == 2
assert fills[0]["price"] == 10.5
assert fills[1]["price"] == 10.8
def test_engine_current_close_fills_same_bar(tmp_path):
sections = [
("2024-01-01", {"600000": _bar("2024-01-01", 10.0, 10.5, 9.5, 10.0)}),
("2024-01-02", {"600000": _bar("2024-01-02", 10.5, 11.0, 10.0, 10.8)}),
]
pe, db, aid, *_ = _build(tmp_path, sections, match_session=MatchSession.CURRENT_CLOSE)
pe.run()
fills = [t for t in list_trades(db, aid) if not t["rejected"]]
assert len(fills) == 2
assert fills[0]["price"] == 10.0 # 当根 close
assert fills[1]["price"] == 10.8
def test_engine_daily_balance_and_consistency(tmp_path):
sections = [
("2024-01-01", {"600000": _bar("2024-01-01", 10.0, 10.5, 9.5, 10.0)}),
("2024-01-02", {"600000": _bar("2024-01-02", 10.5, 11.0, 10.0, 10.8)}),
("2024-01-03", {"600000": _bar("2024-01-03", 10.8, 11.5, 10.5, 11.0)}),
]
pe, db, aid, account, runner = _build(tmp_path, sections)
pe.run()
balances = list_daily_balance(db, aid)
assert len(balances) == 3
# 总账持仓 = 分户持仓(day2+day3 各买100 = 200
assert account.positions["600000"].volume == 200
assert runner.positions["600000"].volume == 200
def test_engine_step_single_bar_advances(tmp_path):
"""engine.step 单根推进(C-S3 实走每日入口):day1 信号缓冲,day2 撮合。"""
sections = [
("2024-01-01", {"600000": _bar("2024-01-01", 10.0, 10.5, 9.5, 10.0)}),
("2024-01-02", {"600000": _bar("2024-01-02", 10.5, 11.0, 10.0, 10.8)}),
]
pe, db, aid, account, runner = _build(tmp_path, sections)
# day1 step_AlwaysBuyStrategy 买单(NEXT_OPEN)→ 进 pending,当根不撮合
pending, closes = pe.step("2024-01-01", sections[0][1], sections[0][1], {}, [])
assert len(pending) == 1
assert account.positions.get("600000") is None
assert closes["600000"] == 10.0
# day2 step:撮合 day1 pending @ open 10.5;策略 on_bar(day2) 又发单进 pending 等 day3
pending2, closes2 = pe.step("2024-01-02", sections[1][1], sections[1][1], closes, pending)
assert len(pending2) == 1 # day2 新信号(无 day3 不撮合)
assert account.positions["600000"].volume == 100 # day1 单 day2 open 10.5 撮合 100 股
# step 入库(day1+day2 各一条余额)
assert len(list_daily_balance(db, aid)) == 2