Files
sanguo_vnpy_v2/tests/trader/test_engine.py
T
claude_dev 3aca14f723 refactor(engine): 抽出step()单根推进(C-S3实走入口,task2基础)
run()循环体抽为step(bar_date,bars,prev_close,pending)→(pending,closes);
run()改为调step。实走scheduler每日喂当日bar调step单步推进。
- 回放行为不变(test_engine原3用例pass)
- 加test_engine_step_single_bar_advances: day1信号缓冲/day2撮合
- trader全量111/111通过
2026-07-07 23:41:18 +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], {}, [])
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], 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