aether-quant 0.3.0__tar.gz → 0.5.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 (252) hide show
  1. {aether_quant-0.3.0 → aether_quant-0.5.0}/.gitignore +5 -0
  2. {aether_quant-0.3.0 → aether_quant-0.5.0}/Dockerfile.retraining_worker +3 -0
  3. {aether_quant-0.3.0 → aether_quant-0.5.0}/PKG-INFO +1 -1
  4. {aether_quant-0.3.0 → aether_quant-0.5.0}/README.md +182 -8
  5. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/PKG-INFO +1 -1
  6. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/SOURCES.txt +11 -0
  7. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/scm_file_list.json +11 -0
  8. aether_quant-0.5.0/aether_quant.egg-info/scm_version.json +8 -0
  9. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/top_level.txt +1 -0
  10. {aether_quant-0.3.0 → aether_quant-0.5.0}/analyzer/README.md +11 -0
  11. {aether_quant-0.3.0 → aether_quant-0.5.0}/aq_cli.py +99 -5
  12. {aether_quant-0.3.0 → aether_quant-0.5.0}/data_pipeline/README.md +27 -0
  13. aether_quant-0.5.0/data_pipeline/fetch.py +134 -0
  14. {aether_quant-0.3.0 → aether_quant-0.5.0}/development/Changelog.md +345 -0
  15. {aether_quant-0.3.0 → aether_quant-0.5.0}/development/Problems.md +207 -12
  16. aether_quant-0.5.0/development/backtest_equity_chart.png +0 -0
  17. aether_quant-0.5.0/development/logo.png +0 -0
  18. {aether_quant-0.3.0 → aether_quant-0.5.0}/development/v2_architecture.md +23 -3
  19. {aether_quant-0.3.0 → aether_quant-0.5.0}/generate_backtest_report.py +59 -18
  20. {aether_quant-0.3.0 → aether_quant-0.5.0}/main.py +139 -33
  21. aether_quant-0.5.0/ml/gating_feature_schema.json +30 -0
  22. aether_quant-0.5.0/ml/gating_model.json +570 -0
  23. aether_quant-0.5.0/ml/gating_training_metrics.json +190 -0
  24. aether_quant-0.5.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json +30 -0
  25. aether_quant-0.5.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json +570 -0
  26. aether_quant-0.5.0/ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json +190 -0
  27. aether_quant-0.5.0/moe/README.md +61 -0
  28. {aether_quant-0.3.0 → aether_quant-0.5.0}/moe/__init__.py +4 -0
  29. {aether_quant-0.3.0 → aether_quant-0.5.0}/moe/gating.py +101 -36
  30. {aether_quant-0.3.0 → aether_quant-0.5.0}/monitoring/README.md +1 -0
  31. {aether_quant-0.3.0 → aether_quant-0.5.0}/monitoring/neural_network_state.py +41 -22
  32. {aether_quant-0.3.0 → aether_quant-0.5.0}/pyproject.toml +3 -2
  33. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/__init__.py +4 -0
  34. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/artifacts.py +23 -6
  35. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/orchestrator.py +54 -0
  36. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/worker.py +6 -0
  37. aether_quant-0.5.0/risk/README.md +50 -0
  38. {aether_quant-0.3.0 → aether_quant-0.5.0}/risk/position_sizing.py +52 -1
  39. {aether_quant-0.3.0 → aether_quant-0.5.0}/risk_controls.py +14 -0
  40. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_aq_cli.py +149 -1
  41. aether_quant-0.5.0/tests/test_fetch.py +231 -0
  42. aether_quant-0.5.0/tests/test_gating_network.py +235 -0
  43. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_generate_backtest_report.py +70 -5
  44. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_lean_backtest_ml_coverage.py +8 -6
  45. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_neural_network_state.py +34 -5
  46. aether_quant-0.5.0/tests/test_position_sizing.py +123 -0
  47. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_retraining_artifacts.py +66 -1
  48. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_retraining_orchestrator.py +49 -0
  49. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_retraining_worker.py +20 -12
  50. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_risk_controls.py +19 -0
  51. aether_quant-0.5.0/tests/test_train_gating.py +179 -0
  52. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_train_pipeline.py +66 -0
  53. {aether_quant-0.3.0 → aether_quant-0.5.0}/topology/README.md +7 -0
  54. {aether_quant-0.3.0 → aether_quant-0.5.0}/train.py +20 -1
  55. aether_quant-0.5.0/train_gating.py +436 -0
  56. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/README.md +13 -0
  57. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/neuralnet/NeuralNetworkScene3D.tsx +5 -3
  58. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/types/state.ts +2 -2
  59. aether_quant-0.3.0/aether_quant.egg-info/scm_version.json +0 -8
  60. aether_quant-0.3.0/development/backtest_equity_chart.png +0 -0
  61. aether_quant-0.3.0/moe/README.md +0 -19
  62. aether_quant-0.3.0/risk/README.md +0 -19
  63. aether_quant-0.3.0/tests/test_gating_network.py +0 -97
  64. aether_quant-0.3.0/tests/test_position_sizing.py +0 -55
  65. {aether_quant-0.3.0 → aether_quant-0.5.0}/.dockerignore +0 -0
  66. {aether_quant-0.3.0 → aether_quant-0.5.0}/.env.compose.example +0 -0
  67. {aether_quant-0.3.0 → aether_quant-0.5.0}/.env.live.example +0 -0
  68. {aether_quant-0.3.0 → aether_quant-0.5.0}/.gitattributes +0 -0
  69. {aether_quant-0.3.0 → aether_quant-0.5.0}/.github/workflows/ci.yml +0 -0
  70. {aether_quant-0.3.0 → aether_quant-0.5.0}/.github/workflows/release.yml +0 -0
  71. {aether_quant-0.3.0 → aether_quant-0.5.0}/Dockerfile +0 -0
  72. {aether_quant-0.3.0 → aether_quant-0.5.0}/Dockerfile.workers +0 -0
  73. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/dependency_links.txt +0 -0
  74. {aether_quant-0.3.0 → aether_quant-0.5.0}/aether_quant.egg-info/entry_points.txt +0 -0
  75. {aether_quant-0.3.0 → aether_quant-0.5.0}/analyzer/__init__.py +0 -0
  76. {aether_quant-0.3.0 → aether_quant-0.5.0}/analyzer/market_analyzer.py +0 -0
  77. {aether_quant-0.3.0 → aether_quant-0.5.0}/backtests/.gitkeep +0 -0
  78. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/estimize/consensus/aapl/.keep +0 -0
  79. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/estimize/estimate/.keep +0 -0
  80. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/estimize/release/.keep +0 -0
  81. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/sec/aapl/.keep +0 -0
  82. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/trading-economics/.keep +0 -0
  83. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/trading-economics/calendar/.keep +0 -0
  84. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/trading-economics/earnings/.keep +0 -0
  85. {aether_quant-0.3.0 → aether_quant-0.5.0}/data/alternative/trading-economics/indicator/.keep +0 -0
  86. {aether_quant-0.3.0 → aether_quant-0.5.0}/data_pipeline/__init__.py +0 -0
  87. {aether_quant-0.3.0 → aether_quant-0.5.0}/data_pipeline/v2_manifest.py +0 -0
  88. {aether_quant-0.3.0 → aether_quant-0.5.0}/data_pipeline/yfinance_backfill.py +0 -0
  89. {aether_quant-0.3.0 → aether_quant-0.5.0}/development/README.md +0 -0
  90. {aether_quant-0.3.0 → aether_quant-0.5.0}/development/infrastructure.md +0 -0
  91. {aether_quant-0.3.0 → aether_quant-0.5.0}/docker-compose.yml +0 -0
  92. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/README.md +0 -0
  93. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/__init__.py +0 -0
  94. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/config_cache.py +0 -0
  95. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/live_credentials.py +0 -0
  96. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/live_credentials_io.py +0 -0
  97. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/order_gate.py +0 -0
  98. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/paper_readiness.py +0 -0
  99. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/paper_readiness_io.py +0 -0
  100. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/paper_readiness_report.py +0 -0
  101. {aether_quant-0.3.0 → aether_quant-0.5.0}/execution/runtime_config_io.py +0 -0
  102. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/README.md +0 -0
  103. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/__init__.py +0 -0
  104. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/observation_metrics.py +0 -0
  105. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/postgres_worker.py +0 -0
  106. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/redis_queue.py +0 -0
  107. {aether_quant-0.3.0 → aether_quant-0.5.0}/experience/simulated_portfolio.py +0 -0
  108. {aether_quant-0.3.0 → aether_quant-0.5.0}/experts/README.md +0 -0
  109. {aether_quant-0.3.0 → aether_quant-0.5.0}/experts/__init__.py +0 -0
  110. {aether_quant-0.3.0 → aether_quant-0.5.0}/experts/expert_datasets.py +0 -0
  111. {aether_quant-0.3.0 → aether_quant-0.5.0}/inference/README.md +0 -0
  112. {aether_quant-0.3.0 → aether_quant-0.5.0}/inference/__init__.py +0 -0
  113. {aether_quant-0.3.0 → aether_quant-0.5.0}/inference/exported_model.py +0 -0
  114. {aether_quant-0.3.0 → aether_quant-0.5.0}/liquidity/README.md +0 -0
  115. {aether_quant-0.3.0 → aether_quant-0.5.0}/liquidity/__init__.py +0 -0
  116. {aether_quant-0.3.0 → aether_quant-0.5.0}/liquidity/market_liquidity.py +0 -0
  117. {aether_quant-0.3.0 → aether_quant-0.5.0}/ml/README.md +0 -0
  118. {aether_quant-0.3.0 → aether_quant-0.5.0}/monitoring/__init__.py +0 -0
  119. {aether_quant-0.3.0 → aether_quant-0.5.0}/monitoring/api_server.py +0 -0
  120. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/README.md +0 -0
  121. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/__init__.py +0 -0
  122. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/postgres_telegram.py +0 -0
  123. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/telegram_alerts.py +0 -0
  124. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/telegram_client.py +0 -0
  125. {aether_quant-0.3.0 → aether_quant-0.5.0}/notifications/telegram_worker.py +0 -0
  126. {aether_quant-0.3.0 → aether_quant-0.5.0}/performance/README.md +0 -0
  127. {aether_quant-0.3.0 → aether_quant-0.5.0}/performance/__init__.py +0 -0
  128. {aether_quant-0.3.0 → aether_quant-0.5.0}/performance/postgres_triggers.py +0 -0
  129. {aether_quant-0.3.0 → aether_quant-0.5.0}/performance/trigger_worker.py +0 -0
  130. {aether_quant-0.3.0 → aether_quant-0.5.0}/performance/triggers.py +0 -0
  131. {aether_quant-0.3.0 → aether_quant-0.5.0}/regime/README.md +0 -0
  132. {aether_quant-0.3.0 → aether_quant-0.5.0}/regime/__init__.py +0 -0
  133. {aether_quant-0.3.0 → aether_quant-0.5.0}/regime/market_regime.py +0 -0
  134. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/README.md +0 -0
  135. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/requirements-dev.txt +0 -0
  136. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/requirements-retraining-worker.txt +0 -0
  137. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/requirements-runtime.txt +0 -0
  138. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/requirements-workers.txt +0 -0
  139. {aether_quant-0.3.0 → aether_quant-0.5.0}/requirements/requirements.txt +0 -0
  140. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/README.md +0 -0
  141. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/backtest_gate.py +0 -0
  142. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/lean_backtest.py +0 -0
  143. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/planning.py +0 -0
  144. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/postgres_registry.py +0 -0
  145. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/status_export.py +0 -0
  146. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/validation_gate.py +0 -0
  147. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/vault_client.py +0 -0
  148. {aether_quant-0.3.0 → aether_quant-0.5.0}/retraining/vault_commands.py +0 -0
  149. {aether_quant-0.3.0 → aether_quant-0.5.0}/risk/__init__.py +0 -0
  150. {aether_quant-0.3.0 → aether_quant-0.5.0}/risk/manual_override.py +0 -0
  151. {aether_quant-0.3.0 → aether_quant-0.5.0}/setup.cfg +0 -0
  152. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/README.md +0 -0
  153. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_backtest_gate.py +0 -0
  154. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_config_cache.py +0 -0
  155. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_experience_queue.py +0 -0
  156. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_expert_datasets.py +0 -0
  157. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_expert_models.py +0 -0
  158. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_exported_model.py +0 -0
  159. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_lean_backtest.py +0 -0
  160. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_learned_topology.py +0 -0
  161. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_live_credentials.py +0 -0
  162. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_live_credentials_io.py +0 -0
  163. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_manual_override.py +0 -0
  164. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_market_analyzer.py +0 -0
  165. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_market_liquidity.py +0 -0
  166. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_market_regime.py +0 -0
  167. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_market_topology.py +0 -0
  168. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_observation_metrics.py +0 -0
  169. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_order_gate.py +0 -0
  170. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_paper_readiness.py +0 -0
  171. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_paper_readiness_io.py +0 -0
  172. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_paper_readiness_report.py +0 -0
  173. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_postgres_telegram.py +0 -0
  174. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_postgres_triggers.py +0 -0
  175. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_postgres_worker.py +0 -0
  176. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_retraining_planning.py +0 -0
  177. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_retraining_postgres_registry.py +0 -0
  178. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_runtime_config_io.py +0 -0
  179. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_simulated_portfolio.py +0 -0
  180. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_status_export.py +0 -0
  181. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_telegram_alerts.py +0 -0
  182. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_telegram_client.py +0 -0
  183. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_telegram_worker.py +0 -0
  184. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_train_topology.py +0 -0
  185. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_trigger_worker.py +0 -0
  186. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_triggers.py +0 -0
  187. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_v2_pipeline_manifest.py +0 -0
  188. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_validation_gate.py +0 -0
  189. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_vault_client.py +0 -0
  190. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_vault_commands.py +0 -0
  191. {aether_quant-0.3.0 → aether_quant-0.5.0}/tests/test_yfinance_backfill.py +0 -0
  192. {aether_quant-0.3.0 → aether_quant-0.5.0}/topology/__init__.py +0 -0
  193. {aether_quant-0.3.0 → aether_quant-0.5.0}/topology/learned_topology.py +0 -0
  194. {aether_quant-0.3.0 → aether_quant-0.5.0}/topology/market_topology.py +0 -0
  195. {aether_quant-0.3.0 → aether_quant-0.5.0}/train_topology.py +0 -0
  196. {aether_quant-0.3.0 → aether_quant-0.5.0}/visualization/README.md +0 -0
  197. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/.gitignore +0 -0
  198. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/.oxlintrc.json +0 -0
  199. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/index.html +0 -0
  200. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/overview_v2_12.png +0 -0
  201. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/package-lock.json +0 -0
  202. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/package.json +0 -0
  203. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/public/favicon.svg +0 -0
  204. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/public/icons.svg +0 -0
  205. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/risk_v2_12.png +0 -0
  206. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/App.tsx +0 -0
  207. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/api/client.ts +0 -0
  208. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/api/hooks.ts +0 -0
  209. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/heatmap/AssetHeatmap.tsx +0 -0
  210. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/layout/AppShell.tsx +0 -0
  211. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/layout/Panel.tsx +0 -0
  212. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/CountTable.tsx +0 -0
  213. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/MonitoringFeeds.tsx +0 -0
  214. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/ObservationPanel.tsx +0 -0
  215. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/PaperReadinessPanel.tsx +0 -0
  216. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/PerformanceTriggersPanel.tsx +0 -0
  217. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/RawStateViewer.tsx +0 -0
  218. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/monitoring/RetrainingStatusPanel.tsx +0 -0
  219. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/neuralnet/NeuralNetworkStatsPanel.tsx +0 -0
  220. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/risk/AssetSizingTable.tsx +0 -0
  221. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/risk/LiquidityTable.tsx +0 -0
  222. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/risk/RiskBar.tsx +0 -0
  223. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/risk/RiskCore.tsx +0 -0
  224. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/risk/StrategyRiskCards.tsx +0 -0
  225. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/scene3d/Scene3D.tsx +0 -0
  226. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/scorecards/Scorecards.tsx +0 -0
  227. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/signals/Badge.tsx +0 -0
  228. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/signals/PositionsList.tsx +0 -0
  229. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/signals/SignalBoard.tsx +0 -0
  230. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/topology/ClusterList.tsx +0 -0
  231. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/topology/TopologyLearningPanel.tsx +0 -0
  232. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/topology/TopologyScene3D.tsx +0 -0
  233. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/AssetPerformancePanel.tsx +0 -0
  234. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/BacktestEquityPanel.tsx +0 -0
  235. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/DivergingBarChart.tsx +0 -0
  236. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/LineChart.tsx +0 -0
  237. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/MetricsSnapshotPanel.tsx +0 -0
  238. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/components/tracing/ObservationEquityPanel.tsx +0 -0
  239. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/index.css +0 -0
  240. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/lib/downsample.ts +0 -0
  241. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/lib/format.ts +0 -0
  242. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/main.tsx +0 -0
  243. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/pages/NeuralNetworkPage.tsx +0 -0
  244. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/pages/Overview.tsx +0 -0
  245. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/pages/RiskPage.tsx +0 -0
  246. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/pages/TopologyPage.tsx +0 -0
  247. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/pages/TracingPage.tsx +0 -0
  248. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/src/types/tracing.ts +0 -0
  249. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/tsconfig.app.json +0 -0
  250. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/tsconfig.json +0 -0
  251. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/tsconfig.node.json +0 -0
  252. {aether_quant-0.3.0 → aether_quant-0.5.0}/webui/vite.config.ts +0 -0
@@ -94,3 +94,8 @@ Aether-quant-Obsidian-Vault/
94
94
  # Webui (React/Vite) build artifacts and dependencies
95
95
  webui/node_modules/
96
96
  webui/dist/
97
+
98
+ # Matplotlib font cache, redirected here by main.py so it persists across
99
+ # Lean CLI's ephemeral per-backtest Docker containers instead of rebuilding
100
+ # every run (see Problems.md)
101
+ .matplotlib_cache/
@@ -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.3.0
3
+ Version: 0.5.0
4
4
  Summary: Aether Quant convenience CLI
5
5
  Requires-Python: >=3.10
@@ -10,7 +10,7 @@
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-583%2F583%20passing-brightgreen?style=flat-square&labelColor=1A1A1A" alt="583 of 583 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>
@@ -35,6 +35,7 @@ window and hope it generalizes.
35
35
  - [Getting Started](#getting-started)
36
36
  - [Requirements](#requirements)
37
37
  - [Architecture](#architecture)
38
+ - [Universe Size](#universe-size)
38
39
  - [Project Structure](#project-structure)
39
40
  - [Module Documentation](#module-documentation)
40
41
  - [Development Documentation](#development-documentation)
@@ -202,6 +203,105 @@ and more), the module map, and an honest analysis of what would need to
202
203
  change for this to become a genuinely low-latency/HFT system — see
203
204
  **[`development/v2_architecture.md`](development/v2_architecture.md)**.
204
205
 
206
+ ## Universe Size
207
+
208
+ The trading universe currently spans **20 assets** — 15 equities and 5
209
+ crypto pairs — defined in `config.json`'s `phase1.universe.assets` and
210
+ shared across training, validation, and backtesting
211
+ (`phase1.universe.common_window`: `2014-12-01` to `2021-03-31`).
212
+
213
+ | Ticker | Type | Role |
214
+ |---|---|---|
215
+ | AAPL | Equity | Trading |
216
+ | SPY | Equity | Trading |
217
+ | QQQ | Equity | Trading |
218
+ | IWM | Equity | Trading |
219
+ | EEM | Equity | Trading |
220
+ | BAC | Equity | Trading |
221
+ | IBM | Equity | Trading |
222
+ | AIG | Equity | Trading |
223
+ | BNO | Equity | Trading |
224
+ | FB | Equity | Trading |
225
+ | GOOG | Equity | Trading |
226
+ | GOOGL | Equity | Trading |
227
+ | USO | Equity | Trading |
228
+ | WM | Equity | Trading |
229
+ | AAA | Equity | Observation-only (thin history) |
230
+ | BTCUSD | Crypto | Trading |
231
+ | ETHUSD | Crypto | Observation-only (thin history) |
232
+ | LTCUSD | Crypto | Trading |
233
+ | XRPUSD | Crypto | Observation-only (thin history) |
234
+ | ADAUSD | Crypto | Observation-only (thin history) |
235
+
236
+ "Observation-only" assets (Phase 9's `asset_quality` gate) are still fed
237
+ through the full model/expert/topology pipeline every bar and visible on
238
+ the dashboard, but are never sized into real positions — their real
239
+ history is too short relative to the training window to be trusted for
240
+ trading decisions (see [`development/Changelog.md`](development/Changelog.md)
241
+ for the exact row-count thresholds). This is re-evaluated automatically
242
+ every time `train.py` rebuilds the dataset, so an asset can move between
243
+ these two roles as more history accumulates.
244
+
245
+ ```mermaid
246
+ flowchart TD
247
+ DNN(("Baseline DNN<br/>+ MoE Experts<br/>bullish / bearish /<br/>sideways / volatility"))
248
+
249
+ subgraph Equities["Equities (15)"]
250
+ AAPL["AAPL"]
251
+ SPY["SPY"]
252
+ QQQ["QQQ"]
253
+ IWM["IWM"]
254
+ EEM["EEM"]
255
+ BAC["BAC"]
256
+ IBM["IBM"]
257
+ AIG["AIG"]
258
+ BNO["BNO"]
259
+ FB["FB"]
260
+ GOOG["GOOG"]
261
+ GOOGL["GOOGL"]
262
+ USO["USO"]
263
+ WM["WM"]
264
+ AAA["AAA"]
265
+ end
266
+
267
+ subgraph Crypto["Crypto (5)"]
268
+ BTCUSD["BTCUSD"]
269
+ ETHUSD["ETHUSD"]
270
+ LTCUSD["LTCUSD"]
271
+ XRPUSD["XRPUSD"]
272
+ ADAUSD["ADAUSD"]
273
+ end
274
+
275
+ AAPL --- DNN
276
+ SPY --- DNN
277
+ QQQ --- DNN
278
+ IWM --- DNN
279
+ EEM --- DNN
280
+ BAC --- DNN
281
+ IBM --- DNN
282
+ AIG --- DNN
283
+ BNO --- DNN
284
+ FB --- DNN
285
+ GOOG --- DNN
286
+ GOOGL --- DNN
287
+ USO --- DNN
288
+ WM --- DNN
289
+ AAA --- DNN
290
+ BTCUSD --- DNN
291
+ ETHUSD --- DNN
292
+ LTCUSD --- DNN
293
+ XRPUSD --- DNN
294
+ ADAUSD --- DNN
295
+
296
+ classDef hub fill:#1A1A1A,stroke:#FF8C00,color:#FF8C00,stroke-width:2px;
297
+ classDef trading fill:#FF8C00,stroke:#1A1A1A,color:#1A1A1A,stroke-width:1px;
298
+ classDef observation fill:#3A3A3A,stroke:#FF8C00,color:#FF8C00,stroke-width:1px,stroke-dasharray: 4 2;
299
+
300
+ class DNN hub;
301
+ class AAPL,SPY,QQQ,IWM,EEM,BAC,IBM,AIG,BNO,FB,GOOG,GOOGL,USO,WM,BTCUSD,LTCUSD trading;
302
+ class AAA,ETHUSD,XRPUSD,ADAUSD observation;
303
+ ```
304
+
205
305
  ## Project Structure
206
306
 
207
307
  ```text
@@ -291,16 +391,88 @@ and how it's wired in — this table is the index.
291
391
 
292
392
  | Metric | Value |
293
393
  |---|---|
294
- | Backtest window | 2014-12-01 to 2016-07-06 |
295
- | Last updated | 2026-07-05 15:37 UTC (auto-generated by `aq backtest`) |
394
+ | Backtest window | 2018-04-01 to 2021-04-02 |
395
+ | Sharpe Ratio | -0.71 |
396
+ | Net Profit | -8.069% |
397
+ | Compounding Annual Return | -2.761% |
398
+ | Drawdown | 12.500% |
399
+ | Total Orders | 27 |
400
+ | Win Rate | 17% |
401
+ | Last updated | 2026-07-06 17:20 UTC (auto-generated by `aq backtest`) |
296
402
  <!-- AQ:BACKTEST_END -->
297
403
 
404
+ <details>
405
+ <summary><strong>Full Lean statistics</strong> (Sharpe, Sortino, Alpha/Beta, fees, capacity, and everything else Lean reports)</summary>
406
+
407
+ <!-- AQ:BACKTEST_FULL_STATS_START -->
408
+ | Metric | Value |
409
+ |---|---|
410
+ | Total Orders | 27 |
411
+ | Average Win | 0.18% |
412
+ | Average Loss | -1.70% |
413
+ | Compounding Annual Return | -2.761% |
414
+ | Drawdown | 12.500% |
415
+ | Expectancy | -0.816 |
416
+ | Start Equity | 100000.00 |
417
+ | End Equity | 91930.60 |
418
+ | Net Profit | -8.069% |
419
+ | Sharpe Ratio | -0.71 |
420
+ | Sortino Ratio | -0.471 |
421
+ | Probabilistic Sharpe Ratio | 0.171% |
422
+ | Loss Rate | 83% |
423
+ | Win Rate | 17% |
424
+ | Profit-Loss Ratio | 0.11 |
425
+ | Alpha | -0.058 |
426
+ | Beta | 0.195 |
427
+ | Annual Standard Deviation | 0.051 |
428
+ | Annual Variance | 0.003 |
429
+ | Information Ratio | -0.972 |
430
+ | Tracking Error | 0.155 |
431
+ | Treynor Ratio | -0.184 |
432
+ | Total Fees | $12.00 |
433
+ | Estimated Strategy Capacity | $210000000.00 |
434
+ | Lowest Capacity Asset | IBM R735QTJ8XC9X |
435
+ | Portfolio Turnover | 0.07% |
436
+ | Drawdown Recovery | 503 |
437
+ <!-- AQ:BACKTEST_FULL_STATS_END -->
438
+
439
+ </details>
440
+
298
441
  This section is regenerated automatically every time you run `aq backtest`
299
442
  (see [`generate_backtest_report.py`](generate_backtest_report.py)) — it
300
443
  always reflects your most recent successful Lean backtest, reading directly
301
444
  from Lean's own result JSON (strategy equity curve, its native SPY benchmark
302
445
  series, and the full statistics block), so it never goes stale as long as
303
- you keep backtesting.
446
+ you keep backtesting. Both the chart image and every statistic above are
447
+ overwritten on each run — there is no manual step and nothing here can go
448
+ stale relative to your last `aq backtest`.
449
+
450
+ **What this backtest does *not* prove:** a bare `lean backtest .` run
451
+ exercises the full inference stack (baseline model, all 4 experts, MoE
452
+ gating, regime, topology, liquidity) every bar, but it does **not**
453
+ exercise the controlled retraining loop — the "learning while trading"
454
+ half of this system's thesis. That loop is a decoupled, asynchronous
455
+ pipeline (`main.py` → Redis → experience-worker → Postgres →
456
+ performance-trigger-worker → retraining-worker), and a bare backtest run
457
+ outside the Docker Compose network can't even reach Redis — events are
458
+ dropped with a warning, so nothing reaches Postgres, no performance
459
+ trigger can fire, and no retraining ever runs (see
460
+ `development/infrastructure.md`). Exercising retraining for real requires
461
+ the full Compose stack up (`docker compose up -d redis postgres
462
+ experience-worker performance-trigger-worker retraining-worker`) with the
463
+ backtest run inside that network, plus an actual trigger condition being
464
+ met during the run.
465
+
466
+ **If `phase_v2.backtest.bypass_safety_gates` is `true`:** this backtest
467
+ also does not represent live/paper-deployable behavior. That flag (default
468
+ `false`) disables the sticky total-drawdown lock and the regime detector's
469
+ drawdown-driven `risk_off` override — both real, designed safety behavior
470
+ in live/paper mode — purely to generate enough trade volume for
471
+ statistically meaningful backtest metrics (see `development/Problems.md`
472
+ #18). A backtest run with this flag set shows the underlying model's
473
+ signal quality across more market conditions, not what this system would
474
+ have actually done if deployed — in live/paper mode, both gates would have
475
+ genuinely halted trading exactly as designed.
304
476
 
305
477
  ## Test Suite
306
478
 
@@ -334,7 +506,7 @@ pip install -e .
334
506
  Either way, `aq --help` gives the full command list:
335
507
 
336
508
  ```text
337
- aq train [--dataset-only|--init-only|--experts-only]
509
+ aq train [--dataset-only|--init-only|--experts-only|--gating-only]
338
510
  aq test
339
511
  aq backtest
340
512
  aq report <backtest-folder> <result-id>
@@ -344,13 +516,14 @@ aq docker up [--lean|--all]
344
516
  aq docker build
345
517
  aq retrain <plan|train|validate|backtest|commit|promote|rollback|status> [...]
346
518
  aq trade-lock --on|--off|--auto|--status
519
+ aq fetch <crypto|stock> --ticker <TICKER> --start <YYYY-MM-DD> --end <YYYY-MM-DD> [--apply]
347
520
  aq status
348
521
  ```
349
522
 
350
- Every command except `aq trade-lock` is a thin `subprocess` wrapper around a
351
- command already documented elsewhere in this README:
523
+ Every command except `aq trade-lock` and `aq fetch` is a thin `subprocess`
524
+ wrapper around a command already documented elsewhere in this README:
352
525
 
353
- - **`aq train`** — runs `train.py`: builds the dataset and trains the baseline + expert models.
526
+ - **`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`.
354
527
  - **`aq test`** — runs the pytest suite and refreshes this README's test badge.
355
528
  - **`aq backtest`** — runs `lean backtest .` and refreshes this README's [Backtest Results](#backtest-results) section.
356
529
  - **`aq report <backtest-folder> <result-id>`** — generates Lean's own HTML backtest report (trade blotter, standard Lean charts) at `backtests/<backtest-folder>/report.html`.
@@ -360,6 +533,7 @@ command already documented elsewhere in this README:
360
533
  - **`aq docker build`** — rebuilds the `aether-quant` app image.
361
534
  - **`aq retrain <stage>`** — dispatches to `python -m retraining.orchestrator <stage> ...` for a single manual pipeline stage.
362
535
  - **`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.
536
+ - **`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.
363
537
  - **`aq status`** — shows `git status`.
364
538
 
365
539
  ## Release Process
@@ -1,5 +1,5 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-quant
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: Aether Quant convenience CLI
5
5
  Requires-Python: >=3.10
@@ -14,6 +14,7 @@ main.py
14
14
  pyproject.toml
15
15
  risk_controls.py
16
16
  train.py
17
+ train_gating.py
17
18
  train_topology.py
18
19
  .github/workflows/ci.yml
19
20
  .github/workflows/release.yml
@@ -38,6 +39,7 @@ data/alternative/trading-economics/earnings/.keep
38
39
  data/alternative/trading-economics/indicator/.keep
39
40
  data_pipeline/README.md
40
41
  data_pipeline/__init__.py
42
+ data_pipeline/fetch.py
41
43
  data_pipeline/v2_manifest.py
42
44
  data_pipeline/yfinance_backfill.py
43
45
  development/Changelog.md
@@ -45,6 +47,7 @@ development/Problems.md
45
47
  development/README.md
46
48
  development/backtest_equity_chart.png
47
49
  development/infrastructure.md
50
+ development/logo.png
48
51
  development/v2_architecture.md
49
52
  execution/README.md
50
53
  execution/__init__.py
@@ -72,6 +75,12 @@ liquidity/README.md
72
75
  liquidity/__init__.py
73
76
  liquidity/market_liquidity.py
74
77
  ml/README.md
78
+ ml/gating_feature_schema.json
79
+ ml/gating_model.json
80
+ ml/gating_training_metrics.json
81
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json
82
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json
83
+ ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json
75
84
  moe/README.md
76
85
  moe/__init__.py
77
86
  moe/gating.py
@@ -124,6 +133,7 @@ tests/test_experience_queue.py
124
133
  tests/test_expert_datasets.py
125
134
  tests/test_expert_models.py
126
135
  tests/test_exported_model.py
136
+ tests/test_fetch.py
127
137
  tests/test_gating_network.py
128
138
  tests/test_generate_backtest_report.py
129
139
  tests/test_lean_backtest.py
@@ -158,6 +168,7 @@ tests/test_status_export.py
158
168
  tests/test_telegram_alerts.py
159
169
  tests/test_telegram_client.py
160
170
  tests/test_telegram_worker.py
171
+ tests/test_train_gating.py
161
172
  tests/test_train_pipeline.py
162
173
  tests/test_train_topology.py
163
174
  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",
@@ -43,6 +44,7 @@
43
44
  "data_pipeline/README.md",
44
45
  "data_pipeline/__init__.py",
45
46
  "data_pipeline/v2_manifest.py",
47
+ "data_pipeline/fetch.py",
46
48
  "data_pipeline/yfinance_backfill.py",
47
49
  "visualization/README.md",
48
50
  "inference/README.md",
@@ -152,6 +154,7 @@
152
154
  "webui/src/types/state.ts",
153
155
  "webui/public/favicon.svg",
154
156
  "webui/public/icons.svg",
157
+ "development/logo.png",
155
158
  "development/backtest_equity_chart.png",
156
159
  "development/README.md",
157
160
  "development/Problems.md",
@@ -209,6 +212,8 @@
209
212
  "tests/test_train_pipeline.py",
210
213
  "tests/test_experience_queue.py",
211
214
  "tests/test_vault_commands.py",
215
+ "tests/test_train_gating.py",
216
+ "tests/test_fetch.py",
212
217
  "data/alternative/sec/aapl/.keep",
213
218
  "data/alternative/trading-economics/.keep",
214
219
  "data/alternative/trading-economics/calendar/.keep",
@@ -224,6 +229,12 @@
224
229
  "requirements/requirements-workers.txt",
225
230
  "requirements/requirements-retraining-worker.txt",
226
231
  "ml/README.md",
232
+ "ml/gating_model.json",
233
+ "ml/gating_feature_schema.json",
234
+ "ml/gating_training_metrics.json",
235
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_model.json",
236
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_feature_schema.json",
237
+ "ml/versions/gating-only-26218402-34d9-4a0a-add8-ee5d50dd0459/gating_training_metrics.json",
227
238
  ".github/workflows/release.yml",
228
239
  ".github/workflows/ci.yml"
229
240
  ]
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.5.0",
3
+ "distance": 0,
4
+ "node": "gb230bc437a96905925c7aa521e7006da67cfb98c",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-07"
8
+ }
@@ -1,4 +1,5 @@
1
1
  aq_cli
2
+ data_pipeline
2
3
  execution
3
4
  generate_backtest_report
4
5
  risk
@@ -38,6 +38,17 @@ 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
@@ -3,11 +3,12 @@
3
3
  Matches this codebase's existing CLI convention exactly (see
4
4
  `retraining/orchestrator.py`'s `argparse` + `subparsers.add_parser(...)`
5
5
  shape) - a single-file dispatcher, not a framework. Every subcommand other
6
- than `trade-lock` is a thin `subprocess.run(...)` wrapper around a command
7
- that already exists and is already documented elsewhere (README.md,
8
- development/infrastructure.md) - no logic is reimplemented here, this file
9
- only saves typing. `trade-lock` is the one exception: it calls
10
- `risk/manual_override.py` directly, no subprocess.
6
+ than `trade-lock` and `fetch` is a thin `subprocess.run(...)` wrapper around
7
+ a command that already exists and is already documented elsewhere
8
+ (README.md, development/infrastructure.md) - no logic is reimplemented
9
+ here, this file only saves typing. `trade-lock` and `fetch` are the two
10
+ exceptions: they call `risk/manual_override.py` and
11
+ `data_pipeline/fetch.py` directly, in-process, no subprocess.
11
12
 
12
13
  Deliberately scoped for v1 - wraps the commands already in daily use, not
13
14
  every command mentioned anywhere in the project. Designed to be extended
@@ -31,9 +32,12 @@ import subprocess
31
32
  import sys
32
33
  import time
33
34
  import urllib.request
35
+ import uuid
36
+ from datetime import date
34
37
  from importlib.metadata import version as installed_version
35
38
  from pathlib import Path
36
39
 
40
+ from data_pipeline.fetch import ASSET_CLASSES, fetch_adhoc_asset
37
41
  from risk.manual_override import read_manual_trade_lock_override, write_manual_trade_lock_override
38
42
 
39
43
  ROOT_DIR = Path(__file__).resolve().parent
@@ -94,6 +98,18 @@ def _parse_simple_version(value: str) -> tuple[int, ...] | None:
94
98
  return None
95
99
 
96
100
 
101
+ def _iso_date(value: str) -> str:
102
+ """Validates --start/--end as strict ISO 8601 YYYY-MM-DD, matching the
103
+ convention used everywhere else in this repo (config.json,
104
+ yfinance_backfill.py) - rejects other formats (e.g. DD.MM.YYYY) with a
105
+ clear error instead of a confusing downstream yfinance failure."""
106
+ try:
107
+ date.fromisoformat(value)
108
+ except ValueError as exc:
109
+ raise argparse.ArgumentTypeError(f"invalid date {value!r} - expected ISO 8601 YYYY-MM-DD") from exc
110
+ return value
111
+
112
+
97
113
  def _read_update_cache() -> dict:
98
114
  if not UPDATE_CACHE_PATH.exists():
99
115
  return {}
@@ -161,6 +177,8 @@ def check_for_update() -> None:
161
177
 
162
178
 
163
179
  def cmd_train(args: argparse.Namespace) -> int:
180
+ if args.gating_only:
181
+ return _train_gating_only()
164
182
  cmd = [sys.executable, "train.py"]
165
183
  if args.dataset_only:
166
184
  cmd.append("--dataset-only")
@@ -171,6 +189,41 @@ def cmd_train(args: argparse.Namespace) -> int:
171
189
  return _run(cmd)
172
190
 
173
191
 
192
+ def _train_gating_only() -> int:
193
+ """`aq train --gating-only`: trains the learned gating blend
194
+ (train_gating.py) and installs it straight into active ml/, mirroring
195
+ what `train.py --experts-only` already does for the expert models.
196
+
197
+ train_gating.py always writes to ml/versions/<version_id>/ (same
198
+ versioned-candidate convention every other trainer in this project
199
+ uses), so this generates a throwaway version-id, runs the trainer,
200
+ then copies the 3 resulting artifacts into active ml/ - the same
201
+ manual promotion-simulation step already documented for verifying this
202
+ trainer, skipping the full retraining/validate/backtest/commit/promote
203
+ pipeline since this is an ad-hoc, user-triggered run, not a scheduled
204
+ candidate."""
205
+ version_id = f"gating-only-{uuid.uuid4()}"
206
+ returncode = _run([sys.executable, "train_gating.py", "--version-id", version_id])
207
+ if returncode != 0:
208
+ return returncode
209
+
210
+ version_dir = ROOT_DIR / "ml" / "versions" / version_id
211
+ artifact_names = ("gating_model.json", "gating_feature_schema.json", "gating_training_metrics.json")
212
+ if any(not (version_dir / name).exists() for name in artifact_names):
213
+ print(
214
+ "aq train --gating-only: train_gating.py exited 0 but skipped writing artifacts "
215
+ "(likely insufficient validation/backtest rows) - active ml/ left unchanged.",
216
+ file=sys.stderr,
217
+ )
218
+ return 0
219
+
220
+ ml_dir = ROOT_DIR / "ml"
221
+ for name in artifact_names:
222
+ shutil.copy2(version_dir / name, ml_dir / name)
223
+ print(f"aq train --gating-only: copied {', '.join(artifact_names)} into active ml/.")
224
+ return 0
225
+
226
+
174
227
  def _update_readme_test_badge(passed: int, failed: int) -> None:
175
228
  """Atomically rewrites the shields.io test-count badge between the
176
229
  AQ:TEST_BADGE markers in README.md, so it never drifts from the real
@@ -340,6 +393,30 @@ def cmd_trade_lock(args: argparse.Namespace) -> int:
340
393
  return 0
341
394
 
342
395
 
396
+ def cmd_fetch(args: argparse.Namespace) -> int:
397
+ report = fetch_adhoc_asset(args.asset_class, args.ticker, args.start, args.end, apply=args.apply)
398
+ label = "APPLY" if args.apply else "DRY RUN"
399
+ print(f"{label} — {report['ticker']} ({report['yahoo_symbol']}): {report['action']}, rows_fetched={report['rows_fetched']}")
400
+ if report["suggested_available_from"]:
401
+ print(f" date range fetched: {report['suggested_available_from']} .. {report['suggested_available_to']}")
402
+ print(f" data_path: {report['data_path']}")
403
+
404
+ if report["config_status"] == "added":
405
+ print(f" config.json: added a new {report['ticker']} asset block to phase1.universe.assets[]")
406
+ elif report["config_status"] == "already_exists":
407
+ print(
408
+ f" config.json: {report['ticker']} is already configured - left untouched. "
409
+ "Use data_pipeline/yfinance_backfill.py to extend an existing asset's date range instead."
410
+ )
411
+
412
+ if not args.apply:
413
+ print("\nDry run only — nothing was written. Re-run with --apply to write the zip file and update config.json.")
414
+ elif report["action"] == "written":
415
+ print("\nReady to prepare training: run `python train.py --dataset-only` to confirm this ticker's asset quality, then `python train.py` when ready.")
416
+
417
+ return 1 if report["action"] == "no_data_returned" else 0
418
+
419
+
343
420
  def cmd_status(_args: argparse.Namespace) -> int:
344
421
  return _run(["git", "status"])
345
422
 
@@ -353,6 +430,9 @@ def build_parser() -> argparse.ArgumentParser:
353
430
  train_group.add_argument("--dataset-only", action="store_true", help="Build dataset/scaler/manifest only")
354
431
  train_group.add_argument("--init-only", action="store_true", help="Refresh the data inventory only")
355
432
  train_group.add_argument("--experts-only", action="store_true", help="Train the 4 expert models only")
433
+ train_group.add_argument(
434
+ "--gating-only", action="store_true", help="Train the learned gating blend only (wraps python train_gating.py)"
435
+ )
356
436
  train_parser.set_defaults(func=cmd_train)
357
437
 
358
438
  test_parser = subparsers.add_parser("test", help="Run the test suite (wraps pytest tests/)")
@@ -409,6 +489,20 @@ def build_parser() -> argparse.ArgumentParser:
409
489
  trade_lock_group.add_argument("--status", dest="status", action="store_true", help="Print the current override state")
410
490
  trade_lock_parser.set_defaults(func=cmd_trade_lock)
411
491
 
492
+ fetch_parser = subparsers.add_parser(
493
+ "fetch", help="Ad-hoc fetch of historical OHLCV from Yahoo Finance for a ticker not yet in config.json"
494
+ )
495
+ fetch_parser.add_argument(
496
+ "asset_class", choices=list(ASSET_CLASSES), help="Asset class (picks the Lean data_path/market convention)"
497
+ )
498
+ fetch_parser.add_argument("--ticker", required=True, help="Internal ticker, e.g. AAPL or BTCUSD")
499
+ fetch_parser.add_argument("--start", required=True, type=_iso_date, help="Start date, ISO 8601 YYYY-MM-DD")
500
+ fetch_parser.add_argument("--end", required=True, type=_iso_date, help="End date, ISO 8601 YYYY-MM-DD")
501
+ fetch_parser.add_argument(
502
+ "--apply", action="store_true", help="Actually write the zip file and update config.json (default: dry run, report only)"
503
+ )
504
+ fetch_parser.set_defaults(func=cmd_fetch)
505
+
412
506
  status_parser = subparsers.add_parser("status", help="Show git status")
413
507
  status_parser.set_defaults(func=cmd_status)
414
508
 
@@ -35,3 +35,30 @@ Usage: `python -m data_pipeline.yfinance_backfill [--tickers ETHUSD LTCUSD] [--a
35
35
  `yfinance` is a dev-only dependency (`requirements/requirements-dev.txt`),
36
36
  never in `requirements.txt`/`requirements-runtime.txt`.
37
37
 
38
+ ## Ad-hoc ticker fetch (`aq fetch`)
39
+
40
+ `fetch.py` is a narrower sibling to `yfinance_backfill.py` above, with a
41
+ different scope and a deliberately different `config.json` policy:
42
+
43
+ | | `yfinance_backfill.py` | `fetch.py` (backs `aq fetch`) |
44
+ |---|---|---|
45
+ | Scope | Fills gaps in **already-configured** assets | Fetches **any** ticker, including ones with no `config.json` entry at all |
46
+ | Date range | Auto-detected gap vs. the asset's `"backfill"` block | Explicit `--start`/`--end` |
47
+ | `config.json` | **Never** edited — only prints suggested values | **Writes** a new asset block on `--apply`, for a ticker that isn't already configured |
48
+
49
+ The differing `config.json` policy is deliberate, not an inconsistency:
50
+ `yfinance_backfill.py`'s rule exists to stop it from silently widening an
51
+ *existing*, already-trained asset's date range without a human decision.
52
+ `fetch.py` exists specifically to *add a brand-new ticker* to the universe
53
+ on purpose — auto-writing `config.json` there is the intended effect, not
54
+ an accidental side effect. If a ticker fetched via `aq fetch` already has a
55
+ `config.json` entry, `fetch.py` leaves it untouched and points at
56
+ `yfinance_backfill.py` instead, since extending an existing asset's range
57
+ is that script's job, not this one's.
58
+
59
+ Reuses `yfinance_backfill.py`'s pure functions (`fetch_yahoo_ohlcv`,
60
+ `scale_for_lean`, `write_lean_zip`) unchanged — no duplicated logic. Same
61
+ dry-run-by-default/`--apply` safety convention. Never runs `train.py`
62
+ itself; see the root [`README.md`](../README.md#cli-reference) for the
63
+ full `aq fetch` usage.
64
+