Document and abstract portfolio trading costs

This commit is contained in:
Yuxuan Yan
2026-06-10 15:41:38 +08:00
parent 4a477b8f75
commit 534b91aaa4
6 changed files with 272 additions and 8 deletions
+65
View File
@@ -22,6 +22,7 @@ from pipeline.portfolio.constraints import (
SuspensionConstraint,
VolumeCapConstraint,
)
from pipeline.portfolio.costs import SimpleProportionalCostModel
from pipeline.portfolio.simulator import (
MarketSlice,
ReferenceSimulator,
@@ -445,6 +446,7 @@ def test_simulator_blocked_buy_when_suspended():
assert res.traded_shares[0] == 0
assert res.realized_shares[0] == 0
assert res.blocked[0] == 1
assert res.cost[0] == 0.0
def test_simulator_cost_is_positive_when_trading():
@@ -458,6 +460,69 @@ def test_simulator_cost_is_positive_when_trading():
assert np.isclose(res.cost[0], 1000 * 20 * 15 / 1e4)
def test_simulator_cost_only_on_nonzero_realized_trades():
n = 2
sim = ReferenceSimulator(constraints=[], cost_bps=10)
sl = _slice(n, price=np.array([10.0, 20.0]))
ctx = TradeContext(np.array([100, 100], np.int64),
np.array([100, 150], np.int64), sl, 1e6)
res = sim.fill(ctx)
assert res.traded_shares.tolist() == [0, 50]
assert res.cost[0] == 0.0
assert np.isclose(res.cost[1], 50 * 20 * 10 / 1e4)
def test_simple_cost_model_adds_cost_and_slippage_without_price_adjustment():
model = SimpleProportionalCostModel(cost_bps=10, slippage_bps=5)
cost = model.compute(
traded_shares=np.array([1000, -1000]),
execution_price=np.array([20.0, 20.0]),
side=np.array([1, -1]),
date=dt.date(2024, 1, 2),
)
assert np.allclose(cost, np.array([30.0, 30.0]))
def test_daily_pnl_cost_matches_fill_trade_cost_sum():
dates = pd.to_datetime(["2024-01-01", "2024-01-02"])
positions = pd.DataFrame({
"symbol_id": ["sh600000", "sz000001"],
"date": [dates[0], dates[0]],
"portfolio_name": ["run1", "run1"],
"target_weight": [0.5, -0.5],
"target_value": [1000.0, -1000.0],
"target_shares": [100.0, -50.0],
"position_shares": [100, -50],
"position_value": [1000.0, -1000.0],
"price": [10.0, 20.0],
})
data = pd.DataFrame([
{
"symbol_id": sym,
"date": d,
"open": price,
"close": price,
"preclose": price,
"amount": 1e9,
"tradestatus": 1,
"isST": 0,
}
for d in dates
for sym, price in (("sh600000", 10.0), ("sz000001", 20.0))
])
fills, pnl = ReferenceSimulator(cost_bps=10, slippage_bps=5).run(positions, data)
total_fill_cost = fills["trade_cost"].sum()
assert np.isclose(total_fill_cost, 3.0)
assert np.isclose(pnl["cost"].iloc[0], total_fill_cost)
assert np.isclose(pnl["pnl"].iloc[0], -total_fill_cost)
# --- evaluate_portfolio ------------------------------------------------------
def test_evaluate_portfolio_keys_no_ic():