fix(trader): matcher slippage/过户费双向/限价超涨停拒单 (H2/H3/H4)
H2: fill_price 应用 slippage(买+/卖-,默认0不影响) H3: 过户费改双向(matcher 直接×2,Account 不再×2,删单边注释) H4: 限价单超涨停价拒单(price_above_limit/price_below_limit) 69 tests passed.
This commit is contained in:
@@ -204,11 +204,11 @@ def test_stamp_duty_only_on_sell():
|
||||
assert t_sell.stamp_duty == pytest.approx(0.5)
|
||||
|
||||
|
||||
def test_transfer_fee_single_sided_in_trade():
|
||||
def test_transfer_fee_double_sided_in_trade():
|
||||
t = cross_order(buy(market=True), mkbar(10, 10, 10, 10), PREV, CFG)
|
||||
assert isinstance(t, PaperTrade)
|
||||
# 单边 100*10*0.00001 = 0.01;trade 里存单边,Account 算 ×2
|
||||
assert t.transfer_fee == pytest.approx(0.01)
|
||||
# 双向 100*10*0.00001*2 = 0.02;matcher 直接出双向总额
|
||||
assert t.transfer_fee == pytest.approx(0.02)
|
||||
|
||||
|
||||
def test_trade_carries_match_session():
|
||||
@@ -232,3 +232,76 @@ def test_trade_carries_strategy_id_and_symbol():
|
||||
assert isinstance(t, PaperTrade)
|
||||
assert t.strategy_id == "my_strat"
|
||||
assert t.symbol == "300750"
|
||||
|
||||
|
||||
# ---- H4: 限价超涨停 / 跌停未拒 ----
|
||||
def test_limit_buy_above_limit_up_rejected():
|
||||
"""限价买单价格超涨停 → reject price_above_limit(触价检查前先查涨跌停)。"""
|
||||
# 主板 600000 prev 10.0 → 涨停 11.0;限价 11.50 超涨停
|
||||
o = PaperOrder("s1", "600000", OrderSide.BUY, 11.50, 100, is_market=False)
|
||||
r = cross_order(o, mkbar(10.5, 11, 10.2, 10.8), PREV, CFG)
|
||||
assert isinstance(r, PaperReject) and r.reason == "price_above_limit"
|
||||
|
||||
|
||||
def test_limit_sell_below_limit_down_rejected():
|
||||
"""限价卖单价格低于跌停 → reject price_below_limit。"""
|
||||
# 主板 600000 prev 10.0 → 跌停 9.0;限价 8.50 跌破
|
||||
o = PaperOrder("s1", "600000", OrderSide.SELL, 8.50, 100, is_market=False)
|
||||
r = cross_order(o, mkbar(10.5, 11, 10.2, 10.8), PREV, CFG)
|
||||
assert isinstance(r, PaperReject) and r.reason == "price_below_limit"
|
||||
|
||||
|
||||
def test_limit_buy_at_limit_up_not_rejected():
|
||||
"""限价 == 涨停价 不拒(严格 >,等号允许)。"""
|
||||
o = PaperOrder("s1", "600000", OrderSide.BUY, 11.0, 100, is_market=False)
|
||||
t = cross_order(o, mkbar(10.8, 11.0, 10.5, 10.9), PREV, CFG)
|
||||
assert isinstance(t, PaperTrade) and t.price == 10.8
|
||||
|
||||
|
||||
def test_limit_buy_above_gem_limit_up_rejected():
|
||||
"""创业板 20% 涨停边界:300750 prev 10.0 → 涨停 12.0;限价 12.50 拒单。"""
|
||||
o = PaperOrder("s1", "300750", OrderSide.BUY, 12.50, 100, is_market=False)
|
||||
r = cross_order(o, mkbar(10.5, 11, 10.2, 10.8), 10.0, CFG)
|
||||
assert isinstance(r, PaperReject) and r.reason == "price_above_limit"
|
||||
|
||||
|
||||
# ---- H2: slippage 应用到 fill_price ----
|
||||
def test_slippage_added_to_buy_fill():
|
||||
"""买单 fill_price += slippage。"""
|
||||
cfg = AccountConfig(initial_capital=1_000_000, slippage=0.05)
|
||||
t = cross_order(buy(market=True), mkbar(10.0, 10, 10, 10), PREV, cfg)
|
||||
assert isinstance(t, PaperTrade)
|
||||
assert t.price == pytest.approx(10.05)
|
||||
|
||||
|
||||
def test_slippage_subtracted_from_sell_fill():
|
||||
"""卖单 fill_price -= slippage。"""
|
||||
cfg = AccountConfig(initial_capital=1_000_000, slippage=0.05)
|
||||
t = cross_order(sell(market=True), mkbar(10.0, 10, 10, 10), PREV, cfg)
|
||||
assert isinstance(t, PaperTrade)
|
||||
assert t.price == pytest.approx(9.95)
|
||||
|
||||
|
||||
def test_zero_slippage_preserves_fill_price():
|
||||
"""默认 slippage=0 → fill_price 不变(向后兼容)。"""
|
||||
t = cross_order(buy(market=True), mkbar(10.0, 10, 10, 10), PREV, CFG)
|
||||
assert isinstance(t, PaperTrade)
|
||||
assert t.price == 10.0
|
||||
|
||||
|
||||
def test_slippage_affects_gross_for_fees():
|
||||
"""slippage 后的 fill_price 用于费用计算(更真实)。"""
|
||||
cfg = AccountConfig(initial_capital=1_000_000, slippage=0.05)
|
||||
t = cross_order(sell(market=True), mkbar(10.0, 10, 10, 10), PREV, cfg)
|
||||
assert isinstance(t, PaperTrade)
|
||||
# 卖单:fill=9.95,gross=100*9.95=995.0
|
||||
# stamp_duty = 995.0 * 0.0005 = 0.4975
|
||||
assert t.stamp_duty == pytest.approx(995.0 * 0.0005)
|
||||
|
||||
|
||||
# ---- H3: 过户费双向(已有 test_transfer_fee_double_sided_in_trade 重命名断言更新)----
|
||||
def test_transfer_fee_double_sided_on_sell_too():
|
||||
"""卖单过户费同样双向(沪深一致)。"""
|
||||
t = cross_order(sell(market=True), mkbar(10, 10, 10, 10), PREV, CFG)
|
||||
assert isinstance(t, PaperTrade)
|
||||
assert t.transfer_fee == pytest.approx(0.02)
|
||||
|
||||
Reference in New Issue
Block a user