aether-quant 0.3.0__tar.gz → 0.4.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.
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.gitignore +5 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/PKG-INFO +1 -1
- {aether_quant-0.3.0 → aether_quant-0.4.0}/README.md +168 -5
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/PKG-INFO +1 -1
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/SOURCES.txt +3 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/scm_file_list.json +3 -0
- aether_quant-0.4.0/aether_quant.egg-info/scm_version.json +8 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/top_level.txt +1 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aq_cli.py +58 -5
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data_pipeline/README.md +27 -0
- aether_quant-0.4.0/data_pipeline/fetch.py +134 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/development/Changelog.md +159 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/development/Problems.md +104 -0
- aether_quant-0.4.0/development/backtest_equity_chart.png +0 -0
- aether_quant-0.4.0/development/logo.png +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/development/v2_architecture.md +3 -3
- {aether_quant-0.3.0 → aether_quant-0.4.0}/generate_backtest_report.py +59 -18
- {aether_quant-0.3.0 → aether_quant-0.4.0}/main.py +71 -31
- {aether_quant-0.3.0 → aether_quant-0.4.0}/pyproject.toml +3 -2
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_aq_cli.py +99 -1
- aether_quant-0.4.0/tests/test_fetch.py +231 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_generate_backtest_report.py +70 -5
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_lean_backtest_ml_coverage.py +8 -6
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_train_pipeline.py +66 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/train.py +20 -1
- aether_quant-0.3.0/aether_quant.egg-info/scm_version.json +0 -8
- aether_quant-0.3.0/development/backtest_equity_chart.png +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.dockerignore +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.env.compose.example +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.env.live.example +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.gitattributes +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.github/workflows/ci.yml +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/.github/workflows/release.yml +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/Dockerfile +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/Dockerfile.retraining_worker +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/Dockerfile.workers +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/dependency_links.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/aether_quant.egg-info/entry_points.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/analyzer/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/analyzer/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/analyzer/market_analyzer.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/backtests/.gitkeep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/estimize/consensus/aapl/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/estimize/estimate/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/estimize/release/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/sec/aapl/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/trading-economics/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/trading-economics/calendar/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/trading-economics/earnings/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data/alternative/trading-economics/indicator/.keep +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data_pipeline/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data_pipeline/v2_manifest.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/data_pipeline/yfinance_backfill.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/development/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/development/infrastructure.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/docker-compose.yml +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/config_cache.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/live_credentials.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/live_credentials_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/order_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/paper_readiness.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/paper_readiness_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/paper_readiness_report.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/execution/runtime_config_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/observation_metrics.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/postgres_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/redis_queue.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experience/simulated_portfolio.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experts/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experts/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/experts/expert_datasets.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/inference/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/inference/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/inference/exported_model.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/liquidity/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/liquidity/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/liquidity/market_liquidity.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/ml/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/moe/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/moe/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/moe/gating.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/monitoring/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/monitoring/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/monitoring/api_server.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/monitoring/neural_network_state.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/postgres_telegram.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/telegram_alerts.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/telegram_client.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/notifications/telegram_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/performance/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/performance/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/performance/postgres_triggers.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/performance/trigger_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/performance/triggers.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/regime/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/regime/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/regime/market_regime.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/requirements-dev.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/requirements-retraining-worker.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/requirements-runtime.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/requirements-workers.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/requirements/requirements.txt +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/artifacts.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/backtest_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/lean_backtest.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/orchestrator.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/planning.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/postgres_registry.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/status_export.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/validation_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/vault_client.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/vault_commands.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/retraining/worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/risk/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/risk/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/risk/manual_override.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/risk/position_sizing.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/risk_controls.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/setup.cfg +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_backtest_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_config_cache.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_experience_queue.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_expert_datasets.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_expert_models.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_exported_model.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_gating_network.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_lean_backtest.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_learned_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_live_credentials.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_live_credentials_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_manual_override.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_market_analyzer.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_market_liquidity.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_market_regime.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_market_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_neural_network_state.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_observation_metrics.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_order_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_paper_readiness.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_paper_readiness_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_paper_readiness_report.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_position_sizing.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_postgres_telegram.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_postgres_triggers.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_postgres_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_retraining_artifacts.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_retraining_orchestrator.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_retraining_planning.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_retraining_postgres_registry.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_retraining_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_risk_controls.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_runtime_config_io.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_simulated_portfolio.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_status_export.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_telegram_alerts.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_telegram_client.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_telegram_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_train_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_trigger_worker.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_triggers.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_v2_pipeline_manifest.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_validation_gate.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_vault_client.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_vault_commands.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/tests/test_yfinance_backfill.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/topology/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/topology/__init__.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/topology/learned_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/topology/market_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/train_topology.py +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/visualization/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/.gitignore +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/.oxlintrc.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/README.md +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/index.html +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/overview_v2_12.png +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/package-lock.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/package.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/public/favicon.svg +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/public/icons.svg +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/risk_v2_12.png +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/App.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/api/client.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/api/hooks.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/heatmap/AssetHeatmap.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/layout/AppShell.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/layout/Panel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/CountTable.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/MonitoringFeeds.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/ObservationPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/PaperReadinessPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/PerformanceTriggersPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/RawStateViewer.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/monitoring/RetrainingStatusPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/neuralnet/NeuralNetworkScene3D.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/neuralnet/NeuralNetworkStatsPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/risk/AssetSizingTable.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/risk/LiquidityTable.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/risk/RiskBar.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/risk/RiskCore.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/risk/StrategyRiskCards.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/scene3d/Scene3D.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/scorecards/Scorecards.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/signals/Badge.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/signals/PositionsList.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/signals/SignalBoard.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/topology/ClusterList.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/topology/TopologyLearningPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/topology/TopologyScene3D.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/AssetPerformancePanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/BacktestEquityPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/DivergingBarChart.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/LineChart.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/MetricsSnapshotPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/components/tracing/ObservationEquityPanel.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/index.css +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/lib/downsample.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/lib/format.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/main.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/pages/NeuralNetworkPage.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/pages/Overview.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/pages/RiskPage.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/pages/TopologyPage.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/pages/TracingPage.tsx +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/types/state.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/src/types/tracing.ts +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/tsconfig.app.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/tsconfig.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.0}/webui/tsconfig.node.json +0 -0
- {aether_quant-0.3.0 → aether_quant-0.4.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/
|
|
@@ -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,77 @@ 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
|
|
295
|
-
|
|
|
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`) |
|
|
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 | 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 |
|
|
434
|
+
| Lowest Capacity Asset | IBM R735QTJ8XC9X |
|
|
435
|
+
| Portfolio Turnover | 0.06% |
|
|
436
|
+
| Drawdown Recovery | 57 |
|
|
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.
|
|
304
465
|
|
|
305
466
|
## Test Suite
|
|
306
467
|
|
|
@@ -344,11 +505,12 @@ aq docker up [--lean|--all]
|
|
|
344
505
|
aq docker build
|
|
345
506
|
aq retrain <plan|train|validate|backtest|commit|promote|rollback|status> [...]
|
|
346
507
|
aq trade-lock --on|--off|--auto|--status
|
|
508
|
+
aq fetch <crypto|stock> --ticker <TICKER> --start <YYYY-MM-DD> --end <YYYY-MM-DD> [--apply]
|
|
347
509
|
aq status
|
|
348
510
|
```
|
|
349
511
|
|
|
350
|
-
Every command except `aq trade-lock` is a thin `subprocess`
|
|
351
|
-
command already documented elsewhere in this README:
|
|
512
|
+
Every command except `aq trade-lock` and `aq fetch` is a thin `subprocess`
|
|
513
|
+
wrapper around a command already documented elsewhere in this README:
|
|
352
514
|
|
|
353
515
|
- **`aq train`** — runs `train.py`: builds the dataset and trains the baseline + expert models.
|
|
354
516
|
- **`aq test`** — runs the pytest suite and refreshes this README's test badge.
|
|
@@ -360,6 +522,7 @@ command already documented elsewhere in this README:
|
|
|
360
522
|
- **`aq docker build`** — rebuilds the `aether-quant` app image.
|
|
361
523
|
- **`aq retrain <stage>`** — dispatches to `python -m retraining.orchestrator <stage> ...` for a single manual pipeline stage.
|
|
362
524
|
- **`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
|
+
- **`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
526
|
- **`aq status`** — shows `git status`.
|
|
364
527
|
|
|
365
528
|
## Release Process
|
|
@@ -38,6 +38,7 @@ data/alternative/trading-economics/earnings/.keep
|
|
|
38
38
|
data/alternative/trading-economics/indicator/.keep
|
|
39
39
|
data_pipeline/README.md
|
|
40
40
|
data_pipeline/__init__.py
|
|
41
|
+
data_pipeline/fetch.py
|
|
41
42
|
data_pipeline/v2_manifest.py
|
|
42
43
|
data_pipeline/yfinance_backfill.py
|
|
43
44
|
development/Changelog.md
|
|
@@ -45,6 +46,7 @@ development/Problems.md
|
|
|
45
46
|
development/README.md
|
|
46
47
|
development/backtest_equity_chart.png
|
|
47
48
|
development/infrastructure.md
|
|
49
|
+
development/logo.png
|
|
48
50
|
development/v2_architecture.md
|
|
49
51
|
execution/README.md
|
|
50
52
|
execution/__init__.py
|
|
@@ -124,6 +126,7 @@ tests/test_experience_queue.py
|
|
|
124
126
|
tests/test_expert_datasets.py
|
|
125
127
|
tests/test_expert_models.py
|
|
126
128
|
tests/test_exported_model.py
|
|
129
|
+
tests/test_fetch.py
|
|
127
130
|
tests/test_gating_network.py
|
|
128
131
|
tests/test_generate_backtest_report.py
|
|
129
132
|
tests/test_lean_backtest.py
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"data_pipeline/README.md",
|
|
44
44
|
"data_pipeline/__init__.py",
|
|
45
45
|
"data_pipeline/v2_manifest.py",
|
|
46
|
+
"data_pipeline/fetch.py",
|
|
46
47
|
"data_pipeline/yfinance_backfill.py",
|
|
47
48
|
"visualization/README.md",
|
|
48
49
|
"inference/README.md",
|
|
@@ -152,6 +153,7 @@
|
|
|
152
153
|
"webui/src/types/state.ts",
|
|
153
154
|
"webui/public/favicon.svg",
|
|
154
155
|
"webui/public/icons.svg",
|
|
156
|
+
"development/logo.png",
|
|
155
157
|
"development/backtest_equity_chart.png",
|
|
156
158
|
"development/README.md",
|
|
157
159
|
"development/Problems.md",
|
|
@@ -209,6 +211,7 @@
|
|
|
209
211
|
"tests/test_train_pipeline.py",
|
|
210
212
|
"tests/test_experience_queue.py",
|
|
211
213
|
"tests/test_vault_commands.py",
|
|
214
|
+
"tests/test_fetch.py",
|
|
212
215
|
"data/alternative/sec/aapl/.keep",
|
|
213
216
|
"data/alternative/trading-economics/.keep",
|
|
214
217
|
"data/alternative/trading-economics/calendar/.keep",
|
|
@@ -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
|
|
7
|
-
that already exists and is already documented elsewhere
|
|
8
|
-
development/infrastructure.md) - no logic is reimplemented
|
|
9
|
-
only saves typing. `trade-lock`
|
|
10
|
-
`risk/manual_override.py`
|
|
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,11 @@ import subprocess
|
|
|
31
32
|
import sys
|
|
32
33
|
import time
|
|
33
34
|
import urllib.request
|
|
35
|
+
from datetime import date
|
|
34
36
|
from importlib.metadata import version as installed_version
|
|
35
37
|
from pathlib import Path
|
|
36
38
|
|
|
39
|
+
from data_pipeline.fetch import ASSET_CLASSES, fetch_adhoc_asset
|
|
37
40
|
from risk.manual_override import read_manual_trade_lock_override, write_manual_trade_lock_override
|
|
38
41
|
|
|
39
42
|
ROOT_DIR = Path(__file__).resolve().parent
|
|
@@ -94,6 +97,18 @@ def _parse_simple_version(value: str) -> tuple[int, ...] | None:
|
|
|
94
97
|
return None
|
|
95
98
|
|
|
96
99
|
|
|
100
|
+
def _iso_date(value: str) -> str:
|
|
101
|
+
"""Validates --start/--end as strict ISO 8601 YYYY-MM-DD, matching the
|
|
102
|
+
convention used everywhere else in this repo (config.json,
|
|
103
|
+
yfinance_backfill.py) - rejects other formats (e.g. DD.MM.YYYY) with a
|
|
104
|
+
clear error instead of a confusing downstream yfinance failure."""
|
|
105
|
+
try:
|
|
106
|
+
date.fromisoformat(value)
|
|
107
|
+
except ValueError as exc:
|
|
108
|
+
raise argparse.ArgumentTypeError(f"invalid date {value!r} - expected ISO 8601 YYYY-MM-DD") from exc
|
|
109
|
+
return value
|
|
110
|
+
|
|
111
|
+
|
|
97
112
|
def _read_update_cache() -> dict:
|
|
98
113
|
if not UPDATE_CACHE_PATH.exists():
|
|
99
114
|
return {}
|
|
@@ -340,6 +355,30 @@ def cmd_trade_lock(args: argparse.Namespace) -> int:
|
|
|
340
355
|
return 0
|
|
341
356
|
|
|
342
357
|
|
|
358
|
+
def cmd_fetch(args: argparse.Namespace) -> int:
|
|
359
|
+
report = fetch_adhoc_asset(args.asset_class, args.ticker, args.start, args.end, apply=args.apply)
|
|
360
|
+
label = "APPLY" if args.apply else "DRY RUN"
|
|
361
|
+
print(f"{label} — {report['ticker']} ({report['yahoo_symbol']}): {report['action']}, rows_fetched={report['rows_fetched']}")
|
|
362
|
+
if report["suggested_available_from"]:
|
|
363
|
+
print(f" date range fetched: {report['suggested_available_from']} .. {report['suggested_available_to']}")
|
|
364
|
+
print(f" data_path: {report['data_path']}")
|
|
365
|
+
|
|
366
|
+
if report["config_status"] == "added":
|
|
367
|
+
print(f" config.json: added a new {report['ticker']} asset block to phase1.universe.assets[]")
|
|
368
|
+
elif report["config_status"] == "already_exists":
|
|
369
|
+
print(
|
|
370
|
+
f" config.json: {report['ticker']} is already configured - left untouched. "
|
|
371
|
+
"Use data_pipeline/yfinance_backfill.py to extend an existing asset's date range instead."
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
if not args.apply:
|
|
375
|
+
print("\nDry run only — nothing was written. Re-run with --apply to write the zip file and update config.json.")
|
|
376
|
+
elif report["action"] == "written":
|
|
377
|
+
print("\nReady to prepare training: run `python train.py --dataset-only` to confirm this ticker's asset quality, then `python train.py` when ready.")
|
|
378
|
+
|
|
379
|
+
return 1 if report["action"] == "no_data_returned" else 0
|
|
380
|
+
|
|
381
|
+
|
|
343
382
|
def cmd_status(_args: argparse.Namespace) -> int:
|
|
344
383
|
return _run(["git", "status"])
|
|
345
384
|
|
|
@@ -409,6 +448,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
409
448
|
trade_lock_group.add_argument("--status", dest="status", action="store_true", help="Print the current override state")
|
|
410
449
|
trade_lock_parser.set_defaults(func=cmd_trade_lock)
|
|
411
450
|
|
|
451
|
+
fetch_parser = subparsers.add_parser(
|
|
452
|
+
"fetch", help="Ad-hoc fetch of historical OHLCV from Yahoo Finance for a ticker not yet in config.json"
|
|
453
|
+
)
|
|
454
|
+
fetch_parser.add_argument(
|
|
455
|
+
"asset_class", choices=list(ASSET_CLASSES), help="Asset class (picks the Lean data_path/market convention)"
|
|
456
|
+
)
|
|
457
|
+
fetch_parser.add_argument("--ticker", required=True, help="Internal ticker, e.g. AAPL or BTCUSD")
|
|
458
|
+
fetch_parser.add_argument("--start", required=True, type=_iso_date, help="Start date, ISO 8601 YYYY-MM-DD")
|
|
459
|
+
fetch_parser.add_argument("--end", required=True, type=_iso_date, help="End date, ISO 8601 YYYY-MM-DD")
|
|
460
|
+
fetch_parser.add_argument(
|
|
461
|
+
"--apply", action="store_true", help="Actually write the zip file and update config.json (default: dry run, report only)"
|
|
462
|
+
)
|
|
463
|
+
fetch_parser.set_defaults(func=cmd_fetch)
|
|
464
|
+
|
|
412
465
|
status_parser = subparsers.add_parser("status", help="Show git status")
|
|
413
466
|
status_parser.set_defaults(func=cmd_status)
|
|
414
467
|
|
|
@@ -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
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Ad-hoc historical OHLCV fetch for tickers not (yet) in config.json.
|
|
2
|
+
|
|
3
|
+
Distinct responsibility from yfinance_backfill.py: that module fills gaps in
|
|
4
|
+
already-configured assets via a config.json "backfill" block, and
|
|
5
|
+
deliberately never touches config.json (widening an *existing*, already
|
|
6
|
+
-trained asset's date range is a decision a human should make explicitly).
|
|
7
|
+
This module fetches an explicit date range for *any* ticker, including ones
|
|
8
|
+
with no config.json entry at all, and — on --apply — *does* add a new asset
|
|
9
|
+
block to config.json, because adding a brand-new ticker to the universe is
|
|
10
|
+
this module's entire purpose, not an accidental side effect.
|
|
11
|
+
|
|
12
|
+
Reuses yfinance_backfill.py's config.json-independent pure functions
|
|
13
|
+
(fetch_yahoo_ohlcv, scale_for_lean, write_lean_zip) - no duplicated logic.
|
|
14
|
+
|
|
15
|
+
Never runs `train.py` itself - preparing a ticker for training (fetching
|
|
16
|
+
its data, formatting it for Lean, wiring it into config.json's universe) is
|
|
17
|
+
this module's job; actually training on it stays a deliberate, separate
|
|
18
|
+
step (`python train.py`).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import json
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Callable
|
|
26
|
+
|
|
27
|
+
from data_pipeline.yfinance_backfill import fetch_yahoo_ohlcv, scale_for_lean, write_lean_zip
|
|
28
|
+
|
|
29
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
30
|
+
CONFIG_PATH = ROOT / "config.json"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _crypto_yahoo_symbol(ticker: str) -> str:
|
|
34
|
+
"""BTCUSD -> BTC-USD, DOGEUSD -> DOGE-USD: strip a trailing USD and join
|
|
35
|
+
with a hyphen, matching Yahoo Finance's crypto pair naming convention."""
|
|
36
|
+
base = ticker[:-3] if ticker.upper().endswith("USD") else ticker
|
|
37
|
+
return f"{base}-USD"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# One entry per asset class - adding "derivative" in V3 is one new entry
|
|
41
|
+
# here (plus adding "derivative" to the CLI's `choices=`), not a redesign.
|
|
42
|
+
ASSET_CLASS_CONFIG = {
|
|
43
|
+
"crypto": {
|
|
44
|
+
"security_type": "crypto",
|
|
45
|
+
"market": "coinbase",
|
|
46
|
+
"data_path_fn": lambda ticker: ROOT / "data" / "crypto" / "coinbase" / "daily" / f"{ticker.lower()}_trade.zip",
|
|
47
|
+
"yahoo_symbol_fn": _crypto_yahoo_symbol,
|
|
48
|
+
},
|
|
49
|
+
"stock": {
|
|
50
|
+
"security_type": "equity",
|
|
51
|
+
"market": "usa",
|
|
52
|
+
"data_path_fn": lambda ticker: ROOT / "data" / "equity" / "usa" / "daily" / f"{ticker.lower()}.zip",
|
|
53
|
+
"yahoo_symbol_fn": lambda ticker: ticker,
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
ASSET_CLASSES = tuple(ASSET_CLASS_CONFIG.keys())
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def add_asset_to_config(config_path: Path, asset_block: dict) -> str:
|
|
60
|
+
"""Appends asset_block to config.json's phase1.universe.assets[] unless
|
|
61
|
+
a block with the same ticker already exists there. Returns "added" or
|
|
62
|
+
"already_exists". Preserves the file's existing exact formatting
|
|
63
|
+
(4-space indent, trailing newline)."""
|
|
64
|
+
config = json.loads(config_path.read_text(encoding="utf-8"))
|
|
65
|
+
assets = config["phase1"]["universe"]["assets"]
|
|
66
|
+
|
|
67
|
+
if any(existing["ticker"] == asset_block["ticker"] for existing in assets):
|
|
68
|
+
return "already_exists"
|
|
69
|
+
|
|
70
|
+
assets.append(asset_block)
|
|
71
|
+
config_path.write_text(json.dumps(config, indent=4) + "\n", encoding="utf-8")
|
|
72
|
+
return "added"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def fetch_adhoc_asset(
|
|
76
|
+
asset_class: str,
|
|
77
|
+
ticker: str,
|
|
78
|
+
start: str,
|
|
79
|
+
end: str,
|
|
80
|
+
*,
|
|
81
|
+
apply: bool,
|
|
82
|
+
fetch_fn: Callable = fetch_yahoo_ohlcv,
|
|
83
|
+
config_path: Path = CONFIG_PATH,
|
|
84
|
+
) -> dict:
|
|
85
|
+
"""apply=False (default): dry run - fetches but writes nothing (no zip,
|
|
86
|
+
no config.json edit). apply=True: writes the Lean zip via
|
|
87
|
+
write_lean_zip(), then adds a new config.json asset block if this
|
|
88
|
+
ticker isn't already configured. fetch_fn is the injection point for
|
|
89
|
+
tests (never real yfinance in tests), matching
|
|
90
|
+
yfinance_backfill.run_backfill()'s exact convention."""
|
|
91
|
+
ticker = ticker.upper()
|
|
92
|
+
class_config = ASSET_CLASS_CONFIG[asset_class]
|
|
93
|
+
yahoo_symbol = class_config["yahoo_symbol_fn"](ticker)
|
|
94
|
+
output_zip = class_config["data_path_fn"](ticker)
|
|
95
|
+
security_type = class_config["security_type"]
|
|
96
|
+
|
|
97
|
+
rows = fetch_fn(yahoo_symbol, start, end)
|
|
98
|
+
scaled_rows = scale_for_lean(rows, security_type)
|
|
99
|
+
|
|
100
|
+
dates = [row["date"] for row in rows]
|
|
101
|
+
suggested_from = min(dates) if dates else None
|
|
102
|
+
suggested_to = max(dates) if dates else None
|
|
103
|
+
|
|
104
|
+
config_status = "not_attempted"
|
|
105
|
+
if not scaled_rows:
|
|
106
|
+
action = "no_data_returned"
|
|
107
|
+
elif apply:
|
|
108
|
+
write_lean_zip(output_zip, ticker, scaled_rows, merge_with_existing=True)
|
|
109
|
+
action = "written"
|
|
110
|
+
config_status = add_asset_to_config(
|
|
111
|
+
config_path,
|
|
112
|
+
{
|
|
113
|
+
"ticker": ticker,
|
|
114
|
+
"security_type": security_type,
|
|
115
|
+
"market": class_config["market"],
|
|
116
|
+
"data_path": output_zip.relative_to(ROOT).as_posix(),
|
|
117
|
+
"available_from": suggested_from.isoformat(),
|
|
118
|
+
"available_to": suggested_to.isoformat(),
|
|
119
|
+
},
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
action = "dry_run"
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
"ticker": ticker,
|
|
126
|
+
"asset_class": asset_class,
|
|
127
|
+
"yahoo_symbol": yahoo_symbol,
|
|
128
|
+
"data_path": str(output_zip),
|
|
129
|
+
"action": action,
|
|
130
|
+
"rows_fetched": len(rows),
|
|
131
|
+
"suggested_available_from": suggested_from.isoformat() if suggested_from else None,
|
|
132
|
+
"suggested_available_to": suggested_to.isoformat() if suggested_to else None,
|
|
133
|
+
"config_status": config_status,
|
|
134
|
+
}
|