Skip to content

Commit 72a3c65

Browse files
committed
feat: add slippage field to Order model and DB
- Add slippage column to domain Order model (init, to_dict, from_dict) - Add slippage column to SQLOrder infrastructure model (SqliteDecimal) - Compute and store slippage in backtest_trade_order_evaluator (BUY/SELL) - Compute and store slippage in vector_backtest_service (all 4 Order sites) - Serialize slippage to backtest report JSON - Add Slippage column to orders table in dashboard JS
1 parent 78b4f9c commit 72a3c65

8 files changed

Lines changed: 22 additions & 2 deletions

File tree

investing_algorithm_framework/app/reporting/backtest_report.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@ def _build_run_data(self):
585585
o_fee_rate = getattr(
586586
o, 'order_fee_rate', 0
587587
) or 0
588+
o_slippage = getattr(o, 'slippage', None)
589+
if o_slippage is None:
590+
o_slippage = 0
588591
orders_list.append({
589592
'sym': getattr(o, 'target_symbol', '')
590593
or '',
@@ -611,6 +614,9 @@ def _build_run_data(self):
611614
'fee_rate': round(
612615
float(o_fee_rate), 4
613616
),
617+
'slippage': round(
618+
float(o_slippage), 4
619+
),
614620
'created': _fmt_date(o_dt)
615621
if o_dt else '',
616622
'updated': _fmt_date(u_dt)

investing_algorithm_framework/app/reporting/templates/dashboard.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6074,6 +6074,7 @@ var ordersHeaders = [
60746074
{key: 'cost', label: 'Cost', align: 'right'},
60756075
{key: 'fee', label: 'Fee', align: 'right'},
60766076
{key: 'fee_rate', label: 'Fee Rate', align: 'right'},
6077+
{key: 'slippage', label: 'Slippage', align: 'right'},
60776078
{key: 'created', label: 'Created'},
60786079
{key: 'updated', label: 'Updated'},
60796080
];

investing_algorithm_framework/domain/models/order/order.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def __init__(
4040
order_fee=None,
4141
order_fee_currency=None,
4242
order_fee_rate=None,
43+
slippage=None,
4344
id=None,
4445
metadata=None,
4546
):
@@ -84,6 +85,7 @@ def __init__(
8485
self.order_fee = order_fee
8586
self.order_fee_currency = order_fee_currency
8687
self.order_fee_rate = order_fee_rate
88+
self.slippage = slippage
8789
self.id = id
8890
self.cost = cost
8991
self.metadata = metadata if metadata is not None else {}
@@ -260,6 +262,7 @@ def ensure_iso(value):
260262
"order_fee_currency": self.order_fee_currency,
261263
"order_fee_rate": self.order_fee_rate,
262264
"order_fee": self.order_fee,
265+
"slippage": self.slippage,
263266
"metadata": self.metadata if hasattr(self, 'metadata') else {},
264267
}
265268

@@ -301,6 +304,7 @@ def from_dict(data: dict):
301304
order_fee=data.get("order_fee", None),
302305
order_fee_currency=data.get("order_fee_currency", None),
303306
order_fee_rate=data.get("order_fee_rate", None),
307+
slippage=data.get("slippage", None),
304308
metadata=data.get("metadata", {}),
305309
)
306310
return order

investing_algorithm_framework/infrastructure/models/order/order.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class SQLOrder(Order, SQLBaseModel, SQLAlchemyModelExtension):
5050
order_fee = Column(SqliteDecimal(), default=None)
5151
order_fee_currency = Column(String, default=None)
5252
order_fee_rate = Column(SqliteDecimal(), default=None)
53+
slippage = Column(SqliteDecimal(), default=None)
5354
sell_order_metadata_id = Column(Integer, ForeignKey('orders.id'))
5455
trade_allocations = relationship(
5556
'SQLTradeAllocation', back_populates='order'

investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def _close_trade(sym, sym_data, price, date):
269269
order_fee=sell_fee,
270270
order_fee_rate=tc.fee_percentage / 100
271271
if tc.fee_percentage else None,
272+
slippage=price - sell_fill,
272273
)
273274
orders.append(sell_order)
274275
trade_orders = lt.orders
@@ -306,6 +307,7 @@ def _close_trade(sym, sym_data, price, date):
306307
order_fee=ot_sell_fee,
307308
order_fee_rate=tc.fee_percentage / 100
308309
if tc.fee_percentage else None,
310+
slippage=price - sell_fill,
309311
)
310312
orders.append(sell_o)
311313
ot_orders = ot.orders
@@ -363,6 +365,7 @@ def _open_trade(sym, sym_data, price, date, capital):
363365
order_fee=buy_fee,
364366
order_fee_rate=tc.fee_percentage / 100
365367
if tc.fee_percentage else None,
368+
slippage=fill_price - price,
366369
)
367370
orders.append(buy_order)
368371
trade = Trade(
@@ -464,6 +467,7 @@ def _partial_close(sym, sym_data, price, date, sell_pct):
464467
order_fee=sell_fee,
465468
order_fee_rate=tc.fee_percentage / 100
466469
if tc.fee_percentage else None,
470+
slippage=price - sell_fill,
467471
)
468472
orders.append(sell_order)
469473
trade_orders = lt.orders

investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,14 @@ def _check_has_executed(self, order, ohlcv_df):
122122
if (ohlcv_data_after_order['Low'] <= order_price).any():
123123
fill_price = tc.get_buy_fill_price(order_price)
124124
fee = tc.get_fee(fill_price * order.amount)
125+
slippage = fill_price - order_price
125126
update_data = {
126127
'status': OrderStatus.CLOSED.value,
127128
'remaining': 0,
128129
'filled': order.amount,
129130
'price': fill_price,
130131
'order_fee': fee,
132+
'slippage': slippage,
131133
}
132134
if tc.fee_percentage:
133135
update_data['order_fee_rate'] = \
@@ -139,12 +141,14 @@ def _check_has_executed(self, order, ohlcv_df):
139141
if (ohlcv_data_after_order['High'] >= order_price).any():
140142
fill_price = tc.get_sell_fill_price(order_price)
141143
fee = tc.get_fee(fill_price * order.amount)
144+
slippage = order_price - fill_price
142145
update_data = {
143146
'status': OrderStatus.CLOSED.value,
144147
'remaining': 0,
145148
'filled': order.amount,
146149
'price': fill_price,
147150
'order_fee': fee,
151+
'slippage': slippage,
148152
}
149153
if tc.fee_percentage:
150154
update_data['order_fee_rate'] = \
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"backtest_start_date": "2023-12-01 00:00:00", "backtest_date_range_name": null, "backtest_end_date": "2023-12-02 00:00:00", "trading_symbol": "EUR", "initial_unallocated": 1000.0, "number_of_runs": 1441, "portfolio_snapshots": [{"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-01T00:00:00+00:00", "total_value": 1000.0}, {"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-02T00:00:00+00:00", "total_value": 1000.0}], "trades": [], "orders": [], "positions": [{"symbol": "EUR", "amount": 1000.0, "cost": 1000.0, "portfolio_id": 1}], "created_at": "2026-04-15 13:13:32", "symbols": [], "number_of_days": 0, "number_of_trades": 0, "number_of_trades_closed": 0, "number_of_trades_open": 0, "number_of_orders": 0, "number_of_positions": 0, "metadata": {}, "signals": {}, "signal_events": []}
1+
{"backtest_start_date": "2023-12-01 00:00:00", "backtest_date_range_name": null, "backtest_end_date": "2023-12-02 00:00:00", "trading_symbol": "EUR", "initial_unallocated": 1000.0, "number_of_runs": 1441, "portfolio_snapshots": [{"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-01T00:00:00+00:00", "total_value": 1000.0}, {"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-02T00:00:00+00:00", "total_value": 1000.0}], "trades": [], "orders": [], "positions": [{"symbol": "EUR", "amount": 1000.0, "cost": 1000.0, "portfolio_id": 1}], "created_at": "2026-04-15 14:57:33", "symbols": [], "number_of_days": 0, "number_of_trades": 0, "number_of_trades_closed": 0, "number_of_trades_open": 0, "number_of_orders": 0, "number_of_positions": 0, "metadata": {}, "signals": {}, "signal_events": []}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"backtest_start_date": "2023-12-02 00:00:00", "backtest_date_range_name": null, "backtest_end_date": "2023-12-03 00:00:00", "trading_symbol": "EUR", "initial_unallocated": 1000.0, "number_of_runs": 1441, "portfolio_snapshots": [{"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-02T00:00:00+00:00", "total_value": 1000.0}, {"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-03T00:00:00+00:00", "total_value": 1000.0}], "trades": [], "orders": [], "positions": [{"symbol": "EUR", "amount": 1000.0, "cost": 1000.0, "portfolio_id": 1}], "created_at": "2026-04-15 13:13:08", "symbols": [], "number_of_days": 0, "number_of_trades": 0, "number_of_trades_closed": 0, "number_of_trades_open": 0, "number_of_orders": 0, "number_of_positions": 0, "metadata": {}, "signals": {}, "signal_events": []}
1+
{"backtest_start_date": "2023-12-02 00:00:00", "backtest_date_range_name": null, "backtest_end_date": "2023-12-03 00:00:00", "trading_symbol": "EUR", "initial_unallocated": 1000.0, "number_of_runs": 1441, "portfolio_snapshots": [{"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-02T00:00:00+00:00", "total_value": 1000.0}, {"metadata": "MetaData()", "portfolio_id": "1", "trading_symbol": "EUR", "pending_value": 0.0, "unallocated": 1000.0, "total_net_gain": 0.0, "total_revenue": 0.0, "total_cost": 0.0, "cash_flow": 0.0, "net_size": 1000.0, "created_at": "2023-12-03T00:00:00+00:00", "total_value": 1000.0}], "trades": [], "orders": [], "positions": [{"symbol": "EUR", "amount": 1000.0, "cost": 1000.0, "portfolio_id": 1}], "created_at": "2026-04-15 14:57:08", "symbols": [], "number_of_days": 0, "number_of_trades": 0, "number_of_trades_closed": 0, "number_of_trades_open": 0, "number_of_orders": 0, "number_of_positions": 0, "metadata": {}, "signals": {}, "signal_events": []}

0 commit comments

Comments
 (0)