58 lines
2.4 KiB
Python
58 lines
2.4 KiB
Python
# -*- 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
|