Files
sanguo_vnpy_v2/tests/trader/test_account.py
T
claude_dev e5e4eef807 feat(trader): Account总账+StrategyRunner分户(双层记账/资金T0/股票T1)
- Account: cash资金T0/合并持仓/equity盯市/cash_enough买单检查
- StrategyRunner: 分户持仓+realized_pnl归因/unrealized_pnl
- transfer_fee 直接用(matcher已双向,不再×2,review H3)
- unfreeze_all 对称(总账+分户,T+1每日解冻)
7 tests passed.
2026-07-07 11:50:40 +08:00

88 lines
3.4 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.
"""Account + StrategyRunner 双层记账测试(spec §7)。"""
import pytest
from sanguo_trader.account import Account
from sanguo_trader.strategy_runner import StrategyRunner
from sanguo_trader.models import MatchSession, OrderSide, PaperTrade
def mktrade(side, price=10.0, volume=100, commission=5.0, stamp=0.0,
transfer_fee=0.02, strategy_id="s1", symbol="600000"):
return PaperTrade(
strategy_id, symbol, side, price, volume, commission,
stamp, transfer_fee, "", MatchSession.NEXT_OPEN,
)
def test_capital_t0_sell_then_buy_immediately():
"""资金 T+0:卖出现金立即可用于再买。"""
acc = Account(initial_capital=1_000_000)
acc.apply_trade(mktrade(OrderSide.BUY, price=10.0, volume=100))
acc.unfreeze_all()
cash_after_buy = acc.cash
acc.apply_trade(mktrade(OrderSide.SELL, price=11.0, volume=100, stamp=0.55))
assert acc.cash > cash_after_buy # 卖出后现金立即增加
assert acc.cash_enough(mktrade(OrderSide.BUY, price=11.0, volume=50)) # 可立即再买
def test_share_t1_buy_frozen_until_unfreeze():
"""股票 T+1:买入当日 frozenunfreeze 后才可卖。"""
acc = Account(initial_capital=1_000_000)
acc.apply_trade(mktrade(OrderSide.BUY, volume=100))
pos = acc.positions["600000"]
assert pos.frozen == 100
assert pos.available == 0
acc.unfreeze_all()
assert pos.available == 100
def test_cash_enough_insufficient_rejected():
acc = Account(initial_capital=1000)
# cost = 10*100 + 5 + 0.02 = 1005.02 > 1000
assert acc.cash_enough(mktrade(OrderSide.BUY, price=10.0, volume=100)) is False
def test_transfer_fee_not_doubled_on_buy():
"""review H3matcher 出双向 transfer_feeAccount 直接用不再 ×2。"""
acc = Account(initial_capital=1_000_000)
acc.apply_trade(mktrade(OrderSide.BUY, price=10.0, volume=100, transfer_fee=0.02))
assert acc.cash == pytest.approx(1_000_000 - 1005.02)
def test_double_entry_account_equals_sum_of_runners():
"""双层记账一致性:总账持仓 = 各分户持仓之和。"""
acc = Account(initial_capital=1_000_000)
r1 = StrategyRunner("s1")
r2 = StrategyRunner("s2")
t1 = mktrade(OrderSide.BUY, volume=100, strategy_id="s1")
t2 = mktrade(OrderSide.BUY, volume=200, strategy_id="s2")
for t in (t1, t2):
acc.apply_trade(t)
r1.apply_trade(t1)
r2.apply_trade(t2)
assert acc.positions["600000"].volume == 300
assert r1.positions["600000"].volume + r2.positions["600000"].volume == 300
def test_runner_realized_pnl_on_sell():
acc = Account(initial_capital=1_000_000)
runner = StrategyRunner("s1")
buy_t = mktrade(OrderSide.BUY, price=10.0, volume=100, strategy_id="s1")
acc.apply_trade(buy_t)
runner.apply_trade(buy_t)
acc.unfreeze_all()
runner.unfreeze_all()
sell_t = mktrade(OrderSide.SELL, price=12.0, volume=100, stamp=0.6, strategy_id="s1")
acc.apply_trade(sell_t)
runner.apply_trade(sell_t)
# (12-10)*100 - 5(佣) - 0.6(印) - 0.02(过) = 194.38
assert runner.realized_pnl == pytest.approx(194.38)
def test_equity_mark_to_market():
acc = Account(initial_capital=1_000_000)
acc.apply_trade(mktrade(OrderSide.BUY, price=10.0, volume=100))
acc.unfreeze_all()
acc.mark_to_market({"600000": 11.0})
assert acc.market_value == pytest.approx(1100.0)
assert acc.equity == pytest.approx(acc.cash + 1100.0)