mts1b-oms-algos
Execution algorithms used by
mts1b-oms: VWAP, TWAP, POV, Iceberg, Almgren-Chriss, Implementation Shortfall planners.
Repo: github.com/MTS1B/mts1b-oms-algos
Layer: 3
Depends on: foundation, platform, quantkit
Audience: mts1b-oms (one consumer)
What it is
Each algorithm is an async function that takes a parent Order + params and produces a stream of child orders. They share a uniform signature so OMS can dispatch by name.
Naming convention
This repo uses the parent-child naming pattern: mts1b-oms-algos is "the library used by mts1b-oms". It has no audience independent of OMS.
The contract
from typing import AsyncIterator
from mts1b_foundation.orders import Order
async def execute(parent: Order, *, params: dict) -> AsyncIterator[Order]:
"""Execute parent order via this algorithm.
Yields child orders to be submitted via mts1b-oms.
The algorithm owns: slice sizing, timing, child order types.
OMS owns: state, idempotency, broker routing for each child.
"""
API source: e.g. services/trading/src/mts/trading/modules/execution/algos/vwap.py:execute.
Algorithms
VWAP — Volume-Weighted Average Price
from mts1b_oms_algos.vwap import execute
async for child in execute(parent_order, params={
"horizon_minutes": 30,
"volume_profile": "intraday_estimated", # historical avg by minute
"max_participation": 0.10, # cap at 10% of bar volume
}):
await oms.submit(child)
Slices the parent across the horizon weighted by the venue's historical volume profile. Designed to minimize timing risk vs the average price during the window.
Child order types: limit at NBBO mid or marketable_limit at touch.
TWAP — Time-Weighted Average Price
from mts1b_oms_algos.twap import execute
async for child in execute(parent_order, params={
"horizon_minutes": 30,
"n_slices": 30, # 1 slice per minute
"jitter_pct": 0.2, # ±20% jitter on each slice timing
}):
...
Equal-sized slices at fixed intervals (with jitter to avoid signaling). Used when volume profile is unreliable or unknown.
API source: services/trading/src/mts/trading/modules/execution/algos/twap.py:execute.
POV — Participation of Volume
from mts1b_oms_algos.pov import execute
async for child in execute(parent_order, params={
"target_participation": 0.15, # 15% of incremental volume
"max_horizon_minutes": 120, # bail out after 2h
}):
...
Adaptive: as venue volume builds, child orders scale up. As volume thins, they slow. No fixed horizon — finishes when parent quantity is filled or max_horizon_minutes elapses.
Iceberg
from mts1b_oms_algos.iceberg import execute
async for child in execute(parent_order, params={
"display_qty": 100, # show 100 of e.g. 5000
"refill_threshold": 50, # refill when remaining drops below this
"limit_price": Decimal("180.50"),
}):
...
Hides parent quantity by only displaying a portion. When the displayed lot fills, the algorithm refreshes with the next slice.
Almgren-Chriss
from mts1b_oms_algos.almgren_chriss import execute
async for child in execute(parent_order, params={
"horizon_minutes": 30,
"risk_aversion": 1.0,
"volatility": 0.18, # annualized
"permanent_impact": 0.1, # bps per unit of parent size
"temporary_impact": 5.0, # bps per unit of slice size
}):
...
Optimal-execution from Almgren & Chriss (2000) — trades off market impact (faster execution costs more) vs timing risk (slower execution exposes you to price drift). Closed-form solution for the optimal trade trajectory.
API source: services/trading/src/mts/trading/modules/execution/algos/almgren_chriss.py:execute.
Implementation Shortfall (IS)
from mts1b_oms_algos.is_ import execute
async for child in execute(parent_order, params={
"arrival_price": Decimal("180.50"),
"risk_aversion": 1.0,
"max_horizon_minutes": 60,
}):
...
Targets minimum implementation shortfall = realized_avg_price - arrival_price. More aggressive than VWAP for urgent fills; less than market order.
When to use which
| Scenario | Algorithm |
|---|---|
| Standard rebalance, moderate size, want benchmark | VWAP |
| Schedule-driven, want predictable timing | TWAP |
| Liquidity-driven, want to be a fraction of flow | POV |
| Hide size (large block) | Iceberg |
| Optimal trade-off impact/timing | Almgren-Chriss |
| Urgent — minimize shortfall vs arrival | IS |
| Tiny order (under threshold) | None — direct to broker |
The OMS routing/splitter (mts1b-oms/routing/splitter.py) picks an algorithm based on size + urgency from the parent order's params.
Configuration
Each algorithm has sensible defaults. Override per-strategy:
strategies:
large_cap_momentum:
execution:
algorithm: vwap
params:
horizon_minutes: 30
max_participation: 0.05
Risk
Child orders go through the full OMS pipeline — same risk gates as any order. The algorithm only decides the slice schedule; OMS still:
- Dedupes via idempotency_key (each child gets a unique one)
- Calls
riskengine.CheckOrder - Routes to the broker
- Tracks state independently
This means a faulty algorithm cannot bypass risk. The worst it can do is generate too many child orders → riskengine rejects the excess.
Build + test
pip install -e ".[dev]"
pytest -v # unit tests
pytest -m simulate # simulate against historical bar data
Tests verify:
- Total child quantity = parent quantity
- Last child completes within horizon (for time-bounded algos)
- VWAP child schedule matches expected volume profile
- Almgren-Chriss matches the closed-form solution
Roadmap
| Version | Items |
|---|---|
| 0.1 (Wave 1) | VWAP, TWAP, POV, Iceberg, Almgren-Chriss, IS |
| 0.2 (Wave 2) | Adaptive POV with sniper override |
| 0.3 (Wave 2) | TCA hooks (post-trade) |
| 0.4 (Wave 3) | Reinforcement-learning sliced execution (research) |
| 1.0 (LTS) | Stable execution interface |
See also
mts1b-oms— consumermts1b-quantkit/cost_models— fee + slippage models used in IS calculation- Concept: Foundation types — Order schema