# -*- coding: utf-8 -*- """影子撮合定价(2026-08-28 价差对齐,用户拍板「费率一致后只留撮合方法差」)。 08-27 双轨 19/60 实锤:影子按「昨收 fq=pre」记账 vs 实盘盘中真实成交,每笔 天然偏差(600988:47.12 vs 46.00,低开日 -237bps;除权日另跳一个分红因子)。 对齐后契约:实时 tick 优先(与实盘同源同刻的裸价),失败回退**不复权**昨收。 """ from __future__ import annotations from types import SimpleNamespace from sanguo_trader.shadow.runner import build_price_getter class _StubProvider: """provider 替身:实时价/日线收盘可控,记录 get_price 的 fq 实参。""" def __init__(self, realtime=None, close=10.0): self._realtime = realtime self._close = close self.fq_seen = [] def get_realtime_price(self, security): if isinstance(self._realtime, Exception): raise self._realtime return self._realtime def get_price(self, security=None, start_date=None, end_date=None, frequency=None, fields=None, fq=None, **_): self.fq_seen.append(fq) import pandas as pd return pd.DataFrame({"close": [self._close]}) class TestBuildPriceGetter: def test_realtime_tick_preferred(self): """有实时价 → 直接用(与实盘同源同刻),不碰日线。""" p = _StubProvider(realtime=12.34, close=10.0) assert build_price_getter(p)("600519.XSHG") == 12.34 assert p.fq_seen == [] def test_fallback_to_raw_close_when_realtime_none(self): """实时不可得 → 回退最新收盘,且 fq=raw(不复权,实盘成交价为裸价)。""" p = _StubProvider(realtime=None, close=46.85) assert build_price_getter(p)("600988.XSHG") == 46.85 assert p.fq_seen == ["raw"] def test_fallback_when_realtime_raises(self): """实时抛异常 → 静默回退收盘(撮合不因实时源故障停摆)。""" p = _StubProvider(realtime=RuntimeError("断连"), close=9.9) assert build_price_getter(p)("000001.XSHE") == 9.9 def test_provider_without_realtime_method_uses_raw_close(self): """旧/回测 provider 无实时方法 → 原路径可用,fq 仍为 raw。""" p = SimpleNamespace(get_price=_StubProvider(close=22.35).get_price) getter = build_price_getter(p) assert getter("001234.XSHE") == 22.35