aether-quant 0.0.1__py3-none-any.whl
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.
- aether_quant-0.0.1.dist-info/METADATA +7 -0
- aether_quant-0.0.1.dist-info/RECORD +99 -0
- aether_quant-0.0.1.dist-info/WHEEL +5 -0
- aether_quant-0.0.1.dist-info/entry_points.txt +2 -0
- aether_quant-0.0.1.dist-info/licenses/LICENSE +133 -0
- aether_quant-0.0.1.dist-info/top_level.txt +13 -0
- analyzer/README.md +69 -0
- analyzer/__init__.py +17 -0
- analyzer/market_analyzer.py +521 -0
- aq_cli.py +3633 -0
- audit/README.md +47 -0
- audit/__init__.py +38 -0
- audit/hash_chain.py +52 -0
- audit/postgres_audit.py +61 -0
- audit/postgres_worker.py +402 -0
- audit/redis_queue.py +124 -0
- audit/status_export.py +62 -0
- data_pipeline/README.md +257 -0
- data_pipeline/__init__.py +6 -0
- data_pipeline/bar_synthesis.py +97 -0
- data_pipeline/dividend_backfill.py +243 -0
- data_pipeline/factor_file_backfill.py +276 -0
- data_pipeline/fetch.py +206 -0
- data_pipeline/fred_backfill.py +435 -0
- data_pipeline/ib_backfill.py +287 -0
- data_pipeline/v2_manifest.py +75 -0
- data_pipeline/yfinance_backfill.py +501 -0
- evaluation/README.md +152 -0
- evaluation/__init__.py +73 -0
- evaluation/ablation.py +310 -0
- evaluation/benchmark_comparison.py +195 -0
- evaluation/confidence_threshold_calibration.py +157 -0
- evaluation/factor_neutralization_check.py +94 -0
- evaluation/feature_parity.py +321 -0
- evaluation/feature_reconciliation.py +286 -0
- evaluation/impact_model.py +77 -0
- evaluation/kill_switch_replay.py +186 -0
- evaluation/limit_fill_simulator.py +152 -0
- evaluation/model_predictions.py +192 -0
- evaluation/monte_carlo.py +161 -0
- evaluation/public_benchmarks.py +117 -0
- evaluation/rank_book_simulator.py +494 -0
- evaluation/rank_ic_core.py +204 -0
- evaluation/rank_signal_calibration.py +690 -0
- evaluation/rolling_ic_gate_calibration.py +190 -0
- evaluation/rolling_ic_gate_replay.py +138 -0
- execution/README.md +281 -0
- execution/__init__.py +80 -0
- execution/config_cache.py +48 -0
- execution/cost_model.py +213 -0
- execution/lean_config_render.py +131 -0
- execution/live_credentials.py +38 -0
- execution/live_credentials_io.py +52 -0
- execution/order_gate.py +388 -0
- execution/paper_readiness.py +181 -0
- execution/paper_readiness_io.py +54 -0
- execution/paper_readiness_report.py +103 -0
- execution/paper_readiness_scheduler.py +137 -0
- execution/reconciliation.py +160 -0
- execution/runtime_config_io.py +38 -0
- execution/secret_scan.py +73 -0
- features/README.md +77 -0
- features/__init__.py +137 -0
- features/alt_data_features.py +154 -0
- features/bond_features.py +275 -0
- features/cross_asset_sensitivity.py +79 -0
- features/derivatives_macro_features.py +72 -0
- features/macro_features.py +79 -0
- features/options_greeks.py +329 -0
- features/sector_map.py +53 -0
- features/technical_indicators.py +185 -0
- generate_backtest_report.py +311 -0
- generate_evaluation_report.py +712 -0
- inference/README.md +105 -0
- inference/__init__.py +36 -0
- inference/exported_model.py +929 -0
- inference/parallel_inference.py +160 -0
- inference/strategy_selector_inference.py +75 -0
- json_safety.py +130 -0
- portfolio/README.md +447 -0
- portfolio/__init__.py +41 -0
- portfolio/book_construction.py +786 -0
- portfolio/book_neutrality.py +178 -0
- portfolio/hrp_allocation.py +159 -0
- portfolio/options_arbitrage_detector.py +232 -0
- portfolio/options_assignment_risk.py +119 -0
- portfolio/options_margin_sizing.py +333 -0
- portfolio/options_strategy.py +1999 -0
- portfolio/rank_signal.py +219 -0
- portfolio/rolling_ic_gate.py +183 -0
- risk/README.md +275 -0
- risk/__init__.py +38 -0
- risk/asset_class_router.py +584 -0
- risk/forex_risk.py +158 -0
- risk/futures_risk.py +202 -0
- risk/kill_switch.py +261 -0
- risk/manual_override.py +118 -0
- risk/position_sizing.py +303 -0
- risk/rl_sizing.py +189 -0
risk/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# risk
|
|
2
|
+
|
|
3
|
+
Dynamic, per-asset-class risk control: volatility-adjusted position sizing,
|
|
4
|
+
leverage caps, drawdown-aware sizing, liquidity checks, market-impact/slippage
|
|
5
|
+
controls, and asset-class dispatch (equity/crypto/bond, futures, options,
|
|
6
|
+
forex). Equity/crypto/bond flow through the shared volatility-scaled sizer;
|
|
7
|
+
futures/options/forex get dedicated sizers behind one dispatch point
|
|
8
|
+
(`asset_class_router.py`) so downstream consumers stay asset-class-agnostic.
|
|
9
|
+
|
|
10
|
+
## Base sizing — `position_sizing.py`
|
|
11
|
+
|
|
12
|
+
`build_dynamic_position_sizing(...)` → `PositionSizingDecision`.
|
|
13
|
+
`classify_volatility_regime()` buckets `rolling_volatility_20d` into
|
|
14
|
+
low/normal/high, driving `volatility_multiplier` (shrinks in high-vol,
|
|
15
|
+
expands in low-vol toward a target daily volatility). Full chain:
|
|
16
|
+
|
|
17
|
+
`volatility_multiplier × confidence_multiplier(0.5+0.5*confidence) ×
|
|
18
|
+
topology_multiplier × rank_multiplier × rl_multiplier`, then
|
|
19
|
+
`cost_sizing_multiplier()` on top. Every optional multiplier below is a
|
|
20
|
+
strict `1.0` no-op unless its own config flag is on and its model output is
|
|
21
|
+
present.
|
|
22
|
+
|
|
23
|
+
| Multiplier | Function | Config flag (default) | Bounds / shape |
|
|
24
|
+
|---|---|---|---|
|
|
25
|
+
| Volatility source swap | `_resolve_effective_volatility()` | `phase_v2.dynamic_risk.use_predicted_volatility` (`false`) | swaps `rolling_volatility_20d` → predicted |
|
|
26
|
+
| Topology | `topology_sizing_multiplier()` | `phase_v2.dynamic_risk.topology_sizing_enabled` (`true`) | `[min,1.0]`, shrink-only |
|
|
27
|
+
| Rank | `rank_sizing_multiplier()` | `phase_v2.dynamic_risk.rank_sizing_enabled` (`false`) | `[0.75,1.25]`, can amplify |
|
|
28
|
+
| RL overlay | `rl_sizing.py::rl_sizing_multiplier()` | `phase_v2.dynamic_risk.rl_sizing_enabled` (`false`) | `[0.6,1.0]`, shrink-only |
|
|
29
|
+
| Cost | `cost_sizing_multiplier()` | `phase_v2.costs.cost_sizing_enabled` (`false`) | `<=1.0`, shrink-only |
|
|
30
|
+
|
|
31
|
+
### Predicted volatility
|
|
32
|
+
|
|
33
|
+
`build_dynamic_position_sizing(..., predicted_volatility=None,
|
|
34
|
+
use_predicted_volatility=False)` swaps the number driving
|
|
35
|
+
`volatility_regime`/`annualized_volatility`/`volatility_multiplier` from
|
|
36
|
+
trailing `rolling_volatility_20d` to the forward-looking `volatility` head of
|
|
37
|
+
the multi-task model (`train_multitask.py`/`AetherNetMultiTask`).
|
|
38
|
+
`_resolve_effective_volatility()` falls back to rolling whenever the flag is
|
|
39
|
+
off or the prediction is `None`. `PositionSizingDecision.volatility_source`
|
|
40
|
+
(`"rolling"`/`"predicted"`) records which was used. Fed from
|
|
41
|
+
`gating_payload["final_volatility"]` (`moe/gating.py::_weighted_blend()`,
|
|
42
|
+
also includes the sequence encoder when `phase_v2.gating_network.sequence_weight`
|
|
43
|
+
is on — see `moe/README.md`), not the raw multitask output directly. Wired
|
|
44
|
+
via `main.py::_build_dynamic_sizing_payload(..., predicted_volatility=...)`.
|
|
45
|
+
Sizing only, never routing — `analyzer/market_analyzer.py` never reads it.
|
|
46
|
+
|
|
47
|
+
### Topology multiplier (shrink-only)
|
|
48
|
+
|
|
49
|
+
`topology_sizing_multiplier(topology_source, topology_confidence,
|
|
50
|
+
topology_disagreement, min_topology_multiplier=0.5,
|
|
51
|
+
max_topology_multiplier=1.0)`. No-op unless `topology_source == "learned"`
|
|
52
|
+
(the `topology/learned_topology.py` overlay's own confidence-gated label);
|
|
53
|
+
otherwise `multiplier = min + (max-min) * confidence * (1-disagreement)`,
|
|
54
|
+
always `<= max`. Config: `phase_v2.dynamic_risk.topology_sizing_enabled`
|
|
55
|
+
(`true`), `min_topology_multiplier`/`max_topology_multiplier` (`0.5`/`1.0`)
|
|
56
|
+
— independent of `phase_v2.topology_learning.enabled` (gates the unrelated
|
|
57
|
+
dashboard/retrain consumers). Wired via
|
|
58
|
+
`main.py::_build_dynamic_sizing_payload(..., topology=...)`. Not wired into
|
|
59
|
+
`analyzer/market_analyzer.py`'s trade decision — changes size only.
|
|
60
|
+
|
|
61
|
+
### Rank multiplier (bounded, direction-preserving)
|
|
62
|
+
|
|
63
|
+
`rank_sizing_multiplier(rank_prediction, rank_sizing_enabled,
|
|
64
|
+
min_rank_multiplier=0.75, max_rank_multiplier=1.25)`. Source: `rank_20d`
|
|
65
|
+
head (predicted cross-sectional percentile rank, [0,1], `train.py::compute_rank_ic()`).
|
|
66
|
+
`multiplier = min + (max-min) * rank_prediction` — rank near `1.0` scales up,
|
|
67
|
+
near `0.0` scales down, `0.5` is a no-op; can amplify (unlike topology). No-op
|
|
68
|
+
if disabled or prediction is `None` (model unloaded, inference failed, or
|
|
69
|
+
universe below `train.py`'s `min_universe_size`). Config:
|
|
70
|
+
`phase_v2.dynamic_risk.rank_sizing_enabled` — **default `false`**: full-series
|
|
71
|
+
backtest is significant (sequence model, mean rank-IC 0.073, t-stat 4.40) but
|
|
72
|
+
the 28-window non-overlapping subsample isn't yet independently significant
|
|
73
|
+
(t-stat 1.20). `PositionSizingDecision.rank_multiplier`/`.rank_sizing_reason`.
|
|
74
|
+
Wired via `main.py::_build_dynamic_sizing_payload(..., predicted_rank_20d=...)`
|
|
75
|
+
— sequence model's head preferred, multitask model's as fallback.
|
|
76
|
+
|
|
77
|
+
### RL sizing overlay (Phase 4.12 — ships disabled)
|
|
78
|
+
|
|
79
|
+
`rl_sizing.py::rl_sizing_multiplier(model, state, rl_sizing_enabled,
|
|
80
|
+
min_rl_multiplier=0.6, max_rl_multiplier=1.0)` — an offline **contextual
|
|
81
|
+
bandit**, not online/off-policy RL (no exploration data exists;
|
|
82
|
+
`train_rl_sizing.py` docstring). `build_rl_sizing_state()` assembles
|
|
83
|
+
`RL_SIZING_STATE_KEYS` (rank confidence, predicted volatility, regime,
|
|
84
|
+
topology risk, liquidity) from `base_features`/`confidence`; returns `None`
|
|
85
|
+
(no-op) if any key is missing or the flag is off. `_softmax_argmax_index()`
|
|
86
|
+
is deterministic — argmax over the trained policy, never samples at runtime.
|
|
87
|
+
Trained by `train_rl_sizing.py`: softmax policy-gradient over
|
|
88
|
+
`ml/datasets/validation_dataset.csv`, reward = realized PnL net of fees
|
|
89
|
+
(asymmetrically weighted so oversizing is punished more than undersizing —
|
|
90
|
+
the V5.4.1 fix for an earlier reward that made the policy look honestly
|
|
91
|
+
negative). `aq train --rl-sizing-only` wires it like `_train_topology_only()`.
|
|
92
|
+
**Ships
|
|
93
|
+
disabled** pending a retrain demonstrating positive expected reward over
|
|
94
|
+
the constant-`1.0` baseline (see `development/Problems.md` #71 and the
|
|
95
|
+
V5.4.1 changelog entry).
|
|
96
|
+
`PositionSizingDecision.rl_multiplier`/`.rl_sizing_reason`. All chain
|
|
97
|
+
multipliers render in the webui's `AssetSizingTable.tsx` as a `label×value`
|
|
98
|
+
chip, muted at `1.0`.
|
|
99
|
+
|
|
100
|
+
### Cost-scaled sizing
|
|
101
|
+
|
|
102
|
+
`cost_sizing_multiplier()` shrinks (never grows) sized weight when expected
|
|
103
|
+
edge doesn't clear expected round-trip cost (`execution/cost_model.py`) —
|
|
104
|
+
same shrink-only contract as topology. Config:
|
|
105
|
+
`phase_v2.costs.cost_sizing_enabled` (default off).
|
|
106
|
+
|
|
107
|
+
## Multi-asset-class dispatch — `asset_class_router.py`
|
|
108
|
+
|
|
109
|
+
`route_position_sizing()` is the single dispatch point. Equity/crypto/bond →
|
|
110
|
+
`build_dynamic_position_sizing()` unchanged (bonds get better features via
|
|
111
|
+
`features/bond_features.py`, not a new sizing formula). `future`/`option` →
|
|
112
|
+
dedicated modules below, adapted onto the same `PositionSizingDecision` shape
|
|
113
|
+
so `portfolio/book_construction.py`, liquidity, analyzer, and
|
|
114
|
+
`main.py::_apply_signal()` stay asset-class-agnostic.
|
|
115
|
+
|
|
116
|
+
### Futures — `futures_risk.py`
|
|
117
|
+
|
|
118
|
+
`build_futures_position_sizing()` — margin-utilization-targeted, not
|
|
119
|
+
volatility-of-notional. Max contracts at `max_margin_utilization` (hard
|
|
120
|
+
ceiling), scales toward `target_margin_utilization` by confidence, floors to
|
|
121
|
+
integer `contract_count`. Specs via `load_futures_contract_specs()` from
|
|
122
|
+
`data/reference/futures_contract_specs.json` (static fallback, always
|
|
123
|
+
available). **Live margin source (opt-in, Problems.md #67)**:
|
|
124
|
+
`phase_v2.futures_risk.margin_source` (`"static"` default / `"live"`,
|
|
125
|
+
`resolve_futures_margin_source()`) attaches Lean's IB-calibrated
|
|
126
|
+
`BuyingPowerModel` per-security (`main.py::_add_asset()`, never a global
|
|
127
|
+
`SetBrokerageModel()`), queried via `main.py::_resolve_futures_contract_spec()`/
|
|
128
|
+
`build_live_contract_spec()`. "Live" = Lean's local calc, no network
|
|
129
|
+
round-trip; falls back to static file on any failure. Code-complete,
|
|
130
|
+
Lean-API-unverified. `rollover_due()` is a diagnostic date check only —
|
|
131
|
+
rollover itself is Lean's native `add_future()` + `SetFilter()`. Config:
|
|
132
|
+
`phase_v2.futures_risk.{enabled,target_margin_utilization,
|
|
133
|
+
max_margin_utilization,margin_source}`, off by default.
|
|
134
|
+
|
|
135
|
+
### Options — `portfolio/options_strategy.py` (lives in `portfolio/`, needs the option chain)
|
|
136
|
+
|
|
137
|
+
Sizing for options is chain-aware, so the modules live in `portfolio/` —
|
|
138
|
+
see `portfolio/README.md` for the full options design (the 43-strategy
|
|
139
|
+
registry, held-position sizing, combo limit orders, assignment risk).
|
|
140
|
+
The sizing-relevant summary:
|
|
141
|
+
|
|
142
|
+
- **3 sizing paradigms**, dispatched by strategy shape: vega budget
|
|
143
|
+
(`build_options_position_sizing()` single-leg / `build_multi_leg_position_sizing()`
|
|
144
|
+
for bounded-risk multi-leg shapes, sized by `abs(net_vega)`); margin
|
|
145
|
+
(`portfolio/options_margin_sizing.py` — naked/uncovered-leg/
|
|
146
|
+
bounded-max-loss margin, hard-gated to `runtime_mode == "backtest"`);
|
|
147
|
+
equity-ratio (`build_covered_protective_position_sizing()` — option
|
|
148
|
+
legs sized off the held equity quantity).
|
|
149
|
+
- **Volatility-view gating**: `classify_volatility_view()` classifies
|
|
150
|
+
`predicted_volatility` against chain ATM IV into
|
|
151
|
+
long_vol/short_vol/neutral — gates straddle/strangle/iron-condor/
|
|
152
|
+
butterfly selection only.
|
|
153
|
+
- **Strategy selection**: `phase_v2.options_risk.enabled_strategy_names`
|
|
154
|
+
is an ordered priority list (first match that sizes wins) +
|
|
155
|
+
`risk_tier_preference` (defined-risk-first by default) + optional
|
|
156
|
+
per-asset override. A trained strategy-selector model can rerank the
|
|
157
|
+
list (`route_multi_leg_option_sizing(..., strategy_selector_scores=...)`,
|
|
158
|
+
dormant by default).
|
|
159
|
+
- Config: `phase_v2.options_risk.{enabled,target_delta_at_full_confidence,
|
|
160
|
+
max_vega_budget_pct_of_equity,risk_free_rate,spread_strategy,
|
|
161
|
+
multi_leg_strategies_enabled,max_positions_per_underlying,
|
|
162
|
+
rotation_cooldown_bars,arbitrage_detector}`, off by default.
|
|
163
|
+
- **Verification status**: every combo-order path is code-complete but
|
|
164
|
+
Lean-API-unverified — no real Lean backtest has placed a combo option
|
|
165
|
+
order.
|
|
166
|
+
|
|
167
|
+
### Forex — `forex_risk.py`
|
|
168
|
+
|
|
169
|
+
`build_forex_position_sizing()` mirrors `futures_risk.py`'s soft-target/
|
|
170
|
+
hard-ceiling shape, leverage-utilization-targeted (margin = `lot_size * price
|
|
171
|
+
* margin_pct`). Specs via `load_forex_pair_specs()` from
|
|
172
|
+
`data/reference/forex_pair_specs.json` (15 pairs — EURUSD, GBPUSD, USDJPY,
|
|
173
|
+
AUDUSD, USDCAD, USDCHF, NZDUSD, EURGBP, EURJPY, GBPJPY, EURCHF, EURAUD,
|
|
174
|
+
AUDJPY, CADJPY, GBPCAD — via `aq fetch forex`, `data_pipeline/fetch.py`, in
|
|
175
|
+
`config.json`'s universe; see `development/asset_universe.md`).
|
|
176
|
+
`asset_class_router.py::_forex_decision_to_position_sizing()` adapts the
|
|
177
|
+
result; `resolve_asset_class_enabled()` takes `forex_risk_enabled`.
|
|
178
|
+
Quote-bar (bid/ask, not trade-bar) data: additive fallback in
|
|
179
|
+
`main.py::on_data()` consulting `slice.quote_bars` only for
|
|
180
|
+
`security_type == "forex"` with no `TradeBar` that bar. Config:
|
|
181
|
+
`phase_v2.forex_risk.enabled`, default `false` — nothing technically blocks
|
|
182
|
+
enabling (`aq config set phase_v2.forex_risk.enabled true`). Code-complete,
|
|
183
|
+
IB-unverified for live.
|
|
184
|
+
|
|
185
|
+
Individual-bond trading is infeasible under this Lean version (no
|
|
186
|
+
`SecurityType.Bond`) — reframed as bond-ETF duration/convexity in
|
|
187
|
+
`features/bond_features.py` (`portfolio/README.md`).
|
|
188
|
+
|
|
189
|
+
## Adding to / rotating an existing position
|
|
190
|
+
|
|
191
|
+
`risk_controls.py` (repo root) governs whether an already-open position may
|
|
192
|
+
be scaled toward a fresh target, instead of either fully blocking
|
|
193
|
+
(equity/crypto/bond) or restacking an absolute target as an incremental
|
|
194
|
+
order every bar (futures/options):
|
|
195
|
+
|
|
196
|
+
- `should_scale_position(current_weight, target_weight,
|
|
197
|
+
rebalance_threshold_weight=0.03)` — equity/crypto/bond churn guard: resubmit
|
|
198
|
+
`SetHoldings()` only when target moved ≥ threshold from current weight.
|
|
199
|
+
- `compute_incremental_order_quantity(target_quantity, current_quantity)` —
|
|
200
|
+
signed delta a `MarketOrder`/`self.Buy` submits to converge a
|
|
201
|
+
discrete-contract instrument (futures/options/spreads) toward its fresh
|
|
202
|
+
absolute target (churn guard is "integer delta rounds to nonzero").
|
|
203
|
+
|
|
204
|
+
Gated by `phase_v2.functionality.position_scaling.{enabled,rotate_on_drift}`,
|
|
205
|
+
both off by default (`enabled` = may top up an open matching position;
|
|
206
|
+
`rotate_on_drift` = a drifted option contract/spread is rotated
|
|
207
|
+
`Liquidate()` → fresh entry same bar — independent of `enabled` because a
|
|
208
|
+
same-bar reenter carries real transient margin/vega exposure). Scale-down:
|
|
209
|
+
single-leg negative delta sells via `MarketOrder(contract_symbol, delta)`;
|
|
210
|
+
multi-leg via `self.Sell(strategy, abs(delta))`.
|
|
211
|
+
|
|
212
|
+
## Liquidating positions when an asset class is disabled
|
|
213
|
+
|
|
214
|
+
Flipping `phase_v2.futures_risk.enabled`/`phase_v2.options_risk.enabled` to
|
|
215
|
+
`False` mid-run zeroes new *sizing* but previously left an already-open
|
|
216
|
+
position untouched (equity/crypto/bond have no enable/disable flag — this
|
|
217
|
+
only applies to futures/options).
|
|
218
|
+
|
|
219
|
+
- `asset_class_router.py::resolve_asset_class_enabled(asset_class,
|
|
220
|
+
futures_risk_enabled, options_risk_enabled, forex_risk_enabled=True)` —
|
|
221
|
+
pure lookup: `True` for equity/crypto/bond/unrecognized always;
|
|
222
|
+
future/option/forex follow their flags.
|
|
223
|
+
- `asset_class_router.py::should_liquidate_disabled_asset_class_position(
|
|
224
|
+
asset_class_enabled, is_invested)` — pure predicate:
|
|
225
|
+
`(not asset_class_enabled) and is_invested`.
|
|
226
|
+
- `main.py::_liquidate_positions_for_disabled_asset_classes()` — per-bar
|
|
227
|
+
sweep called right after `_refresh_risk_state()`. Liquidates via real
|
|
228
|
+
`_liquidate_position()` or simulated
|
|
229
|
+
`experience/simulated_portfolio.py::SimulatedPortfolioState.exit_using_last_known_price()`;
|
|
230
|
+
logs via `self.Debug()` only.
|
|
231
|
+
|
|
232
|
+
## Kill switch and manual overrides
|
|
233
|
+
|
|
234
|
+
`kill_switch.py::evaluate_kill_switch(runtime_metrics, config)` →
|
|
235
|
+
`KillSwitchDecision` — automated production circuit breaker. Pure per-bar
|
|
236
|
+
function over tracked runtime state (rolling return history, drawdown
|
|
237
|
+
velocity, live rank-IC, consecutive losing sessions, slippage divergence,
|
|
238
|
+
model age, a reconciliation-breach flag); trips `main.py`'s existing sticky
|
|
239
|
+
trade lock (never a second one) if any of 7 independently config-gated
|
|
240
|
+
conditions fires. Every threshold defaults to a value it can never cross —
|
|
241
|
+
strict no-op until configured.
|
|
242
|
+
|
|
243
|
+
`manual_override.py`: `read_manual_trade_lock_override()`/
|
|
244
|
+
`write_manual_trade_lock_override()` (pre-existing trade-lock override);
|
|
245
|
+
`read_kill_switch_manual_override()`/`write_kill_switch_manual_override()`
|
|
246
|
+
(matching `kill_switch_manual_override` key, same read/write/cache shape).
|
|
247
|
+
Both driven by `aq kill-switch --arm|--disarm|--auto|--status|--history`.
|
|
248
|
+
|
|
249
|
+
See `development/architecture.md`'s Kill-Switch, Reconciliation, and
|
|
250
|
+
Auto-Rollback Contract, including `execution/reconciliation.py` and
|
|
251
|
+
`retraining/auto_rollback.py`.
|
|
252
|
+
|
|
253
|
+
## Backtest safety-gate bypass flags (`risk_controls.py`)
|
|
254
|
+
|
|
255
|
+
Three flags, all backtest-only (`runtime_mode == "backtest"`, else always
|
|
256
|
+
`False`) and off by default. The legacy flag still works standalone; the
|
|
257
|
+
two split flags both OR the legacy flag in for backward compatibility:
|
|
258
|
+
|
|
259
|
+
- `is_backtest_safety_bypass_active(runtime_mode, bypass_flag)` —
|
|
260
|
+
**legacy/combined**, `phase_v2.backtest.bypass_safety_gates` (covers both
|
|
261
|
+
behaviors below at once; kept for backward compatibility).
|
|
262
|
+
- `is_sticky_trade_lock_bypass_active(...)` — backtesting with
|
|
263
|
+
`phase_v2.backtest.bypass_sticky_trade_lock` OR the legacy flag. Controls
|
|
264
|
+
**only** `main.py`'s session-rollover clear of a
|
|
265
|
+
`total_drawdown_limit_breached`/`kill_switch_*` sticky lock — never the
|
|
266
|
+
regime drawdown branch. (Rationale: `kill_switch_*` locks are exempt from
|
|
267
|
+
the normal daily auto-clear — correct for live/paper where a human
|
|
268
|
+
decides — but an unattended backtest has no human to clear it.)
|
|
269
|
+
- `is_regime_drawdown_bypass_active(...)` — backtesting with
|
|
270
|
+
`phase_v2.backtest.bypass_regime_drawdown_gate` OR the legacy flag.
|
|
271
|
+
Controls **only** `main.py::_build_regime_payload()`'s
|
|
272
|
+
`risk_off_drawdown_threshold` override (set to infinity when active) —
|
|
273
|
+
never the sticky trade-lock clear. The two flags are deliberately
|
|
274
|
+
independent: unsticking a stuck kill-switch lock shouldn't force
|
|
275
|
+
disabling the unrelated regime-drawdown protection too.
|
risk/__init__.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Dynamic risk, leverage and liquidity controls for Aether Quant V2."""
|
|
2
|
+
|
|
3
|
+
from .asset_class_router import route_position_sizing
|
|
4
|
+
from .futures_risk import (
|
|
5
|
+
FuturesSizingDecision,
|
|
6
|
+
build_futures_position_sizing,
|
|
7
|
+
load_futures_contract_specs,
|
|
8
|
+
rollover_due,
|
|
9
|
+
)
|
|
10
|
+
from .manual_override import read_manual_trade_lock_override, write_manual_trade_lock_override
|
|
11
|
+
from .position_sizing import (
|
|
12
|
+
PositionSizingDecision,
|
|
13
|
+
build_dynamic_position_sizing,
|
|
14
|
+
classify_volatility_regime,
|
|
15
|
+
)
|
|
16
|
+
from .rl_sizing import (
|
|
17
|
+
RL_SIZING_STATE_KEYS,
|
|
18
|
+
build_rl_sizing_state,
|
|
19
|
+
load_rl_sizing_model,
|
|
20
|
+
rl_sizing_multiplier,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"FuturesSizingDecision",
|
|
25
|
+
"PositionSizingDecision",
|
|
26
|
+
"RL_SIZING_STATE_KEYS",
|
|
27
|
+
"build_dynamic_position_sizing",
|
|
28
|
+
"build_futures_position_sizing",
|
|
29
|
+
"build_rl_sizing_state",
|
|
30
|
+
"classify_volatility_regime",
|
|
31
|
+
"load_futures_contract_specs",
|
|
32
|
+
"load_rl_sizing_model",
|
|
33
|
+
"read_manual_trade_lock_override",
|
|
34
|
+
"rl_sizing_multiplier",
|
|
35
|
+
"rollover_due",
|
|
36
|
+
"route_position_sizing",
|
|
37
|
+
"write_manual_trade_lock_override",
|
|
38
|
+
]
|