ml4t-live 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. ml4t_live-0.1.0/.gitignore +41 -0
  2. ml4t_live-0.1.0/LICENSE +21 -0
  3. ml4t_live-0.1.0/PKG-INFO +410 -0
  4. ml4t_live-0.1.0/README.md +369 -0
  5. ml4t_live-0.1.0/pyproject.toml +150 -0
  6. ml4t_live-0.1.0/src/ml4t/live/__init__.py +138 -0
  7. ml4t_live-0.1.0/src/ml4t/live/_version.py +24 -0
  8. ml4t_live-0.1.0/src/ml4t/live/brokers/__init__.py +6 -0
  9. ml4t_live-0.1.0/src/ml4t/live/brokers/alpaca.py +969 -0
  10. ml4t_live-0.1.0/src/ml4t/live/brokers/ib.py +902 -0
  11. ml4t_live-0.1.0/src/ml4t/live/cli/__init__.py +1 -0
  12. ml4t_live-0.1.0/src/ml4t/live/cli/main.py +901 -0
  13. ml4t_live-0.1.0/src/ml4t/live/engine.py +1559 -0
  14. ml4t_live-0.1.0/src/ml4t/live/feeds/__init__.py +28 -0
  15. ml4t_live-0.1.0/src/ml4t/live/feeds/aggregator.py +409 -0
  16. ml4t_live-0.1.0/src/ml4t/live/feeds/alpaca_feed.py +621 -0
  17. ml4t_live-0.1.0/src/ml4t/live/feeds/crypto_feed.py +468 -0
  18. ml4t_live-0.1.0/src/ml4t/live/feeds/databento_feed.py +470 -0
  19. ml4t_live-0.1.0/src/ml4t/live/feeds/events.py +257 -0
  20. ml4t_live-0.1.0/src/ml4t/live/feeds/experimental.py +37 -0
  21. ml4t_live-0.1.0/src/ml4t/live/feeds/ib_feed.py +353 -0
  22. ml4t_live-0.1.0/src/ml4t/live/feeds/okx_feed.py +460 -0
  23. ml4t_live-0.1.0/src/ml4t/live/feeds/queue.py +198 -0
  24. ml4t_live-0.1.0/src/ml4t/live/lifecycle.py +213 -0
  25. ml4t_live-0.1.0/src/ml4t/live/orders.py +187 -0
  26. ml4t_live-0.1.0/src/ml4t/live/persistence.py +608 -0
  27. ml4t_live-0.1.0/src/ml4t/live/protocols.py +418 -0
  28. ml4t_live-0.1.0/src/ml4t/live/py.typed +0 -0
  29. ml4t_live-0.1.0/src/ml4t/live/runtime.py +994 -0
  30. ml4t_live-0.1.0/src/ml4t/live/safety.py +2654 -0
  31. ml4t_live-0.1.0/src/ml4t/live/state_migration.py +214 -0
  32. ml4t_live-0.1.0/src/ml4t/live/wrappers.py +359 -0
@@ -0,0 +1,41 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+ .env
22
+ .venv/
23
+ venv/
24
+ ENV/
25
+ .mypy_cache/
26
+ .ruff_cache/
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ *.log
31
+ .DS_Store
32
+ *.tmp
33
+ /data/
34
+ logs/
35
+ site/
36
+ src/ml4t/live/_version.py
37
+
38
+ # Claude Code (local development only)
39
+ CLAUDE.md
40
+ .claude/
41
+ .ml4t_risk_state.json
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Stefan Jansen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,410 @@
1
+ Metadata-Version: 2.4
2
+ Name: ml4t-live
3
+ Version: 0.1.0
4
+ Summary: Live trading platform for ML4T strategies
5
+ Project-URL: Homepage, https://www.ml4trading.io/docs/live/
6
+ Project-URL: Documentation, https://www.ml4trading.io/docs/live/
7
+ Project-URL: Repository, https://github.com/ml4t/live
8
+ Project-URL: Issues, https://github.com/ml4t/live/issues
9
+ Project-URL: Changelog, https://github.com/ml4t/live/releases
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Typing :: Typed
18
+ Requires-Python: <3.15,>=3.12
19
+ Requires-Dist: alpaca-py==0.43.5
20
+ Requires-Dist: ccxt<=4.5.71,>=4.5.31
21
+ Requires-Dist: httpx==0.28.1
22
+ Requires-Dist: ib-async==2.1.0
23
+ Requires-Dist: ml4t-backtest<0.2,>=0.1.0
24
+ Requires-Dist: ml4t-specs<0.2,>=0.1.1
25
+ Provides-Extra: dev
26
+ Requires-Dist: packaging==26.3; extra == 'dev'
27
+ Requires-Dist: pre-commit>=3.0; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
29
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
30
+ Requires-Dist: pytest>=8.0; extra == 'dev'
31
+ Requires-Dist: pyyaml==6.0.3; extra == 'dev'
32
+ Requires-Dist: ruff>=0.8; extra == 'dev'
33
+ Requires-Dist: ty; extra == 'dev'
34
+ Provides-Extra: docs
35
+ Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
36
+ Requires-Dist: mkdocs<2,>=1.6; extra == 'docs'
37
+ Requires-Dist: mkdocstrings[python]>=0.24.0; extra == 'docs'
38
+ Provides-Extra: experimental
39
+ Requires-Dist: databento==0.83.0; extra == 'experimental'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # ml4t-live
43
+
44
+ [![Python 3.12-3.14](https://img.shields.io/badge/python-3.12--3.14-blue.svg)](https://www.python.org/downloads/)
45
+ [![PyPI](https://img.shields.io/pypi/v/ml4t-live)](https://pypi.org/project/ml4t-live/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
47
+
48
+ Live trading runtime for causal ML4T strategies.
49
+
50
+ ## Part of the ML4T Library Ecosystem
51
+
52
+ This library is one of six interconnected libraries supporting the machine learning for trading workflow described in [Machine Learning for Trading](https://www.ml4trading.io/):
53
+
54
+ ![ML4T Library Ecosystem](docs/images/ml4t_ecosystem_workflow_color.png)
55
+
56
+ Together they cover data infrastructure, feature engineering, modeling, signal evaluation, strategy backtesting, and live deployment.
57
+
58
+ ## What This Library Does
59
+
60
+ Deploying a backtested strategy to live markets requires careful handling of async broker connections, risk limits, and testing infrastructure. ml4t-live provides:
61
+
62
+ - Strategy portability under the shared lifecycle version 1 contract
63
+ - Two broker integrations: Interactive Brokers (TWS/Gateway) and Alpaca (stocks + crypto)
64
+ - A stable-supported OKX feed plus typed bar aggregation
65
+ - Explicit opt-in experimental adapters for Alpaca, IB, generic CCXT, and DataBento workflows
66
+ - Shadow mode for testing without placing real orders (VirtualPortfolio tracking)
67
+ - 16-parameter risk configuration: position limits, order limits, loss limits, price protection
68
+ - Kill switch with crash-safe state persistence (atomic JSON writes)
69
+ - Startup preflight, reconciliation, and JSONL execution journaling for operator workflows
70
+ - Async architecture with thread-safe sync bridge for strategy callbacks
71
+
72
+ The goal is gradual deployment: shadow mode first, then paper trading, then live with small positions.
73
+
74
+ ![ml4t-live Architecture](docs/images/ml4t_live_architecture_print.jpeg)
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ uv add ml4t-live
80
+ ```
81
+
82
+ Add the optional DataBento SDK only for deliberate experimental evaluation:
83
+
84
+ ```bash
85
+ uv add 'ml4t-live[experimental]'
86
+ ```
87
+
88
+ ## Quick Start
89
+
90
+ ```python
91
+ from ml4t.backtest import Strategy, OrderSide
92
+ from ml4t.live import LiveEngine, LiveRiskConfig, SafeBroker
93
+ from ml4t.live.brokers.alpaca import AlpacaBroker
94
+ from ml4t.live.feeds.alpaca_feed import AlpacaDataFeed
95
+ import asyncio
96
+
97
+ # A lifecycle-v1 strategy that uses only portable callbacks and broker operations
98
+ class MyStrategy(Strategy):
99
+ def on_data(self, timestamp, data, context, broker):
100
+ if not broker.get_position('SPY'):
101
+ broker.submit_order('SPY', 10, side=OrderSide.BUY)
102
+
103
+ async def main():
104
+ broker = AlpacaBroker(api_key="...", secret_key="...", paper=True)
105
+ feed = AlpacaDataFeed(
106
+ api_key="...", secret_key="...", symbols=["SPY"], experimental=True
107
+ )
108
+
109
+ config = LiveRiskConfig(
110
+ execution_mode="shadow", # No real orders
111
+ max_position_value=50_000,
112
+ )
113
+ safe = SafeBroker(broker, config)
114
+
115
+ engine = LiveEngine(MyStrategy(), safe, feed)
116
+ await engine.connect()
117
+
118
+ try:
119
+ await engine.run()
120
+ finally:
121
+ await engine.stop()
122
+
123
+ asyncio.run(main())
124
+ ```
125
+
126
+ ## Broker Integrations
127
+
128
+ ### Alpaca
129
+
130
+ Stocks and crypto with paper trading by default:
131
+
132
+ ```python
133
+ from ml4t.live.brokers.alpaca import AlpacaBroker
134
+
135
+ broker = AlpacaBroker(
136
+ api_key="...",
137
+ secret_key="...",
138
+ paper=True, # Paper trading (default)
139
+ )
140
+ await broker.connect()
141
+ ```
142
+
143
+ ### Interactive Brokers
144
+
145
+ Full market access via TWS or IB Gateway:
146
+
147
+ ```python
148
+ from ml4t.live.brokers.ib import IBBroker
149
+
150
+ broker = IBBroker(port=7497) # TWS paper port
151
+ # broker = IBBroker(port=7496) # TWS live port
152
+
153
+ await broker.connect()
154
+ print(f"Connected: {broker.is_connected}")
155
+ ```
156
+
157
+ Requirements:
158
+ - IB TWS or Gateway running
159
+ - API connections enabled in TWS settings
160
+ - Paper trading account for initial testing
161
+
162
+ ## Data Feeds
163
+
164
+ | Feed | Source | Status | Coverage |
165
+ |------|--------|--------|----------|
166
+ | `AlpacaDataFeed` | Alpaca | experimental | US stocks + crypto, real-time bars/quotes/trades |
167
+ | `IBDataFeed` | Interactive Brokers | experimental | Multi-asset tick-by-tick data |
168
+ | `OKXFundingFeed` | OKX | stable-supported | Perpetual swaps with funding rates |
169
+ | `BarAggregator` | Any typed feed | stable-supported | Multi-feed aggregation + bar assembly |
170
+ | `DataBentoFeed` | DataBento | experimental | Historical replay + real-time streaming |
171
+ | `CryptoFeed` | CCXT | experimental | Generic exchange trades and candles |
172
+
173
+ ```python
174
+ from ml4t.live.feeds.alpaca_feed import AlpacaDataFeed
175
+ from ml4t.live.feeds.crypto_feed import CryptoFeed
176
+
177
+ # Experimental stock + crypto feed via Alpaca
178
+ feed = AlpacaDataFeed(
179
+ api_key="...", secret_key="...",
180
+ symbols=["AAPL", "BTC/USD"],
181
+ feed="iex", # "iex" (free) or "sip" (premium)
182
+ experimental=True,
183
+ )
184
+
185
+ # Experimental generic crypto adapter; not part of the stable support contract
186
+ feed = CryptoFeed(
187
+ exchange="binance",
188
+ symbols=["BTC/USDT", "ETH/USDT"],
189
+ timeframe="1m",
190
+ experimental=True,
191
+ )
192
+ ```
193
+
194
+ The experimental adapters require explicit opt-in and report their adapter-specific missing
195
+ guarantees on first use. The `experimental` package extra installs the DataBento SDK; the other
196
+ adapters are present in the default environment.
197
+
198
+ ## Risk Configuration
199
+
200
+ `LiveRiskConfig` controls all safety parameters. Wrap any broker with `SafeBroker` to enforce them:
201
+
202
+ ```python
203
+ from ml4t.live import LiveRiskConfig, SafeBroker
204
+
205
+ config = LiveRiskConfig(
206
+ # Explicit execution routing
207
+ execution_mode="shadow", # Virtual orders only (no real execution)
208
+
209
+ # Position limits
210
+ max_position_value=50_000, # Max $ per position
211
+ max_position_shares=1000, # Max shares per position
212
+ max_total_exposure=200_000, # Max total $ across all positions
213
+ max_positions=20, # Max number of positions
214
+
215
+ # Order limits
216
+ max_order_value=10_000, # Max $ per order
217
+ max_order_shares=500, # Max shares per order
218
+ max_orders_per_minute=10, # Rate limiting
219
+
220
+ # Loss limits
221
+ max_daily_loss=5_000, # Stop trading if exceeded
222
+ max_drawdown_pct=0.05, # Stop if 5% drawdown
223
+
224
+ # Price protection
225
+ max_price_deviation_pct=0.05, # Fat finger: reject if >5% from market
226
+ max_data_staleness_seconds=60, # Reject if data older than 60s
227
+ dedup_window_seconds=1.0, # Block duplicate orders within 1s
228
+
229
+ # Asset restrictions
230
+ allowed_assets={"SPY", "QQQ"}, # Whitelist (empty = allow all)
231
+
232
+ # Startup and persistence
233
+ fail_on_reconciliation_mismatch=True,
234
+ journal_file=".ml4t_execution_journal.jsonl",
235
+ )
236
+
237
+ safe_broker = SafeBroker(broker, config)
238
+ ```
239
+
240
+ Use `None` to disable an individual numeric limit. NaN and infinity are invalid. Order quantities
241
+ are signed only when `side` is omitted; an explicit side requires a positive unsigned quantity.
242
+
243
+ ## Safety System
244
+
245
+ ### Kill Switch
246
+
247
+ When drawdown exceeds `max_drawdown_pct`, the kill switch activates and blocks all new orders. The state persists across process restarts:
248
+
249
+ ```python
250
+ config = LiveRiskConfig(
251
+ execution_mode="shadow",
252
+ kill_switch_enabled=True,
253
+ max_drawdown_pct=0.05,
254
+ state_file=".ml4t_risk_state.json", # Atomic JSON writes
255
+ )
256
+ ```
257
+
258
+ ### Virtual Portfolio
259
+
260
+ Shadow mode tracks positions internally without broker interaction:
261
+
262
+ ```python
263
+ from ml4t.live import VirtualPortfolio
264
+
265
+ portfolio = VirtualPortfolio(initial_cash=100_000)
266
+ # SafeBroker uses this automatically when execution_mode="shadow"
267
+ ```
268
+
269
+ ### State Persistence
270
+
271
+ Risk state survives process crashes through a versioned, checksummed atomic file:
272
+
273
+ - `daily_loss` - Cumulative daily loss
274
+ - `orders_placed` - Orders placed today
275
+ - `high_water_mark` - Session high equity
276
+ - `kill_switch_activated` - Persists until manually reset
277
+
278
+ State and audit files use mode `0600`, reject unsafe ownership or symlinks, and permit one writer.
279
+ `SafeBroker` also writes a hash-chained JSONL execution journal with reconciliation, order,
280
+ kill-switch, and runtime health events. Audit failure blocks broker calls by default.
281
+
282
+ `LiveEngine` acquires the broker and feed transactionally. Startup failure, strategy failure,
283
+ cancellation, and normal completion release acquired resources in reverse order. Bounded recovery
284
+ does not repeat strategy startup callbacks; exhausted recovery and incomplete cleanup have distinct
285
+ public exceptions and a `failed` runtime state.
286
+
287
+ ## Operator CLI
288
+
289
+ Use the CLI as a thin operator surface around the Python API:
290
+
291
+ ```bash
292
+ # Fail-fast startup check for a real broker session
293
+ uv run ml4t-live preflight ib --state-file .ml4t_risk_state.json --strict
294
+
295
+ # Human-readable state and recent journal tail
296
+ uv run ml4t-live status --state-file .ml4t_risk_state.json
297
+
298
+ # Bounded shadow soak
299
+ uv run ml4t-live shadow examples/shadow_mode_demo.py --feed okx --duration 60
300
+ ```
301
+
302
+ `preflight` is the operator readiness command: it checks broker reachability, balances, persisted
303
+ kill-switch state, startup reconciliation, and session state, and exits non-zero when the result is
304
+ degraded.
305
+
306
+ ## Order Lifecycle
307
+
308
+ Strategies still place orders through the same synchronous wrapper interface, but pending orders can now be replaced in a normalized way:
309
+
310
+ ```python
311
+ def on_data(self, timestamp, data, context, broker):
312
+ if broker.pending_orders:
313
+ broker.replace_order(broker.pending_orders[0].order_id, limit_price=189.5)
314
+ ```
315
+
316
+ The default implementation uses a safe cancel-and-resubmit flow across supported brokers.
317
+
318
+ ## Deployment Progression
319
+
320
+ 1. **Shadow Mode** (1-2 weeks): Verify logic without real orders
321
+ 2. **Paper Trading** (2-4 weeks): Test with paper account
322
+ 3. **Live Micro** (1-2 weeks): Small positions ($100-500)
323
+ 4. **Live Small** (ongoing): Gradual size increase
324
+
325
+ ## Strategy Portability
326
+
327
+ A `Strategy` subclass can run in both environments when it satisfies lifecycle version 1 and uses
328
+ only the portable broker surface. Portability covers callback order and canonical strategy intent.
329
+ It does not make venue fills, latency, data subscriptions, risk decisions, or account state equal.
330
+
331
+ ```python
332
+ from ml4t.backtest import Strategy
333
+
334
+ class MyStrategy(Strategy):
335
+ def on_data(self, timestamp, data, context, broker):
336
+ # Portable decision logic; execution outcomes remain runtime-specific.
337
+ pass
338
+
339
+ # Backtest
340
+ from ml4t.backtest import Engine
341
+ result = Engine(feed, MyStrategy(), config).run()
342
+
343
+ # Live
344
+ from ml4t.live import LiveEngine
345
+ await LiveEngine(MyStrategy(), safe_broker, live_feed).run()
346
+ ```
347
+
348
+ See the [portability contract](docs/user-guide/backtest-to-live.md) and
349
+ [migration guide](docs/user-guide/migration.md) before moving an existing strategy.
350
+
351
+ ## Documentation
352
+
353
+ - [Installation](docs/getting-started/installation.md) - setup instructions
354
+ - [Quick Start](docs/getting-started/quickstart.md) - first live strategy
355
+ - [Brokers](docs/user-guide/brokers.md) - IB and Alpaca setup
356
+ - [Data Feeds](docs/user-guide/feeds.md) - supported and experimental feed contracts
357
+ - [Risk Management](docs/user-guide/risk.md) - LiveRiskConfig and SafeBroker
358
+ - [Candidate Qualification](docs/qualification.md) - validate an exact candidate without release
359
+
360
+ ## Stable Support Boundary
361
+
362
+ The stable candidate supports Linux with Python 3.12, 3.13, and 3.14. CI, wheel, and source
363
+ distribution qualification cover those interpreter versions. Windows, macOS, and Python 3.15 are
364
+ not part of this stable contract. IB and Alpaca broker adapters and the OKX feed are supported only
365
+ within the documented capabilities, reconciliation, causal-event, overload, and paper-account
366
+ boundaries. Alpaca, IB, DataBento, and generic CCXT feeds require explicit experimental opt-in.
367
+
368
+ ## Technical Characteristics
369
+
370
+ - **Versioned lifecycle**: `on_start`, `on_prepare`, `on_data`, and `on_end` follow the
371
+ negotiated shared lifecycle contract
372
+ - **Async/sync bridge**: All synchronous strategy callbacks run on one dedicated worker thread;
373
+ broker I/O stays on the async event loop without event-loop re-entry
374
+ - **Exception behavior**: Strategy exceptions abort the run, invoke `on_end` once after a
375
+ successful run start, and are reraised after cleanup
376
+ - **Protocol-based**: `BrokerProtocol`, `AsyncBrokerProtocol`, `DataFeedProtocol` for extensibility
377
+ - **Virtual portfolio**: Shadow mode tracks positions without broker interaction
378
+ - **Atomic state**: Risk state persisted via POSIX-atomic file writes (crash-safe)
379
+ - **Rate limiting**: Built-in protection against order flooding
380
+ - **Type-safe**: Full type annotations throughout
381
+
382
+ ## Related Libraries
383
+
384
+ - **ml4t-data**: Market data acquisition and storage
385
+ - **ml4t-engineer**: Feature engineering and technical indicators
386
+ - **ml4t-diagnostic**: Signal evaluation and statistical validation
387
+ - **ml4t-backtest**: Event-driven backtesting
388
+
389
+ ## Development
390
+
391
+ ```bash
392
+ git clone https://github.com/ml4t/live.git
393
+ cd ml4t-live
394
+ uv sync --all-extras --dev
395
+ uv run python scripts/qualification/run_stable_gate.py
396
+ ```
397
+
398
+ ## Safety Notice
399
+
400
+ This library is designed for paper trading and educational purposes. When transitioning to live trading:
401
+
402
+ - Always start with `execution_mode="shadow"`
403
+ - Set conservative position and order limits
404
+ - Enable `kill_switch_enabled=True` with a reasonable `max_drawdown_pct`
405
+ - Monitor virtual vs real positions carefully
406
+ - Use the deployment progression above
407
+
408
+ ## License
409
+
410
+ MIT License - see [LICENSE](LICENSE) for details.