Document and abstract portfolio trading costs
This commit is contained in:
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user