test: reproduce execution cash and long-only violations
This commit is contained in:
@@ -405,6 +405,49 @@ def test_simulate_multi_day_with_audit_records_costs_without_replay():
|
||||
assert result.daily_executions[1].executions == ()
|
||||
|
||||
|
||||
def test_simulate_multi_day_with_audit_never_spends_more_cash_than_available():
|
||||
"""满仓目标应按可用现金部分成交,不能用负现金隐式加杠杆。"""
|
||||
result = simulate_multi_day_with_audit(
|
||||
[("d1", {"A": 1.0})],
|
||||
[("d1", {"A": 10.0})],
|
||||
1_000.0,
|
||||
)
|
||||
|
||||
execution = result.daily_executions[0].executions[0]
|
||||
assert result.positions[0].cash >= -1e-9
|
||||
assert 0 < execution.partial_fill_pct < 1
|
||||
assert execution.blocked_reason == "insufficient_cash_partial_fill"
|
||||
assert result.final_portfolio_value == pytest.approx(1_000.0 - result.total_costs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"targets",
|
||||
[
|
||||
{"A": -0.1},
|
||||
{"A": 0.6, "B": 0.5},
|
||||
{"A": float("nan")},
|
||||
],
|
||||
)
|
||||
def test_simulate_multi_day_with_audit_rejects_invalid_long_only_weights(targets):
|
||||
"""多日 A 股目标必须是有限、非负且合计不超过 100% 的权重。"""
|
||||
with pytest.raises(ValueError, match="target weights"):
|
||||
simulate_multi_day_with_audit(
|
||||
[("d1", targets)],
|
||||
[("d1", {"A": 10.0, "B": 10.0})],
|
||||
1_000.0,
|
||||
)
|
||||
|
||||
|
||||
def test_simulate_multi_day_with_audit_requires_price_for_existing_holding():
|
||||
"""已有持仓缺价时无法可信估值,必须失败而不是把市值记为零。"""
|
||||
with pytest.raises(ValueError, match="missing price for held asset A"):
|
||||
simulate_multi_day_with_audit(
|
||||
[("d1", {"A": 1.0}), ("d2", {"A": 1.0})],
|
||||
[("d1", {"A": 10.0}), ("d2", {})],
|
||||
1_000.0,
|
||||
)
|
||||
|
||||
|
||||
def test_simulate_multi_day_with_audit_records_unpriced_target_rejection():
|
||||
"""缺失价格的目标不能吞掉现金,且必须留下拒绝原因。"""
|
||||
result = simulate_multi_day_with_audit(
|
||||
|
||||
Reference in New Issue
Block a user