kite-algo-worker 0.6.2__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 (36) hide show
  1. kite_algo_worker-0.6.2/PKG-INFO +424 -0
  2. kite_algo_worker-0.6.2/README.md +392 -0
  3. kite_algo_worker-0.6.2/kite_algo_worker/__init__.py +154 -0
  4. kite_algo_worker-0.6.2/kite_algo_worker/async_client.py +262 -0
  5. kite_algo_worker-0.6.2/kite_algo_worker/client.py +496 -0
  6. kite_algo_worker-0.6.2/kite_algo_worker/exceptions.py +57 -0
  7. kite_algo_worker-0.6.2/kite_algo_worker/helpers.py +197 -0
  8. kite_algo_worker-0.6.2/kite_algo_worker/indicators/__init__.py +413 -0
  9. kite_algo_worker-0.6.2/kite_algo_worker/indicators/base.py +140 -0
  10. kite_algo_worker-0.6.2/kite_algo_worker/indicators/hybrid.py +314 -0
  11. kite_algo_worker-0.6.2/kite_algo_worker/indicators/live.py +314 -0
  12. kite_algo_worker-0.6.2/kite_algo_worker/indicators/momentum.py +365 -0
  13. kite_algo_worker-0.6.2/kite_algo_worker/indicators/numba_compat.py +31 -0
  14. kite_algo_worker-0.6.2/kite_algo_worker/indicators/oscillators.py +83 -0
  15. kite_algo_worker-0.6.2/kite_algo_worker/indicators/statistics.py +55 -0
  16. kite_algo_worker-0.6.2/kite_algo_worker/indicators/trend.py +336 -0
  17. kite_algo_worker-0.6.2/kite_algo_worker/indicators/utils.py +135 -0
  18. kite_algo_worker-0.6.2/kite_algo_worker/indicators/volatility.py +209 -0
  19. kite_algo_worker-0.6.2/kite_algo_worker/indicators/volume.py +309 -0
  20. kite_algo_worker-0.6.2/kite_algo_worker/live_protection_certification.py +225 -0
  21. kite_algo_worker-0.6.2/kite_algo_worker/marketdata.py +167 -0
  22. kite_algo_worker-0.6.2/kite_algo_worker/models.py +480 -0
  23. kite_algo_worker-0.6.2/kite_algo_worker/options/__init__.py +11 -0
  24. kite_algo_worker-0.6.2/kite_algo_worker/options/client.py +155 -0
  25. kite_algo_worker-0.6.2/kite_algo_worker/options/models.py +53 -0
  26. kite_algo_worker-0.6.2/kite_algo_worker/options/structures.py +54 -0
  27. kite_algo_worker-0.6.2/kite_algo_worker/orders.py +180 -0
  28. kite_algo_worker-0.6.2/kite_algo_worker/protection.py +242 -0
  29. kite_algo_worker-0.6.2/kite_algo_worker/ws.py +279 -0
  30. kite_algo_worker-0.6.2/kite_algo_worker.egg-info/PKG-INFO +424 -0
  31. kite_algo_worker-0.6.2/kite_algo_worker.egg-info/SOURCES.txt +34 -0
  32. kite_algo_worker-0.6.2/kite_algo_worker.egg-info/dependency_links.txt +1 -0
  33. kite_algo_worker-0.6.2/kite_algo_worker.egg-info/requires.txt +12 -0
  34. kite_algo_worker-0.6.2/kite_algo_worker.egg-info/top_level.txt +1 -0
  35. kite_algo_worker-0.6.2/pyproject.toml +38 -0
  36. kite_algo_worker-0.6.2/setup.cfg +4 -0
@@ -0,0 +1,424 @@
1
+ Metadata-Version: 2.4
2
+ Name: kite-algo-worker
3
+ Version: 0.6.2
4
+ Summary: Thin Python SDK for external Kite Algo strategy workers
5
+ License-Expression: Apache-2.0
6
+ Project-URL: Homepage, https://github.com/krishna-vinci/kite-algo
7
+ Project-URL: Documentation, https://github.com/krishna-vinci/kite-algo/blob/main/sdk/python/README.md
8
+ Project-URL: Repository, https://github.com/krishna-vinci/kite-algo
9
+ Project-URL: Issues, https://github.com/krishna-vinci/kite-algo/issues
10
+ Keywords: kite,zerodha,algo-trading,trading,sdk
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Office/Business :: Financial :: Investment
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: requests>=2
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: websockets>=12
25
+ Provides-Extra: dataframe
26
+ Requires-Dist: pandas>=2; extra == "dataframe"
27
+ Requires-Dist: numpy>=1.24; extra == "dataframe"
28
+ Provides-Extra: indicators
29
+ Requires-Dist: pandas>=2; extra == "indicators"
30
+ Requires-Dist: numpy>=1.24; extra == "indicators"
31
+ Requires-Dist: numba>=0.59; extra == "indicators"
32
+
33
+ # Kite Algo Worker Python SDK
34
+
35
+ Thin Python SDK for external Kite Algo strategy workers.
36
+
37
+ The SDK only calls public Kite Algo worker API endpoints under `/api/algo-workers/worker/*`. It does not call broker internals, paper-runtime internals, market-runtime internals, or the database.
38
+
39
+ ## Install from PyPI
40
+
41
+ Recommended for public installs and remote strategy servers:
42
+
43
+ ```bash
44
+ python3 -m pip install kite-algo-worker==0.6.2
45
+ ```
46
+
47
+ Extras:
48
+
49
+ ```bash
50
+ python3 -m pip install "kite-algo-worker[dataframe]==0.6.2"
51
+ python3 -m pip install "kite-algo-worker[indicators]==0.6.2"
52
+ ```
53
+
54
+ Pin to an immutable version in production.
55
+
56
+ ## Install from a Git tag
57
+
58
+ Use this when you need an exact monorepo tag before or instead of a PyPI release:
59
+
60
+ ```bash
61
+ python3 -m pip install \
62
+ "kite-algo-worker @ git+ssh://git@github.com/krishna-vinci/kite-algo.git@kite-algo-worker-v0.6.2#subdirectory=sdk/python"
63
+ ```
64
+
65
+ HTTPS form:
66
+
67
+ ```bash
68
+ python3 -m pip install \
69
+ "kite-algo-worker @ git+https://github.com/krishna-vinci/kite-algo.git@kite-algo-worker-v0.6.2#subdirectory=sdk/python"
70
+ ```
71
+
72
+ Pin to an immutable tag in production. Avoid installing from a moving branch such as `main` on live strategy servers.
73
+
74
+ ## Install variants
75
+
76
+ Choose the smallest install that matches your worker:
77
+
78
+ - base SDK: HTTP/WebSocket clients, typed models, order helpers
79
+ - `dataframe` extra: adds `pandas` + `numpy` for `candles_to_df(...)` and `ohlcv_arrays(...)`
80
+ - `indicators` extra: adds dataframe dependencies plus the indicator stack and optional `numba`
81
+
82
+ From a local checkout:
83
+
84
+ ```bash
85
+ python3 -m pip install -e /path/to/kite-algo/sdk/python
86
+ python3 -m pip install -e "/path/to/kite-algo/sdk/python[dataframe]"
87
+ python3 -m pip install -e "/path/to/kite-algo/sdk/python[indicators]"
88
+ ```
89
+
90
+ From a Git tag:
91
+
92
+ ```bash
93
+ python3 -m pip install \
94
+ "kite-algo-worker[dataframe] @ git+ssh://git@github.com/krishna-vinci/kite-algo.git@kite-algo-worker-v0.6.2#subdirectory=sdk/python"
95
+ python3 -m pip install \
96
+ "kite-algo-worker[indicators] @ git+ssh://git@github.com/krishna-vinci/kite-algo.git@kite-algo-worker-v0.6.2#subdirectory=sdk/python"
97
+ ```
98
+
99
+ ## Release conventions
100
+
101
+ - app/product tags: `vX.Y.Z`
102
+ - SDK package tags: `kite-algo-worker-vX.Y.Z`
103
+
104
+ The SDK has its own semantic version stream because this repo is a monorepo and backend changes should not force SDK releases.
105
+
106
+ ## Publish the SDK
107
+
108
+ After the SDK changes are committed and pushed, bump `sdk/python/pyproject.toml` and create the matching SDK tag from the repository root:
109
+
110
+ ```bash
111
+ git tag -a kite-algo-worker-v0.6.2 -m "kite-algo-worker v0.6.2"
112
+ git push origin kite-algo-worker-v0.6.2
113
+ ```
114
+
115
+ Pushing a `kite-algo-worker-v*` tag triggers GitHub Actions to:
116
+
117
+ 1. build the wheel and source distribution from `sdk/python`
118
+ 2. run `twine check`
119
+ 3. publish the package to PyPI as `kite-algo-worker`
120
+
121
+ ### First-time PyPI setup
122
+
123
+ Configure PyPI trusted publishing for `kite-algo-worker` to trust the GitHub workflow:
124
+
125
+ - repository: `krishna-vinci/kite-algo`
126
+ - workflow: `publish-kite-algo-worker.yml`
127
+ - environment: `pypi`
128
+
129
+ After that, remote servers should prefer the PyPI install command above.
130
+
131
+ ## Minimal usage
132
+
133
+ ```python
134
+ from kite_algo_worker import AlgoWorkerConfig, KiteAlgoWorkerClient, equity_market_order
135
+
136
+ client = KiteAlgoWorkerClient(AlgoWorkerConfig(
137
+ base_url="https://kite-algo.example.com",
138
+ token="kwa_...",
139
+ ))
140
+
141
+ client.health()
142
+
143
+ run = client.create_run(
144
+ strategy_run_id="run_mean_reversion_001",
145
+ template_id="mean-reversion",
146
+ account_scope="kite:paper-a",
147
+ execution_mode="paper",
148
+ metadata={"strategy_family": "indicator_strategy", "strategy_name": "Mean Reversion"},
149
+ )
150
+
151
+ order = equity_market_order("INFY", "BUY", 1)
152
+ client.place_order(run["strategy_run_id"], order, "run_mean_reversion_001:entry:001")
153
+
154
+ pnl = client.get_run_pnl(run["strategy_run_id"])
155
+ print(pnl["totals"]["net_pnl"])
156
+
157
+ for update in client.stream_run_pnl(run["strategy_run_id"], interval_seconds=1.0):
158
+ print(update["totals"]["net_pnl"])
159
+ break
160
+ ```
161
+
162
+ ## Dataframe shaping helpers
163
+
164
+ Use the `dataframe` or `indicators` extra when you want pandas-friendly candle shaping:
165
+
166
+ ```python
167
+ from kite_algo_worker import AlgoWorkerConfig, KiteAlgoWorkerClient, candles_to_df, ohlcv_arrays
168
+
169
+ client = KiteAlgoWorkerClient(AlgoWorkerConfig(
170
+ base_url="http://localhost:8000",
171
+ token="kwa_...",
172
+ ))
173
+
174
+ history = client.get_historical_candles_snapshot(
175
+ "NSE:RELIANCE",
176
+ timeframe="5minute",
177
+ from_date="2026-04-01T09:15:00+05:30",
178
+ to_date="2026-04-28T15:30:00+05:30",
179
+ )
180
+
181
+ df = candles_to_df(history)
182
+ arrays = ohlcv_arrays(df)
183
+
184
+ print(df[["open", "high", "low", "close", "volume"]].tail())
185
+ print(arrays.close[-3:])
186
+ print(arrays.is_complete[-3:])
187
+ ```
188
+
189
+ `candles_to_df(...)` accepts raw API payloads, typed `WorkerHistoricalCandles`, a single `WorkerCandle`, or an existing DataFrame. It sorts by candle timestamp, de-dupes duplicate timestamps by keeping the latest row, and returns a DataFrame indexed by `ts`.
190
+
191
+ `ohlcv_arrays(...)` converts the same inputs into numpy arrays for fast batch calculations or custom vectorized logic.
192
+
193
+ ## Batch indicator workflow
194
+
195
+ Install with `kite-algo-worker[indicators]` to use the built-in indicator surface:
196
+
197
+ ```python
198
+ from kite_algo_worker import AlgoWorkerConfig, KiteAlgoWorkerClient, candles_to_df, ohlcv_arrays, ta
199
+
200
+ client = KiteAlgoWorkerClient(AlgoWorkerConfig(
201
+ base_url="http://localhost:8000",
202
+ token="kwa_...",
203
+ ))
204
+
205
+ history = client.get_historical_candles_snapshot("NSE:INFY", timeframe="5minute")
206
+ df = candles_to_df(history)
207
+ arrays = ohlcv_arrays(df)
208
+
209
+ df["ema_fast"] = ta.ema(df["close"], period=9)
210
+ df["ema_slow"] = ta.ema(df["close"], period=21)
211
+ df["rsi_14"] = ta.rsi(df["close"], period=14)
212
+ df["atr_14"] = ta.atr(df, period=14)
213
+ macd = ta.macd(arrays.close, fast_period=12, slow_period=26, signal_period=9)
214
+
215
+ latest = df.iloc[-1]
216
+ print({
217
+ "close": latest["close"],
218
+ "ema_fast": latest["ema_fast"],
219
+ "ema_slow": latest["ema_slow"],
220
+ "rsi_14": latest["rsi_14"],
221
+ "macd_histogram": macd.iloc[-1]["histogram"],
222
+ })
223
+ ```
224
+
225
+ `from kite_algo_worker import ta` gives you the OpenAlgo-style facade for indicators such as `ta.sma(...)`, `ta.ema(...)`, `ta.rsi(...)`, `ta.macd(...)`, `ta.atr(...)`, `ta.supertrend(...)`, and the related crossover/highest/lowest helpers.
226
+
227
+ See `examples/batch_indicator_workflow.py` for a complete batch example.
228
+
229
+ ## Live indicator engine
230
+
231
+ `LiveIndicatorEngine` keeps confirmed indicator state from historical candles and lets you evaluate provisional values on the current in-progress candle.
232
+
233
+ ```python
234
+ from kite_algo_worker import AlgoWorkerConfig, KiteAlgoWorkerClient, LiveIndicatorEngine, candles_to_df
235
+
236
+ client = KiteAlgoWorkerClient(AlgoWorkerConfig(
237
+ base_url="http://localhost:8000",
238
+ token="kwa_...",
239
+ ))
240
+
241
+ history = client.get_historical_candles_snapshot("NSE:INFY", timeframe="5minute")
242
+ engine = LiveIndicatorEngine.from_history(
243
+ candles_to_df(history),
244
+ indicators=[
245
+ ("ema", {"source": "close", "period": 9}),
246
+ ("rsi", {"source": "close", "period": 14}),
247
+ ("macd", {"source": "close", "fast_period": 12, "slow_period": 26, "signal_period": 9}),
248
+ ],
249
+ )
250
+
251
+ for event in client.stream_candles("NSE:INFY", interval="5minute"):
252
+ candle = event.get("current") or event
253
+ if not candle:
254
+ continue
255
+ values = engine.finalize_candle(candle) if candle.get("is_complete") else engine.update_provisional(candle)
256
+ print(engine.metadata)
257
+ print(values["ema"], values["rsi"], values["macd"])
258
+ ```
259
+
260
+ Semantics:
261
+
262
+ - `update_provisional(candle)` computes values on the current incomplete candle without mutating confirmed history
263
+ - `finalize_candle(candle)` commits that bar into confirmed history and clears provisional state
264
+ - `rebuild(history_df, last_stream_candle=...)` reconstructs local state after a restart or reconnect
265
+
266
+ See `examples/live_indicator_engine_worker.py` for a full recovery-aware example.
267
+
268
+ ## Recovery helpers
269
+
270
+ The SDK also includes polling helpers for common restart/warmup flows:
271
+
272
+ - `wait_for_history(...)`: poll until the backend returns any historical candles
273
+ - `warmup_history(...)`: poll until you have a minimum candle count for indicator warmup
274
+ - `wait_for_fresh_candle(...)`: poll until the current/latest candle is complete
275
+ - `wait_for_terminal_order_state(...)`: poll until an order reaches `COMPLETE`, `CANCELLED`, or `REJECTED`
276
+
277
+ Typical restart flow for indicator workers:
278
+
279
+ 1. call `get_run(...)` to recover backend-owned run state
280
+ 2. call `warmup_history(...)` or `get_historical_candles_snapshot(...)`
281
+ 3. rebuild your local dataframe and `LiveIndicatorEngine`
282
+ 4. reconnect `stream_ticks(...)`, `stream_candles(...)`, or `stream_run_pnl(...)`
283
+
284
+ ## AMO orders
285
+
286
+ AMO is supported with `variety="amo"`:
287
+
288
+ ```python
289
+ from kite_algo_worker import equity_market_order, limit_order
290
+
291
+ amo_market = equity_market_order("INFY", "BUY", 1, variety="amo")
292
+ amo_limit = limit_order("NSE", "INFY", "BUY", "CNC", 1, price=1450.0, variety="amo")
293
+ ```
294
+
295
+ ## Safety rules
296
+
297
+ - Use deterministic idempotency keys for every order intent.
298
+ - Start strategies in `dry_run`, then `paper`, then explicitly validated `live`.
299
+ - Do not send broker tags or attribution; the backend injects them.
300
+ - Keep tokens in environment variables or a secret manager.
301
+
302
+ ## Hardened core surface
303
+
304
+ The production-safe SDK surface is centered on a few stable calls:
305
+
306
+ - lifecycle and recovery: `health()`, `heartbeat(...)`, `create_run(...)`, `get_run(...)`
307
+ - sizing and accounting: `get_funds(...)`, `get_run_funds(...)`, `get_run_pnl(...)`, `stream_run_pnl(...)`
308
+ - execution control: `list_orders(...)`, `list_trades(...)`, `preview_order(...)`, `preview_basket(...)`, `place_order(...)`, `place_basket(...)`, `exit_run(...)`
309
+ - market data: `resolve_ticker(...)`, `search_tickers(...)`, `get_quotes(...)`, `stream_ticks(...)`, `get_candles(...)`, `stream_candles(...)`, `get_historical_candles(...)`, `get_market_snapshot(...)`
310
+ - recovery helpers: `wait_for_history(...)` and the websocket client for reconnecting streams
311
+
312
+ `scripts/sdk_worker_certification.py` now reports preview output and a simple capability summary for this core surface.
313
+
314
+ ## Realtime grouped run P&L
315
+
316
+ The SDK exposes grouped run-level P&L helpers:
317
+
318
+ - `get_run_pnl(strategy_run_id)`
319
+ - `stream_run_pnl(strategy_run_id, interval_seconds=1.0)`
320
+
321
+ The backend remains the source of truth for paper/live separation, attribution, charges, and grouped run state.
322
+
323
+ ## Funds and allocation
324
+
325
+ Workers can read backend-owned account funds and run-level allocation usage without calling broker APIs directly:
326
+
327
+ ```python
328
+ account_funds = client.get_funds(mode="paper")
329
+ run_funds = client.get_run_funds("run_mean_reversion_001")
330
+
331
+ remaining = (run_funds.get("strategy", {}).get("allocation", {}) or {}).get("remaining")
332
+ if remaining is not None and remaining < 10_000:
333
+ print("Skip new entry; allocation cap is nearly used")
334
+ ```
335
+
336
+ `get_funds()` returns account-level funds for the token's account scope. `get_run_funds()` adds current run exposure/P&L and, when the run metadata includes `allocation_cap` or `allocation_cap_inr`, returns remaining run allocation using current gross exposure as the V1 usage basis.
337
+
338
+ ## Backend protection helpers
339
+
340
+ Workers can register backend-owned exposure protection when they create or update a run.
341
+
342
+ Current V1 submits a conservative attributed strategy exit when a declared backend protection rule triggers. Position rules define leg-level thresholds; they do not re-enter, roll, rebalance, or run custom worker logic.
343
+
344
+ ```python
345
+ from kite_algo_worker import BackendProtection, BasketProtection, OperationalProtection, ProtectedPosition
346
+
347
+ protection = BackendProtection(
348
+ positions=[
349
+ ProtectedPosition(
350
+ symbol="NSE:INFY",
351
+ product="CNC",
352
+ side="BUY",
353
+ quantity=1,
354
+ entry_price=1500,
355
+ stoploss_pct=2,
356
+ )
357
+ ],
358
+ basket=BasketProtection(stoploss_pct=4),
359
+ operations=OperationalProtection(exit_on_worker_stale=True, worker_stale_sec=300),
360
+ )
361
+
362
+ client.create_run(
363
+ strategy_run_id="run_mean_reversion_001",
364
+ template_id="mean-reversion",
365
+ account_scope="kite:paper-a",
366
+ execution_mode="paper",
367
+ backend_protection=protection,
368
+ )
369
+
370
+ client.update_backend_protection("run_mean_reversion_001", protection, reason="rebalance")
371
+ ```
372
+
373
+ Validation mirrors the backend contract: products must be `CNC`/`MIS`/`NRML`, sides must be `BUY`/`SELL`, quantities and prices must be positive, stale-worker limits must stay between `30` and `86400` seconds, and MIS squareoff buffer must stay between `0` and `3600` seconds.
374
+
375
+ ## Runtime-backed market data
376
+
377
+ The SDK exposes worker-safe market-data helpers backed by Kite Algo's Go market-runtime. Workers do not connect to broker websockets, Redis, or backend internals directly.
378
+
379
+ ```python
380
+ instrument = client.resolve_ticker("NSE:INFY")
381
+ quotes = client.get_quotes(["NSE:INFY"], mode="quote")
382
+ candles = client.get_candles("NSE:INFY", interval="5minute", lookback=50)
383
+ history = client.get_historical_candles(
384
+ "NSE:INFY",
385
+ timeframe="day",
386
+ from_date="2024-01-01T00:00:00Z",
387
+ to_date="2024-12-31T00:00:00Z",
388
+ ingest=True,
389
+ passthrough=False,
390
+ )
391
+
392
+ for event in client.stream_ticks(["NSE:INFY"], mode="quote"):
393
+ for tick in event.get("ticks", []):
394
+ print(tick["last_price"])
395
+ ```
396
+
397
+ Available helpers:
398
+
399
+ - `resolve_ticker(symbol)` / `resolve_tickers([...])`
400
+ - `search_tickers(query, exchange=None, limit=20)`
401
+ - `get_quotes([...], mode="quote")`
402
+ - `stream_ticks([...], mode="quote")`
403
+ - `get_candles(symbol_or_token, interval="5minute", lookback=50)`
404
+ - `get_current_candle(symbol_or_token, interval="5minute")`
405
+ - `get_historical_candles(symbol_or_token, timeframe="day", from_date=None, to_date=None, ingest=True, passthrough=False)`
406
+ - `stream_candles(symbol_or_token, interval="5minute")`
407
+ - `get_market_snapshot(...)`
408
+
409
+ `get_historical_candles(...)` uses the backend candle facade. With `ingest=True`, the backend can trigger background ingestion for missing DB ranges. With `passthrough=True`, the backend fetches directly from Kite through the controlled system session for fresh historical data. Workers still never call Kite or the database directly.
410
+
411
+ If a worker stops, strategy decisions stop. Existing broker orders and positions remain with broker/backend accounting. Restart workers with the same `strategy_run_id`, call `get_run`, call `get_run_pnl`, rebuild local indicator state from historical candles, and reconnect SSE streams.
412
+
413
+ Options-specific helpers are intentionally deferred to a later `kite_algo_worker.options` layer inside the same SDK package.
414
+
415
+ ## Examples
416
+
417
+ - `examples/mean_reversion_worker.py`: minimal safe worker lifecycle example
418
+ - `examples/realtime_market_data_worker.py`: basic runtime-backed quote/candle streaming
419
+ - `examples/batch_indicator_workflow.py`: dataframe + `ohlcv_arrays(...)` + `from kite_algo_worker import ta`
420
+ - `examples/live_indicator_engine_worker.py`: confirmed/provisional live indicator loop with restart rebuild semantics
421
+ - `examples/protected_mean_reversion_worker.py`: mean-reversion worker with backend protection
422
+ - `examples/protected_momentum_worker.py`: basket worker with backend-owned protection
423
+ - `examples/option_basket_worker.py`: option basket order shaping example
424
+ - `examples/live_exit_preview.py`: safe live exit preview without sending broker exit orders