aether-quant 0.4.0__tar.gz → 0.6.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 (254) hide show
  1. {aether_quant-0.4.0 → aether_quant-0.6.0}/.gitignore +0 -2
  2. {aether_quant-0.4.0 → aether_quant-0.6.0}/Dockerfile.retraining_worker +3 -0
  3. {aether_quant-0.4.0 → aether_quant-0.6.0}/PKG-INFO +1 -1
  4. {aether_quant-0.4.0 → aether_quant-0.6.0}/README.md +65 -38
  5. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/PKG-INFO +1 -1
  6. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/SOURCES.txt +10 -0
  7. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/scm_file_list.json +10 -0
  8. aether_quant-0.6.0/aether_quant.egg-info/scm_version.json +8 -0
  9. {aether_quant-0.4.0 → aether_quant-0.6.0}/analyzer/README.md +37 -0
  10. {aether_quant-0.4.0 → aether_quant-0.6.0}/analyzer/__init__.py +2 -0
  11. {aether_quant-0.4.0 → aether_quant-0.6.0}/analyzer/market_analyzer.py +81 -2
  12. {aether_quant-0.4.0 → aether_quant-0.6.0}/aq_cli.py +192 -1
  13. aether_quant-0.6.0/config.json +543 -0
  14. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/Changelog.md +262 -0
  15. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/Problems.md +103 -12
  16. aether_quant-0.6.0/development/backtest_equity_chart.png +0 -0
  17. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/v2_architecture.md +20 -0
  18. aether_quant-0.6.0/lean.json +649 -0
  19. {aether_quant-0.4.0 → aether_quant-0.6.0}/main.py +70 -2
  20. aether_quant-0.6.0/ml/gating_feature_schema.json +30 -0
  21. aether_quant-0.6.0/ml/gating_model.json +570 -0
  22. aether_quant-0.6.0/ml/gating_training_metrics.json +190 -0
  23. aether_quant-0.6.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json +30 -0
  24. aether_quant-0.6.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json +570 -0
  25. aether_quant-0.6.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json +190 -0
  26. aether_quant-0.6.0/moe/README.md +61 -0
  27. {aether_quant-0.4.0 → aether_quant-0.6.0}/moe/__init__.py +4 -0
  28. {aether_quant-0.4.0 → aether_quant-0.6.0}/moe/gating.py +101 -36
  29. {aether_quant-0.4.0 → aether_quant-0.6.0}/monitoring/README.md +1 -0
  30. {aether_quant-0.4.0 → aether_quant-0.6.0}/monitoring/neural_network_state.py +41 -22
  31. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/__init__.py +4 -0
  32. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/artifacts.py +23 -6
  33. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/orchestrator.py +54 -0
  34. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/worker.py +6 -0
  35. aether_quant-0.6.0/risk/README.md +50 -0
  36. {aether_quant-0.4.0 → aether_quant-0.6.0}/risk/position_sizing.py +52 -1
  37. {aether_quant-0.4.0 → aether_quant-0.6.0}/risk_controls.py +14 -0
  38. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_aq_cli.py +284 -0
  39. aether_quant-0.6.0/tests/test_gating_network.py +235 -0
  40. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_market_analyzer.py +118 -1
  41. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_neural_network_state.py +34 -5
  42. aether_quant-0.6.0/tests/test_position_sizing.py +123 -0
  43. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_retraining_artifacts.py +66 -1
  44. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_retraining_orchestrator.py +49 -0
  45. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_retraining_worker.py +20 -12
  46. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_risk_controls.py +19 -0
  47. aether_quant-0.6.0/tests/test_train_gating.py +179 -0
  48. {aether_quant-0.4.0 → aether_quant-0.6.0}/topology/README.md +7 -0
  49. aether_quant-0.6.0/train_gating.py +436 -0
  50. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/README.md +13 -0
  51. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/neuralnet/NeuralNetworkScene3D.tsx +5 -3
  52. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/types/state.ts +2 -2
  53. aether_quant-0.4.0/aether_quant.egg-info/scm_version.json +0 -8
  54. aether_quant-0.4.0/development/backtest_equity_chart.png +0 -0
  55. aether_quant-0.4.0/moe/README.md +0 -19
  56. aether_quant-0.4.0/risk/README.md +0 -19
  57. aether_quant-0.4.0/tests/test_gating_network.py +0 -97
  58. aether_quant-0.4.0/tests/test_position_sizing.py +0 -55
  59. {aether_quant-0.4.0 → aether_quant-0.6.0}/.dockerignore +0 -0
  60. {aether_quant-0.4.0 → aether_quant-0.6.0}/.env.compose.example +0 -0
  61. {aether_quant-0.4.0 → aether_quant-0.6.0}/.env.live.example +0 -0
  62. {aether_quant-0.4.0 → aether_quant-0.6.0}/.gitattributes +0 -0
  63. {aether_quant-0.4.0 → aether_quant-0.6.0}/.github/workflows/ci.yml +0 -0
  64. {aether_quant-0.4.0 → aether_quant-0.6.0}/.github/workflows/release.yml +0 -0
  65. {aether_quant-0.4.0 → aether_quant-0.6.0}/Dockerfile +0 -0
  66. {aether_quant-0.4.0 → aether_quant-0.6.0}/Dockerfile.workers +0 -0
  67. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/dependency_links.txt +0 -0
  68. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/entry_points.txt +0 -0
  69. {aether_quant-0.4.0 → aether_quant-0.6.0}/aether_quant.egg-info/top_level.txt +0 -0
  70. {aether_quant-0.4.0 → aether_quant-0.6.0}/backtests/.gitkeep +0 -0
  71. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/estimize/consensus/aapl/.keep +0 -0
  72. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/estimize/estimate/.keep +0 -0
  73. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/estimize/release/.keep +0 -0
  74. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/sec/aapl/.keep +0 -0
  75. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/trading-economics/.keep +0 -0
  76. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/trading-economics/calendar/.keep +0 -0
  77. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/trading-economics/earnings/.keep +0 -0
  78. {aether_quant-0.4.0 → aether_quant-0.6.0}/data/alternative/trading-economics/indicator/.keep +0 -0
  79. {aether_quant-0.4.0 → aether_quant-0.6.0}/data_pipeline/README.md +0 -0
  80. {aether_quant-0.4.0 → aether_quant-0.6.0}/data_pipeline/__init__.py +0 -0
  81. {aether_quant-0.4.0 → aether_quant-0.6.0}/data_pipeline/fetch.py +0 -0
  82. {aether_quant-0.4.0 → aether_quant-0.6.0}/data_pipeline/v2_manifest.py +0 -0
  83. {aether_quant-0.4.0 → aether_quant-0.6.0}/data_pipeline/yfinance_backfill.py +0 -0
  84. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/README.md +0 -0
  85. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/infrastructure.md +0 -0
  86. {aether_quant-0.4.0 → aether_quant-0.6.0}/development/logo.png +0 -0
  87. {aether_quant-0.4.0 → aether_quant-0.6.0}/docker-compose.yml +0 -0
  88. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/README.md +0 -0
  89. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/__init__.py +0 -0
  90. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/config_cache.py +0 -0
  91. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/live_credentials.py +0 -0
  92. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/live_credentials_io.py +0 -0
  93. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/order_gate.py +0 -0
  94. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/paper_readiness.py +0 -0
  95. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/paper_readiness_io.py +0 -0
  96. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/paper_readiness_report.py +0 -0
  97. {aether_quant-0.4.0 → aether_quant-0.6.0}/execution/runtime_config_io.py +0 -0
  98. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/README.md +0 -0
  99. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/__init__.py +0 -0
  100. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/observation_metrics.py +0 -0
  101. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/postgres_worker.py +0 -0
  102. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/redis_queue.py +0 -0
  103. {aether_quant-0.4.0 → aether_quant-0.6.0}/experience/simulated_portfolio.py +0 -0
  104. {aether_quant-0.4.0 → aether_quant-0.6.0}/experts/README.md +0 -0
  105. {aether_quant-0.4.0 → aether_quant-0.6.0}/experts/__init__.py +0 -0
  106. {aether_quant-0.4.0 → aether_quant-0.6.0}/experts/expert_datasets.py +0 -0
  107. {aether_quant-0.4.0 → aether_quant-0.6.0}/generate_backtest_report.py +0 -0
  108. {aether_quant-0.4.0 → aether_quant-0.6.0}/inference/README.md +0 -0
  109. {aether_quant-0.4.0 → aether_quant-0.6.0}/inference/__init__.py +0 -0
  110. {aether_quant-0.4.0 → aether_quant-0.6.0}/inference/exported_model.py +0 -0
  111. {aether_quant-0.4.0 → aether_quant-0.6.0}/liquidity/README.md +0 -0
  112. {aether_quant-0.4.0 → aether_quant-0.6.0}/liquidity/__init__.py +0 -0
  113. {aether_quant-0.4.0 → aether_quant-0.6.0}/liquidity/market_liquidity.py +0 -0
  114. {aether_quant-0.4.0 → aether_quant-0.6.0}/ml/README.md +0 -0
  115. {aether_quant-0.4.0 → aether_quant-0.6.0}/monitoring/__init__.py +0 -0
  116. {aether_quant-0.4.0 → aether_quant-0.6.0}/monitoring/api_server.py +0 -0
  117. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/README.md +0 -0
  118. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/__init__.py +0 -0
  119. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/postgres_telegram.py +0 -0
  120. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/telegram_alerts.py +0 -0
  121. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/telegram_client.py +0 -0
  122. {aether_quant-0.4.0 → aether_quant-0.6.0}/notifications/telegram_worker.py +0 -0
  123. {aether_quant-0.4.0 → aether_quant-0.6.0}/performance/README.md +0 -0
  124. {aether_quant-0.4.0 → aether_quant-0.6.0}/performance/__init__.py +0 -0
  125. {aether_quant-0.4.0 → aether_quant-0.6.0}/performance/postgres_triggers.py +0 -0
  126. {aether_quant-0.4.0 → aether_quant-0.6.0}/performance/trigger_worker.py +0 -0
  127. {aether_quant-0.4.0 → aether_quant-0.6.0}/performance/triggers.py +0 -0
  128. {aether_quant-0.4.0 → aether_quant-0.6.0}/pyproject.toml +0 -0
  129. {aether_quant-0.4.0 → aether_quant-0.6.0}/regime/README.md +0 -0
  130. {aether_quant-0.4.0 → aether_quant-0.6.0}/regime/__init__.py +0 -0
  131. {aether_quant-0.4.0 → aether_quant-0.6.0}/regime/market_regime.py +0 -0
  132. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/README.md +0 -0
  133. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/requirements-dev.txt +0 -0
  134. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/requirements-retraining-worker.txt +0 -0
  135. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/requirements-runtime.txt +0 -0
  136. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/requirements-workers.txt +0 -0
  137. {aether_quant-0.4.0 → aether_quant-0.6.0}/requirements/requirements.txt +0 -0
  138. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/README.md +0 -0
  139. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/backtest_gate.py +0 -0
  140. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/lean_backtest.py +0 -0
  141. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/planning.py +0 -0
  142. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/postgres_registry.py +0 -0
  143. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/status_export.py +0 -0
  144. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/validation_gate.py +0 -0
  145. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/vault_client.py +0 -0
  146. {aether_quant-0.4.0 → aether_quant-0.6.0}/retraining/vault_commands.py +0 -0
  147. {aether_quant-0.4.0 → aether_quant-0.6.0}/risk/__init__.py +0 -0
  148. {aether_quant-0.4.0 → aether_quant-0.6.0}/risk/manual_override.py +0 -0
  149. {aether_quant-0.4.0 → aether_quant-0.6.0}/setup.cfg +0 -0
  150. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/README.md +0 -0
  151. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_backtest_gate.py +0 -0
  152. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_config_cache.py +0 -0
  153. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_experience_queue.py +0 -0
  154. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_expert_datasets.py +0 -0
  155. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_expert_models.py +0 -0
  156. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_exported_model.py +0 -0
  157. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_fetch.py +0 -0
  158. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_generate_backtest_report.py +0 -0
  159. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_lean_backtest.py +0 -0
  160. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_lean_backtest_ml_coverage.py +0 -0
  161. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_learned_topology.py +0 -0
  162. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_live_credentials.py +0 -0
  163. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_live_credentials_io.py +0 -0
  164. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_manual_override.py +0 -0
  165. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_market_liquidity.py +0 -0
  166. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_market_regime.py +0 -0
  167. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_market_topology.py +0 -0
  168. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_observation_metrics.py +0 -0
  169. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_order_gate.py +0 -0
  170. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_paper_readiness.py +0 -0
  171. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_paper_readiness_io.py +0 -0
  172. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_paper_readiness_report.py +0 -0
  173. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_postgres_telegram.py +0 -0
  174. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_postgres_triggers.py +0 -0
  175. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_postgres_worker.py +0 -0
  176. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_retraining_planning.py +0 -0
  177. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_retraining_postgres_registry.py +0 -0
  178. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_runtime_config_io.py +0 -0
  179. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_simulated_portfolio.py +0 -0
  180. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_status_export.py +0 -0
  181. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_telegram_alerts.py +0 -0
  182. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_telegram_client.py +0 -0
  183. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_telegram_worker.py +0 -0
  184. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_train_pipeline.py +0 -0
  185. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_train_topology.py +0 -0
  186. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_trigger_worker.py +0 -0
  187. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_triggers.py +0 -0
  188. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_v2_pipeline_manifest.py +0 -0
  189. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_validation_gate.py +0 -0
  190. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_vault_client.py +0 -0
  191. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_vault_commands.py +0 -0
  192. {aether_quant-0.4.0 → aether_quant-0.6.0}/tests/test_yfinance_backfill.py +0 -0
  193. {aether_quant-0.4.0 → aether_quant-0.6.0}/topology/__init__.py +0 -0
  194. {aether_quant-0.4.0 → aether_quant-0.6.0}/topology/learned_topology.py +0 -0
  195. {aether_quant-0.4.0 → aether_quant-0.6.0}/topology/market_topology.py +0 -0
  196. {aether_quant-0.4.0 → aether_quant-0.6.0}/train.py +0 -0
  197. {aether_quant-0.4.0 → aether_quant-0.6.0}/train_topology.py +0 -0
  198. {aether_quant-0.4.0 → aether_quant-0.6.0}/visualization/README.md +0 -0
  199. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/.gitignore +0 -0
  200. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/.oxlintrc.json +0 -0
  201. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/index.html +0 -0
  202. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/overview_v2_12.png +0 -0
  203. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/package-lock.json +0 -0
  204. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/package.json +0 -0
  205. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/public/favicon.svg +0 -0
  206. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/public/icons.svg +0 -0
  207. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/risk_v2_12.png +0 -0
  208. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/App.tsx +0 -0
  209. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/api/client.ts +0 -0
  210. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/api/hooks.ts +0 -0
  211. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/heatmap/AssetHeatmap.tsx +0 -0
  212. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/layout/AppShell.tsx +0 -0
  213. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/layout/Panel.tsx +0 -0
  214. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/CountTable.tsx +0 -0
  215. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/MonitoringFeeds.tsx +0 -0
  216. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/ObservationPanel.tsx +0 -0
  217. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/PaperReadinessPanel.tsx +0 -0
  218. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/PerformanceTriggersPanel.tsx +0 -0
  219. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/RawStateViewer.tsx +0 -0
  220. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/monitoring/RetrainingStatusPanel.tsx +0 -0
  221. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/neuralnet/NeuralNetworkStatsPanel.tsx +0 -0
  222. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/risk/AssetSizingTable.tsx +0 -0
  223. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/risk/LiquidityTable.tsx +0 -0
  224. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/risk/RiskBar.tsx +0 -0
  225. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/risk/RiskCore.tsx +0 -0
  226. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/risk/StrategyRiskCards.tsx +0 -0
  227. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/scene3d/Scene3D.tsx +0 -0
  228. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/scorecards/Scorecards.tsx +0 -0
  229. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/signals/Badge.tsx +0 -0
  230. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/signals/PositionsList.tsx +0 -0
  231. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/signals/SignalBoard.tsx +0 -0
  232. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/topology/ClusterList.tsx +0 -0
  233. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/topology/TopologyLearningPanel.tsx +0 -0
  234. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/topology/TopologyScene3D.tsx +0 -0
  235. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/AssetPerformancePanel.tsx +0 -0
  236. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/BacktestEquityPanel.tsx +0 -0
  237. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/DivergingBarChart.tsx +0 -0
  238. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/LineChart.tsx +0 -0
  239. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/MetricsSnapshotPanel.tsx +0 -0
  240. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/components/tracing/ObservationEquityPanel.tsx +0 -0
  241. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/index.css +0 -0
  242. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/lib/downsample.ts +0 -0
  243. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/lib/format.ts +0 -0
  244. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/main.tsx +0 -0
  245. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/pages/NeuralNetworkPage.tsx +0 -0
  246. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/pages/Overview.tsx +0 -0
  247. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/pages/RiskPage.tsx +0 -0
  248. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/pages/TopologyPage.tsx +0 -0
  249. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/pages/TracingPage.tsx +0 -0
  250. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/src/types/tracing.ts +0 -0
  251. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/tsconfig.app.json +0 -0
  252. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/tsconfig.json +0 -0
  253. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/tsconfig.node.json +0 -0
  254. {aether_quant-0.4.0 → aether_quant-0.6.0}/webui/vite.config.ts +0 -0
@@ -19,9 +19,7 @@ env/
19
19
  !.env.live.example
20
20
  ib_config.py
21
21
  *_config.local.py
22
- config.json
23
22
  config.local.json
24
- lean.json
25
23
  lean.local.json
26
24
 
27
25
  *.key
@@ -8,7 +8,10 @@ COPY performance/ ./performance/
8
8
  COPY regime/ ./regime/
9
9
  COPY experts/ ./experts/
10
10
  COPY topology/ ./topology/
11
+ COPY moe/ ./moe/
12
+ COPY inference/ ./inference/
11
13
  COPY retraining/ ./retraining/
12
14
  COPY train.py .
13
15
  COPY train_topology.py .
16
+ COPY train_gating.py .
14
17
  CMD ["python", "-m", "retraining.worker"]
@@ -1,5 +1,5 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-quant
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Aether Quant convenience CLI
5
5
  Requires-Python: >=3.10
@@ -10,11 +10,23 @@
10
10
 
11
11
  <p align="center">
12
12
  <img src="https://img.shields.io/badge/python-3.10%2B-FF8C00?style=flat-square&labelColor=1A1A1A&logo=python&logoColor=white" alt="Python 3.10+">
13
- <!-- AQ:TEST_BADGE_START --><img src="https://img.shields.io/badge/tests-528%2F528%20passing-brightgreen?style=flat-square&labelColor=1A1A1A" alt="528 of 528 tests passing"><!-- AQ:TEST_BADGE_END -->
13
+ <!-- AQ:TEST_BADGE_START --><img src="https://img.shields.io/badge/tests-611%2F611%20passing-brightgreen?style=flat-square&labelColor=1A1A1A" alt="611 of 611 tests passing"><!-- AQ:TEST_BADGE_END -->
14
14
  <img src="https://img.shields.io/pypi/v/aether-quant?style=flat-square&labelColor=1A1A1A&color=FF8C00" alt="PyPI version">
15
15
  <img src="https://img.shields.io/badge/docker-ghcr.io%2Faether--quant-2496ED?style=flat-square&labelColor=1A1A1A&logo=docker&logoColor=white" alt="Docker image on GHCR">
16
16
  </p>
17
17
 
18
+ <p align="center">
19
+ <img src="https://img.shields.io/badge/PyTorch-4B5563?style=flat-square&labelColor=1A1A1A&logo=pytorch&logoColor=white" alt="PyTorch">
20
+ <img src="https://img.shields.io/badge/scikit--learn-4B5563?style=flat-square&labelColor=1A1A1A&logo=scikitlearn&logoColor=white" alt="scikit-learn">
21
+ <img src="https://img.shields.io/badge/QuantConnect%20Lean-4B5563?style=flat-square&labelColor=1A1A1A" alt="QuantConnect Lean">
22
+ <img src="https://img.shields.io/badge/FastAPI-4B5563?style=flat-square&labelColor=1A1A1A&logo=fastapi&logoColor=white" alt="FastAPI">
23
+ <img src="https://img.shields.io/badge/React-4B5563?style=flat-square&labelColor=1A1A1A&logo=react&logoColor=white" alt="React">
24
+ <img src="https://img.shields.io/badge/TypeScript-4B5563?style=flat-square&labelColor=1A1A1A&logo=typescript&logoColor=white" alt="TypeScript">
25
+ <img src="https://img.shields.io/badge/Redis-4B5563?style=flat-square&labelColor=1A1A1A&logo=redis&logoColor=white" alt="Redis">
26
+ <img src="https://img.shields.io/badge/PostgreSQL-4B5563?style=flat-square&labelColor=1A1A1A&logo=postgresql&logoColor=white" alt="PostgreSQL">
27
+ <img src="https://img.shields.io/badge/GitHub%20Actions-4B5563?style=flat-square&labelColor=1A1A1A&logo=githubactions&logoColor=white" alt="GitHub Actions">
28
+ </p>
29
+
18
30
  Aether Quant is not a single static strategy — it's a **dynamic system**: a
19
31
  Mixture-of-Experts ensemble (bullish/bearish/sideways/volatility specialists)
20
32
  routed by a learned gating network, a market-regime detector, a 3D market
@@ -391,14 +403,14 @@ and how it's wired in — this table is the index.
391
403
 
392
404
  | Metric | Value |
393
405
  |---|---|
394
- | Backtest window | 2014-12-01 to 2018-08-15 |
395
- | Sharpe Ratio | -0.765 |
396
- | Net Profit | -7.955% |
397
- | Compounding Annual Return | -2.212% |
398
- | Drawdown | 12.100% |
399
- | Total Orders | 15 |
400
- | Win Rate | 0% |
401
- | Last updated | 2026-07-05 16:59 UTC (auto-generated by `aq backtest`) |
406
+ | Backtest window | 2018-04-01 to 2021-04-02 |
407
+ | Sharpe Ratio | -0.71 |
408
+ | Net Profit | -8.069% |
409
+ | Compounding Annual Return | -2.761% |
410
+ | Drawdown | 12.500% |
411
+ | Total Orders | 27 |
412
+ | Win Rate | 17% |
413
+ | Last updated | 2026-07-06 17:20 UTC (auto-generated by `aq backtest`) |
402
414
  <!-- AQ:BACKTEST_END -->
403
415
 
404
416
  <details>
@@ -407,33 +419,33 @@ and how it's wired in — this table is the index.
407
419
  <!-- AQ:BACKTEST_FULL_STATS_START -->
408
420
  | Metric | Value |
409
421
  |---|---|
410
- | Total Orders | 15 |
411
- | Average Win | 0% |
412
- | Average Loss | -1.64% |
413
- | Compounding Annual Return | -2.212% |
414
- | Drawdown | 12.100% |
415
- | Expectancy | -1 |
416
- | Start Equity | 100000 |
417
- | End Equity | 92045.02 |
418
- | Net Profit | -7.955% |
419
- | Sharpe Ratio | -0.765 |
420
- | Sortino Ratio | -0.531 |
421
- | Probabilistic Sharpe Ratio | 0.052% |
422
- | Loss Rate | 100% |
423
- | Win Rate | 0% |
424
- | Profit-Loss Ratio | 0 |
425
- | Alpha | -0.043 |
426
- | Beta | 0.224 |
427
- | Annual Standard Deviation | 0.036 |
428
- | Annual Variance | 0.001 |
429
- | Information Ratio | -1.103 |
430
- | Tracking Error | 0.087 |
431
- | Treynor Ratio | -0.123 |
432
- | Total Fees | $14.51 |
433
- | Estimated Strategy Capacity | $310000000.00 |
422
+ | Total Orders | 27 |
423
+ | Average Win | 0.18% |
424
+ | Average Loss | -1.70% |
425
+ | Compounding Annual Return | -2.761% |
426
+ | Drawdown | 12.500% |
427
+ | Expectancy | -0.816 |
428
+ | Start Equity | 100000.00 |
429
+ | End Equity | 91930.60 |
430
+ | Net Profit | -8.069% |
431
+ | Sharpe Ratio | -0.71 |
432
+ | Sortino Ratio | -0.471 |
433
+ | Probabilistic Sharpe Ratio | 0.171% |
434
+ | Loss Rate | 83% |
435
+ | Win Rate | 17% |
436
+ | Profit-Loss Ratio | 0.11 |
437
+ | Alpha | -0.058 |
438
+ | Beta | 0.195 |
439
+ | Annual Standard Deviation | 0.051 |
440
+ | Annual Variance | 0.003 |
441
+ | Information Ratio | -0.972 |
442
+ | Tracking Error | 0.155 |
443
+ | Treynor Ratio | -0.184 |
444
+ | Total Fees | $12.00 |
445
+ | Estimated Strategy Capacity | $210000000.00 |
434
446
  | Lowest Capacity Asset | IBM R735QTJ8XC9X |
435
- | Portfolio Turnover | 0.06% |
436
- | Drawdown Recovery | 57 |
447
+ | Portfolio Turnover | 0.07% |
448
+ | Drawdown Recovery | 503 |
437
449
  <!-- AQ:BACKTEST_FULL_STATS_END -->
438
450
 
439
451
  </details>
@@ -463,6 +475,17 @@ experience-worker performance-trigger-worker retraining-worker`) with the
463
475
  backtest run inside that network, plus an actual trigger condition being
464
476
  met during the run.
465
477
 
478
+ **If `phase_v2.backtest.bypass_safety_gates` is `true`:** this backtest
479
+ also does not represent live/paper-deployable behavior. That flag (default
480
+ `false`) disables the sticky total-drawdown lock and the regime detector's
481
+ drawdown-driven `risk_off` override — both real, designed safety behavior
482
+ in live/paper mode — purely to generate enough trade volume for
483
+ statistically meaningful backtest metrics (see `development/Problems.md`
484
+ #18). A backtest run with this flag set shows the underlying model's
485
+ signal quality across more market conditions, not what this system would
486
+ have actually done if deployed — in live/paper mode, both gates would have
487
+ genuinely halted trading exactly as designed.
488
+
466
489
  ## Test Suite
467
490
 
468
491
  507 tests, one file per source module, run via:
@@ -495,7 +518,7 @@ pip install -e .
495
518
  Either way, `aq --help` gives the full command list:
496
519
 
497
520
  ```text
498
- aq train [--dataset-only|--init-only|--experts-only]
521
+ aq train [--dataset-only|--init-only|--experts-only|--gating-only]
499
522
  aq test
500
523
  aq backtest
501
524
  aq report <backtest-folder> <result-id>
@@ -503,6 +526,8 @@ aq api
503
526
  aq webui
504
527
  aq docker up [--lean|--all]
505
528
  aq docker build
529
+ aq config [get <dotted.key>|set <dotted.key> <value>|keys [<dotted.prefix>]]
530
+ aq lean [get <dotted.key>|set <dotted.key> <value>|keys [<dotted.prefix>]]
506
531
  aq retrain <plan|train|validate|backtest|commit|promote|rollback|status> [...]
507
532
  aq trade-lock --on|--off|--auto|--status
508
533
  aq fetch <crypto|stock> --ticker <TICKER> --start <YYYY-MM-DD> --end <YYYY-MM-DD> [--apply]
@@ -512,7 +537,7 @@ aq status
512
537
  Every command except `aq trade-lock` and `aq fetch` is a thin `subprocess`
513
538
  wrapper around a command already documented elsewhere in this README:
514
539
 
515
- - **`aq train`** — runs `train.py`: builds the dataset and trains the baseline + expert models.
540
+ - **`aq train`** — runs `train.py`: builds the dataset and trains the baseline + expert models. `--gating-only` trains just the learned gating blend (`train_gating.py`) and installs it straight into active `ml/`, mirroring what `--experts-only` already does for the expert models — see `moe/README.md`.
516
541
  - **`aq test`** — runs the pytest suite and refreshes this README's test badge.
517
542
  - **`aq backtest`** — runs `lean backtest .` and refreshes this README's [Backtest Results](#backtest-results) section.
518
543
  - **`aq report <backtest-folder> <result-id>`** — generates Lean's own HTML backtest report (trade blotter, standard Lean charts) at `backtests/<backtest-folder>/report.html`.
@@ -520,6 +545,8 @@ wrapper around a command already documented elsewhere in this README:
520
545
  - **`aq webui`** — starts the webui dev server (`npm run dev`).
521
546
  - **`aq docker up [--lean|--all]`** — starts local infrastructure (default: Redis + PostgreSQL only).
522
547
  - **`aq docker build`** — rebuilds the `aether-quant` app image.
548
+ - **`aq config`** — reads or edits `config.json` directly, no manual file editing needed. Bare `aq config` pretty-prints the whole file; `aq config keys [<dotted.prefix>]` lists every leaf key path (handy for finding the right key in a deeply nested file); `aq config get <dotted.key>` prints one value (scalar, or a whole nested section as JSON); `aq config set <dotted.key> <value>` writes it — the value is parsed as JSON first (so `true`/`123`/`0.5`/`["a","b"]` become their real types automatically), falling back to a plain string otherwise. Every `set` backs up the previous file to `config.json.bak` first and prints old → new so a mistake is immediately visible; changing a value's type (e.g. bool → string) prints a warning but still writes it, since this command intentionally gives full access to every key, not just a safe subset.
549
+ - **`aq lean`** — the exact same `get`/`set`/`keys` tool as `aq config`, just pointed at `lean.json` (the QuantConnect Lean CLI's own config file — broker credentials, environments, data providers) instead. `aq lean set ib-trading-mode live`, `aq lean keys environments.live-paper`, etc.
523
550
  - **`aq retrain <stage>`** — dispatches to `python -m retraining.orchestrator <stage> ...` for a single manual pipeline stage.
524
551
  - **`aq trade-lock --on|--off|--auto|--status`** — manually overrides `main.py`'s sticky total-drawdown trade lock (see `development/v2_architecture.md`'s Manual Trade-Lock Override Contract). `--off` deliberately clears an otherwise-permanent lock; `--auto` returns to fully automatic behavior.
525
552
  - **`aq fetch <crypto|stock> --ticker <TICKER> --start <YYYY-MM-DD> --end <YYYY-MM-DD> [--apply]`** — fetches historical OHLCV from Yahoo Finance for a ticker that isn't in `config.json` yet, formats it into Lean's zip/CSV convention, and writes it to the right spot under `data/` (`data/crypto/coinbase/daily/<ticker>_trade.zip` or `data/equity/usa/daily/<ticker>.zip`). On `--apply`, it also appends a new asset block to `config.json`'s `phase1.universe.assets[]` — no manual editing needed. Dry run by default (no `--apply`): reports what would happen, writes nothing. Never runs `train.py` itself — once applied, run `python train.py --dataset-only` (then `python train.py` when ready) yourself to actually train on the new ticker. `crypto`/`stock` today; a `derivative` asset class is planned for V3.
@@ -695,5 +722,5 @@ architecture docs already identify:
695
722
  ---
696
723
 
697
724
  <div align="center">
698
- <sub>Built with Python · PyTorch · QuantConnect Lean · FastAPI · React · Redis · PostgreSQL · Docker</sub>
725
+ <sub>Aether Quant</sub>
699
726
  </div>
@@ -1,5 +1,5 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-quant
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Aether Quant convenience CLI
5
5
  Requires-Python: >=3.10
@@ -8,12 +8,15 @@ Dockerfile.retraining_worker
8
8
  Dockerfile.workers
9
9
  README.md
10
10
  aq_cli.py
11
+ config.json
11
12
  docker-compose.yml
12
13
  generate_backtest_report.py
14
+ lean.json
13
15
  main.py
14
16
  pyproject.toml
15
17
  risk_controls.py
16
18
  train.py
19
+ train_gating.py
17
20
  train_topology.py
18
21
  .github/workflows/ci.yml
19
22
  .github/workflows/release.yml
@@ -74,6 +77,12 @@ liquidity/README.md
74
77
  liquidity/__init__.py
75
78
  liquidity/market_liquidity.py
76
79
  ml/README.md
80
+ ml/gating_feature_schema.json
81
+ ml/gating_model.json
82
+ ml/gating_training_metrics.json
83
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json
84
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json
85
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json
77
86
  moe/README.md
78
87
  moe/__init__.py
79
88
  moe/gating.py
@@ -161,6 +170,7 @@ tests/test_status_export.py
161
170
  tests/test_telegram_alerts.py
162
171
  tests/test_telegram_client.py
163
172
  tests/test_telegram_worker.py
173
+ tests/test_train_gating.py
164
174
  tests/test_train_pipeline.py
165
175
  tests/test_train_topology.py
166
176
  tests/test_trigger_worker.py
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "files": [
3
3
  "README.md",
4
+ "train_gating.py",
4
5
  "Dockerfile.workers",
5
6
  "aq_cli.py",
6
7
  "Dockerfile",
@@ -8,8 +9,10 @@
8
9
  ".env.compose.example",
9
10
  "Dockerfile.retraining_worker",
10
11
  "train_topology.py",
12
+ "config.json",
11
13
  "train.py",
12
14
  ".env.live.example",
15
+ "lean.json",
13
16
  ".dockerignore",
14
17
  "generate_backtest_report.py",
15
18
  ".gitattributes",
@@ -211,6 +214,7 @@
211
214
  "tests/test_train_pipeline.py",
212
215
  "tests/test_experience_queue.py",
213
216
  "tests/test_vault_commands.py",
217
+ "tests/test_train_gating.py",
214
218
  "tests/test_fetch.py",
215
219
  "data/alternative/sec/aapl/.keep",
216
220
  "data/alternative/trading-economics/.keep",
@@ -227,6 +231,12 @@
227
231
  "requirements/requirements-workers.txt",
228
232
  "requirements/requirements-retraining-worker.txt",
229
233
  "ml/README.md",
234
+ "ml/gating_model.json",
235
+ "ml/gating_feature_schema.json",
236
+ "ml/gating_training_metrics.json",
237
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json",
238
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json",
239
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json",
230
240
  ".github/workflows/release.yml",
231
241
  ".github/workflows/ci.yml"
232
242
  ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.6.0",
3
+ "distance": 0,
4
+ "node": "g5cffff5750320cc223918c89314db15f96f5365b",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-07"
8
+ }
@@ -38,7 +38,44 @@ risk engine, and before action categorization / Lean order placement.
38
38
  explicitly ruled out of scope for that phase as a materially riskier
39
39
  change than what its safety rule ("confidence/uncertainty only, never a
40
40
  randomized decision") allows.
41
+ - **Follow-up:** the learned topology overlay's confidence/disagreement
42
+ scores now *do* reach a real trade — but through position sizing
43
+ (`risk/position_sizing.py::topology_sizing_multiplier()`), not this
44
+ module. That integration point was chosen specifically because sizing
45
+ is already a continuous, shrink-only multiplier applied after this
46
+ analyzer has categorized the action, so it changes only *how large* an
47
+ already-approved trade is, never *whether* one happens — preserving
48
+ exactly the "confidence/uncertainty only, never a randomized decision"
49
+ rule above. This module's own `trade`/`simulate`/`observe`/
50
+ `reduce_risk` categorization remains fully deterministic and
51
+ unaffected. See `risk/README.md` for details.
41
52
  - `main.py` only calls `_apply_signal` when `action == "trade"`. All five
42
53
  actions are written into the per-asset `signal_payload` for
43
54
  dashboard/Grafana visibility regardless of whether a real order is
44
55
  placed.
56
+ - **Real composite scoring (additive, config-gated).** Until now, every
57
+ priority tier here checked exactly one raw field in isolation against a
58
+ fixed threshold (confidence alone, `topology_risk` alone,
59
+ `liquidity_action` alone) — pure if/elif routing, no aggregation. New
60
+ `compute_signal_quality_score(confidence, regime_confidence, topology,
61
+ liquidity)` computes a real bounded `[0,1]` composite — a small,
62
+ hand-tuned weighted blend of raw model confidence (0.45), regime
63
+ confidence (0.20), topology peer-support (0.20, penalized when
64
+ `topology_risk` is `isolated`/`elevated`), and liquidity friction (0.15,
65
+ penalized by `participation_rate`) — mirroring `moe/gating.py`'s
66
+ `_quality_multiplier`/`_performance_score` style: real math over
67
+ already-available fields, not a trained model. `MarketAnalysisDecision`
68
+ gains `signal_quality_score`/`signal_quality_breakdown` fields that are
69
+ **always** computed and populated on every decision, regardless of any
70
+ flag — visible in `visualization/state.json` immediately for dashboard
71
+ use. It only ever changes routing when
72
+ `phase_v2.market_analyzer.use_composite_signal_score` is explicitly
73
+ `true` (default `false`) — in that case it replaces raw `confidence`
74
+ with the composite score in the `trade` gate (priority 7) and the
75
+ `simulate`-vs-`observe` split (priority 8) only. The hard safety-override
76
+ tiers (trade-lock, `risk_off` regime, elevated topology, liquidity
77
+ blocked — priorities 1-6) are **never** affected by this flag, regardless
78
+ of its value — they stay simple categorical gates on purpose, same
79
+ reasoning as the topology-elevated/isolated rules above. Default `false`
80
+ means output is byte-identical to pre-this-change behavior everywhere
81
+ the flag isn't explicitly turned on.
@@ -4,10 +4,12 @@ from .market_analyzer import (
4
4
  ACTIONS,
5
5
  MarketAnalysisDecision,
6
6
  build_market_analysis_decision,
7
+ compute_signal_quality_score,
7
8
  )
8
9
 
9
10
  __all__ = [
10
11
  "ACTIONS",
11
12
  "MarketAnalysisDecision",
12
13
  "build_market_analysis_decision",
14
+ "compute_signal_quality_score",
13
15
  ]
@@ -4,7 +4,7 @@ action."""
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- from dataclasses import asdict, dataclass
7
+ from dataclasses import asdict, dataclass, field
8
8
 
9
9
 
10
10
  ACTIONS = ("observe", "simulate", "trade", "reduce_risk", "retrain_candidate")
@@ -21,11 +21,57 @@ class MarketAnalysisDecision:
21
21
  topology_considered: bool
22
22
  liquidity_considered: bool
23
23
  reasons: list[str]
24
+ signal_quality_score: float = 0.0
25
+ signal_quality_breakdown: dict = field(default_factory=dict)
24
26
 
25
27
  def to_dict(self) -> dict:
26
28
  return asdict(self)
27
29
 
28
30
 
31
+ def _clamp01(value: float) -> float:
32
+ return max(0.0, min(1.0, value))
33
+
34
+
35
+ def compute_signal_quality_score(
36
+ confidence: float,
37
+ regime_confidence: float,
38
+ topology: dict,
39
+ liquidity: dict,
40
+ ) -> tuple[float, dict]:
41
+ """Bounded [0,1] composite of raw model confidence, regime confidence,
42
+ topology peer-support and liquidity friction. Always computed and
43
+ exposed (signal_quality_score/signal_quality_breakdown) for dashboard
44
+ visibility; only influences routing in build_market_analysis_decision()
45
+ when use_composite_signal_score=True - see analyzer/README.md. Small
46
+ hand-tuned weights, mirroring moe/gating.py's
47
+ _quality_multiplier/_performance_score style - not a trained model."""
48
+ confidence_component = _clamp01(float(confidence))
49
+ regime_component = _clamp01(float(regime_confidence))
50
+
51
+ correlation_strength = _clamp01(float(topology.get("correlation_strength", 0.0) or 0.0))
52
+ topology_penalty = {"isolated": 0.3, "elevated": 0.6}.get(str(topology.get("topology_risk", "unknown")), 1.0)
53
+ topology_component = correlation_strength * topology_penalty if topology else regime_component
54
+
55
+ participation_rate = _clamp01(float(liquidity.get("participation_rate", 0.0) or 0.0))
56
+ liquidity_component = _clamp01(1.0 - participation_rate) if liquidity else 1.0
57
+
58
+ weights = {"confidence": 0.45, "regime": 0.20, "topology": 0.20, "liquidity": 0.15}
59
+ score = (
60
+ weights["confidence"] * confidence_component
61
+ + weights["regime"] * regime_component
62
+ + weights["topology"] * topology_component
63
+ + weights["liquidity"] * liquidity_component
64
+ )
65
+ breakdown = {
66
+ "confidence_component": confidence_component,
67
+ "regime_component": regime_component,
68
+ "topology_component": topology_component,
69
+ "liquidity_component": liquidity_component,
70
+ "weights": dict(weights),
71
+ }
72
+ return _clamp01(score), breakdown
73
+
74
+
29
75
  def build_market_analysis_decision(
30
76
  signal_name: str,
31
77
  confidence: float,
@@ -41,6 +87,7 @@ def build_market_analysis_decision(
41
87
  min_confidence_to_trade: float = 0.12,
42
88
  retrain_min_regime_confidence: float = 0.20,
43
89
  low_regime_confidence_threshold: float = 0.35,
90
+ use_composite_signal_score: bool = False,
44
91
  ) -> MarketAnalysisDecision:
45
92
  reasons: list[str] = []
46
93
  decision_source = str(gating.get("decision_source", "unknown"))
@@ -57,6 +104,17 @@ def build_market_analysis_decision(
57
104
  else:
58
105
  reasons.append("topology_absent_v2_11_pending")
59
106
 
107
+ # Always computed and exposed for dashboard visibility, regardless of
108
+ # use_composite_signal_score - see compute_signal_quality_score()'s
109
+ # docstring and analyzer/README.md. trade_metric is what priorities 7/8
110
+ # actually gate on below: the composite score when the flag is on,
111
+ # otherwise raw confidence - byte-identical to pre-flag behavior when
112
+ # use_composite_signal_score=False (the default).
113
+ signal_quality_score, signal_quality_breakdown = compute_signal_quality_score(
114
+ confidence, regime_confidence, topology, liquidity
115
+ )
116
+ trade_metric = signal_quality_score if use_composite_signal_score else confidence
117
+
60
118
  # Priority 1: reduce_risk - portfolio-level risk lock always wins.
61
119
  if trade_lock_active:
62
120
  reasons.append(trade_lock_reason or "risk_lock_active")
@@ -70,6 +128,8 @@ def build_market_analysis_decision(
70
128
  topology_considered=topology_considered,
71
129
  liquidity_considered=liquidity_considered,
72
130
  reasons=reasons,
131
+ signal_quality_score=signal_quality_score,
132
+ signal_quality_breakdown=signal_quality_breakdown,
73
133
  )
74
134
 
75
135
  # Priority 2: reduce_risk - asset-level risk regime override even
@@ -86,6 +146,8 @@ def build_market_analysis_decision(
86
146
  topology_considered=topology_considered,
87
147
  liquidity_considered=liquidity_considered,
88
148
  reasons=reasons,
149
+ signal_quality_score=signal_quality_score,
150
+ signal_quality_breakdown=signal_quality_breakdown,
89
151
  )
90
152
 
91
153
  # Priority 3: reduce_risk - elevated cross-sectional volatility pressure
@@ -106,6 +168,8 @@ def build_market_analysis_decision(
106
168
  topology_considered=topology_considered,
107
169
  liquidity_considered=liquidity_considered,
108
170
  reasons=reasons,
171
+ signal_quality_score=signal_quality_score,
172
+ signal_quality_breakdown=signal_quality_breakdown,
109
173
  )
110
174
 
111
175
  # Priority 4: retrain_candidate - zero experts contributing AND the
@@ -125,6 +189,8 @@ def build_market_analysis_decision(
125
189
  topology_considered=topology_considered,
126
190
  liquidity_considered=liquidity_considered,
127
191
  reasons=reasons,
192
+ signal_quality_score=signal_quality_score,
193
+ signal_quality_breakdown=signal_quality_breakdown,
128
194
  )
129
195
 
130
196
  # Priority 5: simulate - liquidity blocked (zero volume or DDV below
@@ -142,6 +208,8 @@ def build_market_analysis_decision(
142
208
  topology_considered=topology_considered,
143
209
  liquidity_considered=liquidity_considered,
144
210
  reasons=reasons,
211
+ signal_quality_score=signal_quality_score,
212
+ signal_quality_breakdown=signal_quality_breakdown,
145
213
  )
146
214
 
147
215
  # Priority 6: simulate - thin market; participation rate would be
@@ -158,15 +226,20 @@ def build_market_analysis_decision(
158
226
  topology_considered=topology_considered,
159
227
  liquidity_considered=liquidity_considered,
160
228
  reasons=reasons,
229
+ signal_quality_score=signal_quality_score,
230
+ signal_quality_breakdown=signal_quality_breakdown,
161
231
  )
162
232
 
163
233
  # Priority 7: trade - only for trading-eligible assets with an actionable
164
234
  # directional signal, sufficient confidence, no topology isolation, and
165
235
  # no liquidity block (redundant guard — tiers 5/6 already caught those).
236
+ # Gates on trade_metric, not raw confidence directly - see trade_metric's
237
+ # definition above (identical to confidence unless
238
+ # use_composite_signal_score=True).
166
239
  if (
167
240
  trading_eligible
168
241
  and signal_name in {"buy", "sell"}
169
- and confidence >= min_confidence_to_trade
242
+ and trade_metric >= min_confidence_to_trade
170
243
  and topology_risk != "isolated"
171
244
  and liquidity_action not in {"block", "simulate_instead"}
172
245
  ):
@@ -181,6 +254,8 @@ def build_market_analysis_decision(
181
254
  topology_considered=topology_considered,
182
255
  liquidity_considered=liquidity_considered,
183
256
  reasons=reasons,
257
+ signal_quality_score=signal_quality_score,
258
+ signal_quality_breakdown=signal_quality_breakdown,
184
259
  )
185
260
 
186
261
  # Priority 8: simulate vs observe.
@@ -201,6 +276,8 @@ def build_market_analysis_decision(
201
276
  topology_considered=topology_considered,
202
277
  liquidity_considered=liquidity_considered,
203
278
  reasons=reasons,
279
+ signal_quality_score=signal_quality_score,
280
+ signal_quality_breakdown=signal_quality_breakdown,
204
281
  )
205
282
 
206
283
  reasons.append("no_actionable_edge")
@@ -214,4 +291,6 @@ def build_market_analysis_decision(
214
291
  topology_considered=topology_considered,
215
292
  liquidity_considered=liquidity_considered,
216
293
  reasons=reasons,
294
+ signal_quality_score=signal_quality_score,
295
+ signal_quality_breakdown=signal_quality_breakdown,
217
296
  )