Cost models¶
arbitrix-core separates what costs to charge (the cost model — a Python
module) from what parameters drive them (commission per lot, spread points,
slippage points, swap rates). You configure both through a single entry point:
arbitrix_core.costs.configure(...).
The default model¶
By default the engine uses
arbitrix_core.costs.models.parameterized — a six-function module that
applies user-provided parameters with sensible defaults:
| Function | Meaning |
|---|---|
commission_one_side(symbol, price, volume_lot) |
Fee per fill, scaled by volume |
commission_round_turn(symbol, price, volume_lot) |
Sum of both fills |
spread_cost(symbol, spread_points, volume_lot) |
Spread paid in account currency |
slippage_cost(symbol, slippage_points, volume_lot) |
Slippage paid in account currency |
swap_points(symbol, direction, static_override=None) |
Daily swap in points |
swap_cost_per_day(symbol, volume_lot, direction, static_override=None) |
Daily swap in account currency |
Configuring costs¶
from arbitrix_core import costs
costs.configure(
commission_per_lot=3.0,
point_overrides={"EURUSD": 10.0}, # 1 point = $10 per 1.0 lot
allow_provider_lookups=False, # open-core has no provider by default
model_identifier="arbitrix_core.costs.models.parameterized", # default
model_parameters={
"commission_per_lot": 3.0,
"spread_points": 1.0,
"slippage_points": 0.5,
},
symbol_model_parameters={
"eurusd": {"commission_per_lot": 2.5}, # per-symbol override
},
)
| Argument | Purpose |
|---|---|
commission_per_lot |
Per-fill commission baseline |
point_overrides |
{symbol: point_value} — required when no provider is wired |
allow_provider_lookups |
Set False for offline / open-core use |
model_identifier |
Module path of the active cost model |
model_parameters |
Dict passed to the active model's functions |
symbol_model_parameters |
Per-symbol overrides; lookup key is lowercased symbol |
symbol_models |
{symbol: identifier} — different model per symbol |
instruments |
{symbol: InstrumentConfig} — for advanced cost structures |
provider |
Live broker symbol-info source (closed Arbitrix only) |
After configuration:
costs.get_active_cost_model() # -> {"name": "...", "module": "..."}
costs.commission_one_side("EURUSD", price=1.10, volume_lot=1.0)
costs.spread_cost("EURUSD", spread_points=1.5, volume_lot=1.0)
Disabling cost components¶
Two layers of switches control how costs flow into the equity curve:
BTConfig.apply_spread_cost(defaultTrue) — whether the engine adds spread cost to each tradeBTConfig.apply_swap_cost(defaultTrue) — whether daily swap accrues on open trades- Set
model_parameters={"spread_points": 0, "slippage_points": 0}to zero out at the model layer
Custom cost modules¶
Any importable Python module exposing the six required functions can be a
cost model. Convention: define MODULE_NAME = __name__ at the top so the
registry stores the canonical import path.
Skeleton:
# my_costs.py
MODULE_NAME = __name__
def commission_one_side(symbol, price, volume_lot):
return 1.5 * volume_lot
def commission_round_turn(symbol, price, volume_lot):
return 2.0 * commission_one_side(symbol, price, volume_lot)
def spread_cost(symbol, spread_points, volume_lot):
return 0.5 * spread_points * volume_lot
def slippage_cost(symbol, slippage_points, volume_lot):
return 0.0
def swap_points(symbol, direction, static_override=None):
return 0.0
def swap_cost_per_day(symbol, volume_lot, direction, static_override=None):
return 0.0
Registering it:
costs.configure(
commission_per_lot=1.5,
point_overrides={"EURUSD": 10.0},
allow_provider_lookups=False,
model_identifier="my_costs", # any importable name
)
The registry calls importlib.import_module(model_identifier) — there is no
filesystem magic in open-core. Make sure your module is on sys.path.
A complete worked example, including a custom cost_models/zero_slippage.py
sibling to a runnable backtest, lives at examples/03_custom_cost_model.py
in the source tree.
Per-symbol models¶
Pass symbol_models={"EURUSD": "module.a", "AAPL": "module.b"} to
configure() to use a different model per symbol. The active default model
applies to every other symbol. Use symbol_model_parameters to give each
symbol its own parameter set.