investing-algorithm-framework 8.6.1__tar.gz → 8.7.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 (304) hide show
  1. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/PKG-INFO +5 -1
  2. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/README.md +1 -0
  3. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/__init__.py +20 -1
  4. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/eventloop.py +262 -1
  5. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/backtest_report.py +53 -7
  6. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/strategy.py +59 -0
  7. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/cli.py +70 -0
  8. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/__init__.py +42 -1
  9. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/__init__.py +19 -2
  10. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest.py +125 -3
  11. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_metrics.py +47 -19
  12. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_permutation_test.py +53 -2
  13. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_run.py +124 -103
  14. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_summary_metrics.py +8 -1
  15. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/backtesting/backtest_utils.py +713 -0
  16. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/backtesting/bundle.py +351 -0
  17. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/datetime_parsing.py +29 -0
  18. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/order/order.py +5 -2
  19. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/portfolio/portfolio_snapshot.py +5 -4
  20. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/trade/trade.py +7 -4
  21. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/trade/trade_stop_loss.py +9 -7
  22. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/trade/trade_take_profit.py +8 -5
  23. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/__init__.py +40 -0
  24. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/custom_factor.py +34 -0
  25. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/factor.py +538 -0
  26. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/factors/__init__.py +26 -0
  27. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/factors/builtin.py +496 -0
  28. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/filter.py +98 -0
  29. investing_algorithm_framework-8.7.0/investing_algorithm_framework/domain/pipeline/pipeline.py +142 -0
  30. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/backtesting/backtest_service.py +41 -17
  31. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/backtesting/event_backtest_service.py +23 -0
  32. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/backtesting/vector_backtest_service.py +80 -0
  33. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/generate.py +61 -20
  34. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/sharpe_ratio.py +6 -2
  35. investing_algorithm_framework-8.7.0/investing_algorithm_framework/services/pipeline/__init__.py +5 -0
  36. investing_algorithm_framework-8.7.0/investing_algorithm_framework/services/pipeline/pipeline_engine.py +174 -0
  37. investing_algorithm_framework-8.7.0/investing_algorithm_framework/services/pipeline/vector_pipeline_engine.py +219 -0
  38. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/pyproject.toml +4 -1
  39. investing_algorithm_framework-8.6.1/investing_algorithm_framework/domain/backtesting/backtest_utils.py +0 -297
  40. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/LICENSE +0 -0
  41. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/analysis/__init__.py +0 -0
  42. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/analysis/backtest_data_ranges.py +0 -0
  43. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/analysis/markdown.py +0 -0
  44. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/analysis/ranking.py +0 -0
  45. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/__init__.py +0 -0
  46. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/algorithm/__init__.py +0 -0
  47. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/algorithm/algorithm.py +0 -0
  48. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/algorithm/algorithm_factory.py +0 -0
  49. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/app.py +0 -0
  50. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/app_hook.py +0 -0
  51. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/context.py +0 -0
  52. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/__init__.py +0 -0
  53. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/ascii.py +0 -0
  54. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/backtest_report_old.py +0 -0
  55. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/__init__.py +0 -0
  56. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/entry_exist_signals.py +0 -0
  57. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/equity_curve.py +0 -0
  58. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/equity_curve_drawdown.py +0 -0
  59. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/line_chart.py +0 -0
  60. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/monthly_returns_heatmap.py +0 -0
  61. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/ohlcv_data_completeness.py +0 -0
  62. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/rolling_sharp_ratio.py +0 -0
  63. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/charts/yearly_returns_barchart.py +0 -0
  64. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/generate.py +0 -0
  65. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/__init__.py +0 -0
  66. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/key_metrics_table.py +0 -0
  67. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/stop_loss_table.py +0 -0
  68. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/time_metrics_table.py +0 -0
  69. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/trade_metrics_table.py +0 -0
  70. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/trades_table.py +0 -0
  71. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/tables/utils.py +0 -0
  72. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/templates/dashboard.css +0 -0
  73. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/templates/dashboard.js +0 -0
  74. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/templates/dashboard_template.html.j2 +0 -0
  75. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/reporting/templates/report_template.html.j2 +0 -0
  76. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/__init__.py +0 -0
  77. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/action_handlers/__init__.py +0 -0
  78. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/action_handlers/action_handler_strategy.py +0 -0
  79. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/action_handlers/check_online_handler.py +0 -0
  80. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/action_handlers/run_strategy_handler.py +0 -0
  81. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/stateless/exception_handler.py +0 -0
  82. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/task.py +0 -0
  83. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/__init__.py +0 -0
  84. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/controllers/__init__.py +0 -0
  85. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/controllers/orders.py +0 -0
  86. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/controllers/portfolio.py +0 -0
  87. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/controllers/positions.py +0 -0
  88. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/create_app.py +0 -0
  89. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/error_handler.py +0 -0
  90. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/responses.py +0 -0
  91. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/run_strategies.py +0 -0
  92. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/schemas/__init__.py +0 -0
  93. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/schemas/order.py +0 -0
  94. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/schemas/portfolio.py +0 -0
  95. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/schemas/position.py +0 -0
  96. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/app/web/setup_cors.py +0 -0
  97. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/__init__.py +0 -0
  98. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/deploy_to_aws_lambda.py +0 -0
  99. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/deploy_to_azure_function.py +0 -0
  100. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/initialize_app.py +0 -0
  101. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/mcp_server.py +0 -0
  102. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/.gitignore.template +0 -0
  103. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/app.py.template +0 -0
  104. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/app_aws_lambda_function.py.template +0 -0
  105. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/app_azure_function.py.template +0 -0
  106. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/app_web.py.template +0 -0
  107. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/aws_lambda_dockerfile.template +0 -0
  108. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/aws_lambda_dockerignore.template +0 -0
  109. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/aws_lambda_readme.md.template +0 -0
  110. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/aws_lambda_requirements.txt.template +0 -0
  111. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/azure_function_function_app.py.template +0 -0
  112. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/azure_function_host.json.template +0 -0
  113. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/azure_function_local.settings.json.template +0 -0
  114. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/azure_function_requirements.txt.template +0 -0
  115. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/data_providers.py.template +0 -0
  116. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/env.example.template +0 -0
  117. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/env_azure_function.example.template +0 -0
  118. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/market_data_providers.py.template +0 -0
  119. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/readme.md.template +0 -0
  120. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/requirements.txt.template +0 -0
  121. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/run_backtest.py.template +0 -0
  122. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/templates/strategy.py.template +0 -0
  123. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/cli/validate_backtest_checkpoints.py +0 -0
  124. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/create_app.py +0 -0
  125. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/dependency_container.py +0 -0
  126. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/algorithm_id.py +0 -0
  127. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_date_range.py +0 -0
  128. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/backtest_evaluation_focuss.py +0 -0
  129. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/combine_backtests.py +0 -0
  130. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/consistency.py +0 -0
  131. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/templates/backtest.html +0 -0
  132. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/templates/finterion-dark.png +0 -0
  133. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/backtesting/templates/finterion-light.png +0 -0
  134. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/blotter.py +0 -0
  135. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/config.py +0 -0
  136. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/constants.py +0 -0
  137. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/data_provider.py +0 -0
  138. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/data_structures.py +0 -0
  139. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/decimal_parsing.py +0 -0
  140. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/exceptions.py +0 -0
  141. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/fx.py +0 -0
  142. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/__init__.py +0 -0
  143. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/app_mode.py +0 -0
  144. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/base_model.py +0 -0
  145. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/data/__init__.py +0 -0
  146. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/data/data_source.py +0 -0
  147. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/data/data_type.py +0 -0
  148. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/event.py +0 -0
  149. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/market/__init__.py +0 -0
  150. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/market/market_credential.py +0 -0
  151. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/order/__init__.py +0 -0
  152. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/order/order_side.py +0 -0
  153. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/order/order_status.py +0 -0
  154. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/order/order_type.py +0 -0
  155. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/portfolio/__init__.py +0 -0
  156. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/portfolio/portfolio.py +0 -0
  157. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/portfolio/portfolio_configuration.py +0 -0
  158. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/position/__init__.py +0 -0
  159. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/position/position.py +0 -0
  160. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/position/position_size.py +0 -0
  161. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/position/position_snapshot.py +0 -0
  162. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/risk_rules/__init__.py +0 -0
  163. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/risk_rules/scaling_rule.py +0 -0
  164. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/risk_rules/stop_loss_rule.py +0 -0
  165. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/risk_rules/take_profit_rule.py +0 -0
  166. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/risk_rules/trading_cost.py +0 -0
  167. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/snapshot_interval.py +0 -0
  168. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/strategy_profile.py +0 -0
  169. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/time_frame.py +0 -0
  170. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/time_interval.py +0 -0
  171. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/time_unit.py +0 -0
  172. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/tracing/__init__.py +0 -0
  173. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/tracing/trace.py +0 -0
  174. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/trade/__init__.py +0 -0
  175. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/models/trade/trade_status.py +0 -0
  176. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/order_executor.py +0 -0
  177. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/portfolio_provider.py +0 -0
  178. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/__init__.py +0 -0
  179. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/market_credential_service.py +0 -0
  180. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/portfolios/__init__.py +0 -0
  181. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/portfolios/portfolio_sync_service.py +0 -0
  182. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/rounding_service.py +0 -0
  183. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/services/state_handler.py +0 -0
  184. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/stateless_actions.py +0 -0
  185. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/strategy.py +0 -0
  186. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/__init__.py +0 -0
  187. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/csv.py +0 -0
  188. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/custom_tqdm.py +0 -0
  189. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/dates.py +0 -0
  190. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/jupyter_notebook_detection.py +0 -0
  191. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/polars.py +0 -0
  192. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/random.py +0 -0
  193. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/signatures.py +0 -0
  194. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/stoppable_thread.py +0 -0
  195. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/domain/utils/synchronized.py +0 -0
  196. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/download_data.py +0 -0
  197. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/__init__.py +0 -0
  198. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/__init__.py +0 -0
  199. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/alpha_vantage.py +0 -0
  200. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/base_url.py +0 -0
  201. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/ccxt.py +0 -0
  202. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/csv.py +0 -0
  203. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/csv_url.py +0 -0
  204. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/json_url.py +0 -0
  205. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/ohlcv_base.py +0 -0
  206. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/pandas.py +0 -0
  207. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/parquet_url.py +0 -0
  208. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/polygon.py +0 -0
  209. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/data_providers/yahoo.py +0 -0
  210. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/database/__init__.py +0 -0
  211. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/database/sql_alchemy.py +0 -0
  212. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/__init__.py +0 -0
  213. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/decimal_parser.py +0 -0
  214. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/model_extension.py +0 -0
  215. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/order/__init__.py +0 -0
  216. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/order/order.py +0 -0
  217. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/order/trade_allocation.py +0 -0
  218. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/order_trade_association.py +0 -0
  219. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/portfolio/__init__.py +0 -0
  220. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/portfolio/portfolio_snapshot.py +0 -0
  221. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/portfolio/sql_portfolio.py +0 -0
  222. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/position/__init__.py +0 -0
  223. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/position/position.py +0 -0
  224. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/position/position_snapshot.py +0 -0
  225. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/trades/__init__.py +0 -0
  226. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/trades/trade.py +0 -0
  227. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/trades/trade_stop_loss.py +0 -0
  228. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/models/trades/trade_take_profit.py +0 -0
  229. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/order_executors/__init__.py +0 -0
  230. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/order_executors/ccxt_order_executor.py +0 -0
  231. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/portfolio_providers/__init__.py +0 -0
  232. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/portfolio_providers/ccxt_portfolio_provider.py +0 -0
  233. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/__init__.py +0 -0
  234. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/order_repository.py +0 -0
  235. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/portfolio_repository.py +0 -0
  236. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/portfolio_snapshot_repository.py +0 -0
  237. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/position_repository.py +0 -0
  238. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/position_snapshot_repository.py +0 -0
  239. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/repository.py +0 -0
  240. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/trade_allocation_repository.py +0 -0
  241. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/trade_repository.py +0 -0
  242. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/trade_stop_loss_repository.py +0 -0
  243. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/repositories/trade_take_profit_repository.py +0 -0
  244. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/__init__.py +0 -0
  245. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/aws/__init__.py +0 -0
  246. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/aws/state_handler.py +0 -0
  247. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/azure/__init__.py +0 -0
  248. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/azure/state_handler.py +0 -0
  249. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/backtesting/__init__.py +0 -0
  250. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/infrastructure/services/backtesting/checkpoint_manifest.py +0 -0
  251. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/notebook/__init__.py +0 -0
  252. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/notebook/magic.py +0 -0
  253. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/__init__.py +0 -0
  254. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/configuration_service.py +0 -0
  255. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/data_providers/__init__.py +0 -0
  256. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/data_providers/data.py +0 -0
  257. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/data_providers/data_provider_service.py +0 -0
  258. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/market_credential_service.py +0 -0
  259. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/__init__.py +0 -0
  260. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/alpha.py +0 -0
  261. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/beta.py +0 -0
  262. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/cagr.py +0 -0
  263. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/calmar_ratio.py +0 -0
  264. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/consistency.py +0 -0
  265. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/drawdown.py +0 -0
  266. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/equity_curve.py +0 -0
  267. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/exposure.py +0 -0
  268. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/mean_daily_return.py +0 -0
  269. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/price_efficiency.py +0 -0
  270. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/profit_factor.py +0 -0
  271. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/recovery.py +0 -0
  272. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/returns.py +0 -0
  273. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/risk_free_rate.py +0 -0
  274. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/sortino_ratio.py +0 -0
  275. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/standard_deviation.py +0 -0
  276. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/trades.py +0 -0
  277. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/treynor_ratio.py +0 -0
  278. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/ulcer.py +0 -0
  279. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/value_at_risk.py +0 -0
  280. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/volatility.py +0 -0
  281. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/metrics/win_rate.py +0 -0
  282. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/order_service/__init__.py +0 -0
  283. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/order_service/order_backtest_service.py +0 -0
  284. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/order_service/order_executor_lookup.py +0 -0
  285. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/order_service/order_service.py +0 -0
  286. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/__init__.py +0 -0
  287. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/backtest_portfolio_service.py +0 -0
  288. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/portfolio_configuration_service.py +0 -0
  289. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/portfolio_provider_lookup.py +0 -0
  290. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/portfolio_service.py +0 -0
  291. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/portfolio_snapshot_service.py +0 -0
  292. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/portfolios/portfolio_sync_service.py +0 -0
  293. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/positions/__init__.py +0 -0
  294. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/positions/position_service.py +0 -0
  295. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/positions/position_snapshot_service.py +0 -0
  296. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/repository_service.py +0 -0
  297. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_order_evaluator/__init__.py +0 -0
  298. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_order_evaluator/backtest_trade_oder_evaluator.py +0 -0
  299. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_order_evaluator/default_trade_order_evaluator.py +0 -0
  300. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_order_evaluator/trade_order_evaluator.py +0 -0
  301. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_service/__init__.py +0 -0
  302. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_service/trade_service.py +0 -0
  303. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_service/trade_stop_loss_service.py +0 -0
  304. {investing_algorithm_framework-8.6.1 → investing_algorithm_framework-8.7.0}/investing_algorithm_framework/services/trade_service/trade_take_profit_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: investing-algorithm-framework
3
- Version: 8.6.1
3
+ Version: 8.7.0
4
4
  Summary: A framework for creating trading bots
5
5
  Author: MDUYN
6
6
  Requires-Python: >=3.10,<4.0
@@ -27,9 +27,11 @@ Requires-Dist: ccxt (>=4.2.48)
27
27
  Requires-Dist: dependency-injector (>=4.40.0)
28
28
  Requires-Dist: jupyter (>=1.0.0)
29
29
  Requires-Dist: marshmallow (>=3.5.0)
30
+ Requires-Dist: msgpack (>=1.0.0)
30
31
  Requires-Dist: plotly (>=6.1.2,<7.0.0)
31
32
  Requires-Dist: polars[numpy,pandas] (>=0.20.10)
32
33
  Requires-Dist: polygon-api-client (>=1.14.0,<2.0.0) ; extra == "polygon" or extra == "all"
34
+ Requires-Dist: pyarrow (>=14.0.0)
33
35
  Requires-Dist: python-dateutil (>=2.8.2)
34
36
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
35
37
  Requires-Dist: schedule (>=1.1.0)
@@ -37,6 +39,7 @@ Requires-Dist: tabulate (>=0.9.0)
37
39
  Requires-Dist: tqdm (>=4.66.1)
38
40
  Requires-Dist: wrapt (>=1.16.0)
39
41
  Requires-Dist: yfinance (>=0.2.61,<0.3.0) ; extra == "yahoo" or extra == "all"
42
+ Requires-Dist: zstandard (>=0.20.0)
40
43
  Description-Content-Type: text/markdown
41
44
 
42
45
  <h1 align="center">
@@ -124,6 +127,7 @@ This framework is built around the full loop: **create strategies → vector bac
124
127
  - 🎯 **Return Scenario Projections** — Good, average, bad & very bad year projections from backtest data
125
128
  - 📉 **Benchmark Comparison** — Beat-rate analysis vs Buy & Hold, DCA, risk-free & custom benchmarks
126
129
  - 📄 **One-Click HTML Report** — Self-contained file, no server, dark & light theme, shareable
130
+ - 📦 **Custom `.iafbt` Backtest Bundle Format** — An explicit, versioned, compressed, language-portable container (zstd + msgpack with magic-byte header) plus a separate parquet index for fast filtering without loading. ~21× smaller and ~27× fewer files than standard filebased directory layouts, with parallel I/O for fast load/save of large amounts of backtests.
127
131
  - 🌐 **Load External Data** — Fetch CSV, JSON, or Parquet from any URL with caching and auto-refresh
128
132
  - � **[Record Custom Variables](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/recording-variables)** — Track any indicator or metric during backtests with `context.record()`
129
133
  - �🚀 **Build → Backtest → Deploy** — Local dev, cloud deploy (AWS / Azure), or monetize on Finterion
@@ -83,6 +83,7 @@ This framework is built around the full loop: **create strategies → vector bac
83
83
  - 🎯 **Return Scenario Projections** — Good, average, bad & very bad year projections from backtest data
84
84
  - 📉 **Benchmark Comparison** — Beat-rate analysis vs Buy & Hold, DCA, risk-free & custom benchmarks
85
85
  - 📄 **One-Click HTML Report** — Self-contained file, no server, dark & light theme, shareable
86
+ - 📦 **Custom `.iafbt` Backtest Bundle Format** — An explicit, versioned, compressed, language-portable container (zstd + msgpack with magic-byte header) plus a separate parquet index for fast filtering without loading. ~21× smaller and ~27× fewer files than standard filebased directory layouts, with parallel I/O for fast load/save of large amounts of backtests.
86
87
  - 🌐 **Load External Data** — Fetch CSV, JSON, or Parquet from any URL with caching and auto-refresh
87
88
  - � **[Record Custom Variables](https://coding-kitties.github.io/investing-algorithm-framework/Advanced%20Concepts/recording-variables)** — Track any indicator or metric during backtests with `context.record()`
88
89
  - �🚀 **Build → Backtest → Deploy** — Local dev, cloud deploy (AWS / Azure), or monetize on Finterion
@@ -30,7 +30,11 @@ from .domain import ApiException, combine_backtests, PositionSize, \
30
30
  VolumeImpactSlippage, \
31
31
  CommissionModel, NoCommission, PercentageCommission, FixedCommission, \
32
32
  FillModel, FullFill, VolumeBasedFill, \
33
- FXRateProvider, StaticFXRateProvider
33
+ FXRateProvider, StaticFXRateProvider, \
34
+ Pipeline, Factor, CustomFactor, Filter, \
35
+ Returns, AverageDollarVolume, AverageTradedValue, SMA, RSI, \
36
+ Volatility, StaticPerSymbol, CrossSectionalMean, RollingBeta, \
37
+ Neutralize
34
38
  from .infrastructure import AzureBlobStorageStateHandler, \
35
39
  CSVOHLCVDataProvider, CSVTickerDataProvider, CSVURLDataProvider, \
36
40
  JSONURLDataProvider, ParquetURLDataProvider, \
@@ -256,6 +260,21 @@ __all__ = [
256
260
  "VolumeBasedFill",
257
261
  "FXRateProvider",
258
262
  "StaticFXRateProvider",
263
+ # Pipeline API (Phase 1, see docs/design/pipeline-api.md)
264
+ "Pipeline",
265
+ "Factor",
266
+ "CustomFactor",
267
+ "Filter",
268
+ "Returns",
269
+ "AverageDollarVolume",
270
+ "AverageTradedValue",
271
+ "SMA",
272
+ "RSI",
273
+ "Volatility",
274
+ "StaticPerSymbol",
275
+ "CrossSectionalMean",
276
+ "RollingBeta",
277
+ "Neutralize",
259
278
  "load_ipython_extension",
260
279
  "get_cv_consistency",
261
280
  "get_normalized_stability",
@@ -1,6 +1,6 @@
1
1
  from datetime import datetime, timedelta, timezone
2
2
  from time import sleep
3
- from typing import List, Set, Dict
3
+ from typing import List, Set, Dict, FrozenSet, Tuple, Optional, Type
4
4
  from logging import getLogger
5
5
 
6
6
  import polars as pl
@@ -10,6 +10,7 @@ from investing_algorithm_framework.domain import Environment, ENVIRONMENT, \
10
10
  TradeStatus, SNAPSHOT_INTERVAL, SnapshotInterval, OperationalException, \
11
11
  LAST_SNAPSHOT_DATETIME, INDEX_DATETIME
12
12
  from investing_algorithm_framework.services import TradeOrderEvaluator
13
+ from investing_algorithm_framework.services.pipeline import PipelineEngine
13
14
 
14
15
  from .algorithm import Algorithm
15
16
  from .strategy import TradingStrategy
@@ -82,6 +83,19 @@ class EventLoopService:
82
83
  self.data_sources = set()
83
84
  self.next_run_times = {}
84
85
  self.history = {}
86
+ self._pipeline_engine = PipelineEngine()
87
+
88
+ # Per (strategy_id, pipeline_cls) cache of the most recent
89
+ # universe-refresh: (refresh_at, frozen surviving-symbol set).
90
+ # Populated when a pipeline declares
91
+ # ``refresh_universe_every`` so the engine can skip re-running
92
+ # the universe filter every bar.
93
+ self._pipeline_universe_cache: Dict[
94
+ Tuple[str, Type], Tuple[datetime, FrozenSet[str]]
95
+ ] = {}
96
+ # One-shot flag: live-mode envelope validation runs once per
97
+ # process. Reset by ``cleanup`` so a new run re-validates.
98
+ self._pipelines_live_validated: bool = False
85
99
 
86
100
  @staticmethod
87
101
  def _get_data_sources_for_iteration(
@@ -330,6 +344,11 @@ class EventLoopService:
330
344
  None
331
345
  """
332
346
  self._portfolio_snapshot_service.save_all(self._snapshots)
347
+ # Reset per-run pipeline state so a subsequent run re-runs
348
+ # live envelope validation and starts with a fresh universe
349
+ # cache.
350
+ self._pipeline_universe_cache.clear()
351
+ self._pipelines_live_validated = False
333
352
 
334
353
  def start(
335
354
  self,
@@ -443,6 +462,232 @@ class EventLoopService:
443
462
 
444
463
  self.cleanup()
445
464
 
465
+ # ------------------------------------------------------------------ #
466
+ # Pipeline live-mode helpers (#503 phase 3b/3c/3d)
467
+ # ------------------------------------------------------------------ #
468
+ # Envelope: v1 of live pipelines supports daily timeframes only and
469
+ # caps universes at 50 symbols per pipeline. Any sub-daily timeframe
470
+ # on a strategy that declares pipelines, or a strategy whose total
471
+ # OHLCV symbol set exceeds the cap, raises at first iteration when
472
+ # running outside backtest mode. See #503.
473
+ _LIVE_MAX_PIPELINE_SYMBOLS: int = 50
474
+ _LIVE_MIN_TIMEFRAME_MINUTES: int = 24 * 60 # daily
475
+
476
+ def _validate_live_envelope(
477
+ self, strategies: List[TradingStrategy]
478
+ ) -> None:
479
+ """Validate the v1 live-pipeline envelope (max 50 symbols /
480
+ daily-or-coarser timeframes). Called once per run when env is
481
+ not BACKTEST. Raises :class:`OperationalException` on
482
+ violation."""
483
+ for strategy in strategies or []:
484
+ pipelines = getattr(strategy, "pipelines", None)
485
+ if not pipelines:
486
+ continue
487
+
488
+ ohlcv_sources = [
489
+ ds for ds in (strategy.data_sources or [])
490
+ if DataType.OHLCV.equals(ds.data_type)
491
+ ]
492
+
493
+ sub_daily = [
494
+ ds for ds in ohlcv_sources
495
+ if ds.time_frame is not None
496
+ and ds.time_frame.amount_of_minutes
497
+ < self._LIVE_MIN_TIMEFRAME_MINUTES
498
+ ]
499
+ if sub_daily:
500
+ desc = ", ".join(
501
+ f"{ds.symbol}@{ds.time_frame.value}"
502
+ for ds in sub_daily
503
+ )
504
+ raise OperationalException(
505
+ f"Strategy '{strategy.strategy_id}' declares "
506
+ f"pipelines but uses sub-daily OHLCV timeframes "
507
+ f"in live mode: {desc}. v1 of the live pipeline "
508
+ f"engine supports daily timeframes only — see "
509
+ f"#503. Use a daily timeframe or run the strategy "
510
+ f"in backtest mode."
511
+ )
512
+
513
+ unique_symbols = {ds.symbol for ds in ohlcv_sources}
514
+ unique_symbols.discard(None)
515
+ if len(unique_symbols) > self._LIVE_MAX_PIPELINE_SYMBOLS:
516
+ raise OperationalException(
517
+ f"Strategy '{strategy.strategy_id}' declares "
518
+ f"pipelines over {len(unique_symbols)} symbols, "
519
+ f"which exceeds the v1 live cap of "
520
+ f"{self._LIVE_MAX_PIPELINE_SYMBOLS}. Reduce the "
521
+ f"universe or run the strategy in backtest mode "
522
+ f"(see #503)."
523
+ )
524
+
525
+ def _maybe_validate_live_envelope(
526
+ self, strategies: List[TradingStrategy], environment: str
527
+ ) -> None:
528
+ """One-shot envelope validation; no-op for BACKTEST mode."""
529
+ if self._pipelines_live_validated:
530
+ return
531
+ if Environment.BACKTEST.equals(environment):
532
+ self._pipelines_live_validated = True
533
+ return
534
+ self._validate_live_envelope(strategies)
535
+ self._pipelines_live_validated = True
536
+
537
+ def _filter_symbols_for_universe_cache(
538
+ self,
539
+ strategy_id: str,
540
+ pipeline_cls: Type,
541
+ symbol_to_identifier: Dict[str, str],
542
+ as_of: datetime,
543
+ ) -> Optional[Dict[str, str]]:
544
+ """If ``pipeline_cls`` declares ``refresh_universe_every`` and
545
+ we have a cached surviving-symbol set still inside the cadence,
546
+ return a restricted ``symbol_to_identifier`` mapping. Returning
547
+ ``None`` means "no cache hit — run a full universe evaluation".
548
+ """
549
+ cadence: Optional[timedelta] = getattr(
550
+ pipeline_cls, "refresh_universe_every", None
551
+ )
552
+ if cadence is None or cadence <= timedelta(0):
553
+ return None
554
+
555
+ cache_key = (strategy_id, pipeline_cls)
556
+ cached = self._pipeline_universe_cache.get(cache_key)
557
+ if cached is None:
558
+ return None
559
+
560
+ last_refresh, symbols = cached
561
+ # Normalise tz so naive backtest datetimes and aware live
562
+ # datetimes compare cleanly.
563
+ if last_refresh.tzinfo is None and as_of.tzinfo is not None:
564
+ cmp_as_of = as_of.replace(tzinfo=None)
565
+ elif last_refresh.tzinfo is not None and as_of.tzinfo is None:
566
+ cmp_as_of = as_of.replace(tzinfo=last_refresh.tzinfo)
567
+ else:
568
+ cmp_as_of = as_of
569
+ if cmp_as_of - last_refresh >= cadence:
570
+ return None # cadence elapsed → refresh
571
+
572
+ # Cache hit: restrict the symbol set, skipping the universe
573
+ # filter entirely on this iteration.
574
+ return {
575
+ sym: ident
576
+ for sym, ident in symbol_to_identifier.items()
577
+ if sym in symbols
578
+ }
579
+
580
+ def _run_pipelines(
581
+ self,
582
+ strategy: TradingStrategy,
583
+ data: Dict,
584
+ data_object: Dict,
585
+ as_of: datetime,
586
+ ) -> None:
587
+ """Compute cross-sectional pipelines attached to ``strategy``
588
+ and inject their outputs into ``data`` keyed by pipeline
589
+ class name.
590
+
591
+ Strategies without ``pipelines`` skip this entirely (zero cost).
592
+
593
+ Live-mode hardening (#503):
594
+
595
+ * The v1 envelope (max 50 symbols, daily-or-coarser timeframes)
596
+ is validated once per run.
597
+ * Pipelines that declare ``refresh_universe_every`` reuse the
598
+ last surviving symbol set within the cadence — saving the
599
+ cost of evaluating the universe filter every bar.
600
+ * In non-backtest environments, a single failing pipeline is
601
+ logged and skipped (the iteration continues with an empty
602
+ output) instead of killing the whole event loop. Backtests
603
+ keep raising so failures stay deterministic.
604
+ """
605
+ pipelines = getattr(strategy, "pipelines", None)
606
+ if not pipelines:
607
+ return
608
+
609
+ config = self._configuration_service.get_config()
610
+ environment = config[ENVIRONMENT]
611
+ is_backtest = Environment.BACKTEST.equals(environment)
612
+
613
+ # Map symbol -> data-source identifier from the strategy's
614
+ # OHLCV data sources. If a symbol appears on multiple data
615
+ # sources (e.g. multiple timeframes) the first OHLCV match wins.
616
+ symbol_to_identifier: Dict[str, str] = {}
617
+ for ds in strategy.data_sources or []:
618
+ if not DataType.OHLCV.equals(ds.data_type):
619
+ continue
620
+ if ds.symbol is None or ds.symbol in symbol_to_identifier:
621
+ continue
622
+ symbol_to_identifier[ds.symbol] = ds.get_identifier()
623
+
624
+ if not symbol_to_identifier:
625
+ logger.warning(
626
+ "Strategy %s declares pipelines but has no OHLCV data "
627
+ "sources to feed them; pipelines will be skipped.",
628
+ strategy.strategy_id,
629
+ )
630
+ return
631
+
632
+ for pipeline_cls in pipelines:
633
+ # 3c: universe-refresh cache. If the pipeline declares a
634
+ # refresh cadence and we're inside it, restrict the panel
635
+ # input to the cached symbols.
636
+ cached_mapping = self._filter_symbols_for_universe_cache(
637
+ strategy_id=strategy.strategy_id,
638
+ pipeline_cls=pipeline_cls,
639
+ symbol_to_identifier=symbol_to_identifier,
640
+ as_of=as_of,
641
+ )
642
+ mapping = (
643
+ cached_mapping
644
+ if cached_mapping is not None
645
+ else symbol_to_identifier
646
+ )
647
+
648
+ try:
649
+ output = self._pipeline_engine.evaluate(
650
+ pipeline_cls=pipeline_cls,
651
+ data_object=data_object,
652
+ symbol_to_identifier=mapping,
653
+ as_of=as_of,
654
+ )
655
+ except Exception:
656
+ # 3d: live-mode resilience. In live trading a single
657
+ # pipeline failure must not kill the iteration —
658
+ # surface an empty frame and log so the rest of the
659
+ # strategies can still run. Backtests re-raise so
660
+ # failures stay deterministic.
661
+ logger.exception(
662
+ "Pipeline %s failed during evaluation at %s",
663
+ pipeline_cls.__name__,
664
+ as_of,
665
+ )
666
+ if is_backtest:
667
+ raise
668
+ output = self._pipeline_engine._empty_output(
669
+ pipeline_cls
670
+ )
671
+
672
+ # 3c: refresh the universe cache when we just ran a full
673
+ # evaluation (cached_mapping was None) and the pipeline
674
+ # declares a cadence.
675
+ cadence = getattr(
676
+ pipeline_cls, "refresh_universe_every", None
677
+ )
678
+ if (
679
+ cadence is not None
680
+ and cadence > timedelta(0)
681
+ and cached_mapping is None
682
+ and "symbol" in output.columns
683
+ ):
684
+ surviving = frozenset(output["symbol"].to_list())
685
+ self._pipeline_universe_cache[
686
+ (strategy.strategy_id, pipeline_cls)
687
+ ] = (as_of, surviving)
688
+
689
+ data[pipeline_cls.__name__] = output
690
+
446
691
  def _run_iteration(
447
692
  self,
448
693
  strategies: List[TradingStrategy] = None,
@@ -470,6 +715,11 @@ class EventLoopService:
470
715
  environment = config[ENVIRONMENT]
471
716
  current_datetime = config[INDEX_DATETIME]
472
717
 
718
+ # Validate the live-pipeline envelope (max 50 symbols /
719
+ # daily-or-coarser timeframes) once per run. No-op for
720
+ # backtests. See #503 phase 3b.
721
+ self._maybe_validate_live_envelope(strategies, environment)
722
+
473
723
  # Step 1: Collect all data for the strategies and for the
474
724
  # pending orders
475
725
  open_orders = self._order_service.get_all(
@@ -553,6 +803,17 @@ class EventLoopService:
553
803
  else:
554
804
  data = {}
555
805
 
806
+ # Step 5b: Run any cross-sectional pipelines attached to
807
+ # the strategy (Phase 1 of the Pipeline API, see
808
+ # docs/design/pipeline-api.md). Strategies without
809
+ # ``pipelines`` skip this entirely.
810
+ self._run_pipelines(
811
+ strategy=strategy,
812
+ data=data,
813
+ data_object=data_object,
814
+ as_of=current_datetime,
815
+ )
816
+
556
817
  for on_strategy_run_hook in \
557
818
  self._algorithm.on_strategy_run_hooks:
558
819
  on_strategy_run_hook.execute(
@@ -12,7 +12,7 @@ from datetime import datetime, timedelta
12
12
  from jinja2 import Environment, FileSystemLoader
13
13
 
14
14
  from investing_algorithm_framework.domain import (
15
- Backtest, OperationalException, tqdm
15
+ Backtest, OperationalException, tqdm, BUNDLE_EXT
16
16
  )
17
17
 
18
18
  logger = logging.getLogger("investing_algorithm_framework")
@@ -217,9 +217,15 @@ class BacktestReport:
217
217
 
218
218
  @staticmethod
219
219
  def _is_backtest(backtest_path):
220
+ if not os.path.exists(backtest_path):
221
+ return False
222
+ # Bundle file (.iafbt)
223
+ if os.path.isfile(backtest_path) and \
224
+ backtest_path.endswith(BUNDLE_EXT):
225
+ return True
226
+ # Legacy directory layout
220
227
  return (
221
- os.path.exists(backtest_path)
222
- and os.path.isdir(backtest_path)
228
+ os.path.isdir(backtest_path)
223
229
  and os.path.isfile(
224
230
  os.path.join(backtest_path, "algorithm_id.json")
225
231
  )
@@ -231,6 +237,7 @@ class BacktestReport:
231
237
  backtests: List[Backtest] = None,
232
238
  directory_path: Union[str, List[str], None] = None,
233
239
  show_progress: bool = False,
240
+ workers: Union[int, None] = None,
234
241
  ) -> "BacktestReport":
235
242
  loaded = []
236
243
  source_tags = []
@@ -254,7 +261,7 @@ class BacktestReport:
254
261
  if BacktestReport._is_backtest(dp):
255
262
  backtest_paths.append((dp, tag))
256
263
  else:
257
- for root, dirs, _ in os.walk(dp):
264
+ for root, dirs, files in os.walk(dp):
258
265
  for dir_name in dirs:
259
266
  subdir = os.path.join(
260
267
  root, dir_name
@@ -265,6 +272,11 @@ class BacktestReport:
265
272
  backtest_paths.append(
266
273
  (subdir, tag)
267
274
  )
275
+ for file_name in files:
276
+ if file_name.endswith(BUNDLE_EXT):
277
+ backtest_paths.append(
278
+ (os.path.join(root, file_name), tag)
279
+ )
268
280
 
269
281
  iterator = backtest_paths
270
282
  if show_progress:
@@ -273,9 +285,43 @@ class BacktestReport:
273
285
  desc="Loading backtests",
274
286
  )
275
287
 
276
- for path, tag in iterator:
277
- loaded.append(Backtest.open(path))
278
- source_tags.append(tag)
288
+ # Parallel load is opt-in (workers > 1). ProcessPoolExecutor
289
+ # startup costs typically dwarf the per-backtest decode for
290
+ # batches < ~30, so keep default behaviour serial. Pass
291
+ # ``workers=N`` (or ``-1`` for cpu_count) to load large
292
+ # batches in parallel.
293
+ from investing_algorithm_framework.domain.backtesting.\
294
+ backtest_utils import (
295
+ _load_one_dispatch, _resolve_workers,
296
+ )
297
+
298
+ n = len(backtest_paths)
299
+ resolved_workers = (
300
+ _resolve_workers(workers) if workers is not None else 1
301
+ )
302
+ if resolved_workers > 1 and n >= 4:
303
+ from concurrent.futures import ProcessPoolExecutor
304
+
305
+ items = [
306
+ (path, "bundle" if path.endswith(BUNDLE_EXT) else "dir")
307
+ for path, _ in backtest_paths
308
+ ]
309
+ with ProcessPoolExecutor(max_workers=resolved_workers) as ex:
310
+ results = list(
311
+ tqdm(
312
+ ex.map(_load_one_dispatch, items),
313
+ total=n,
314
+ desc="Loading backtests",
315
+ disable=not show_progress,
316
+ )
317
+ )
318
+ for bt, (_, tag) in zip(results, backtest_paths):
319
+ loaded.append(bt)
320
+ source_tags.append(tag)
321
+ else:
322
+ for path, tag in iterator:
323
+ loaded.append(Backtest.open(path))
324
+ source_tags.append(tag)
279
325
 
280
326
  for bt in backtests:
281
327
  if not isinstance(bt, Backtest):
@@ -48,6 +48,7 @@ class TradingStrategy:
48
48
  strategy_id: str = None
49
49
  decorated = None
50
50
  data_sources: List[DataSource] = []
51
+ pipelines: List[type] = []
51
52
  traces = None
52
53
  context: Context = None
53
54
  metadata: Dict[str, Any] = None
@@ -131,6 +132,13 @@ class TradingStrategy:
131
132
  self.data_sources = list(class_data_sources) \
132
133
  if class_data_sources else []
133
134
 
135
+ # Initialize pipelines as a new list per instance. Pipelines
136
+ # are class-attribute opt-in (Phase 1 of the Pipeline API,
137
+ # see docs/design/pipeline-api.md). When empty, the engine
138
+ # is never invoked and there is zero behavioural change.
139
+ class_pipelines = getattr(self.__class__, 'pipelines', [])
140
+ self.pipelines = list(class_pipelines) if class_pipelines else []
141
+
134
142
  if decorated is not None:
135
143
  self.decorated = decorated
136
144
 
@@ -186,6 +194,57 @@ class TradingStrategy:
186
194
  f"interval or use a smaller data timeframe."
187
195
  )
188
196
 
197
+ # Validate that every declared pipeline has enough warmup on
198
+ # the strategy's OHLCV data sources to compute its longest
199
+ # factor window. Without this check a live strategy starts
200
+ # silently emitting NaN columns until enough bars accrue.
201
+ # Tracked under #503 (Phase 3 — live hardening).
202
+ if self.pipelines:
203
+ ohlcv_sources = [
204
+ ds for ds in self.data_sources
205
+ if DataType.OHLCV.equals(ds.data_type)
206
+ ]
207
+ if not ohlcv_sources:
208
+ raise OperationalException(
209
+ f"Strategy '{self.strategy_id}' declares "
210
+ f"{len(self.pipelines)} pipeline(s) but has no "
211
+ f"OHLCV data sources. Pipelines build their panel "
212
+ f"from the strategy's OHLCV data sources — add at "
213
+ f"least one DataSource(data_type=DataType.OHLCV, "
214
+ f"...) to data_sources."
215
+ )
216
+ for pipeline_cls in self.pipelines:
217
+ required = pipeline_cls.required_window()
218
+ # We compare against each OHLCV source's warmup_window
219
+ # (canonical) / window_size (legacy alias) — the
220
+ # framework keeps them in sync, but a source may
221
+ # legitimately leave both unset, which we treat as
222
+ # "insufficient" because the pipeline cannot guarantee
223
+ # a full window of data.
224
+ short_sources = [
225
+ ds for ds in ohlcv_sources
226
+ if ds.warmup_window is None
227
+ or ds.warmup_window < required
228
+ ]
229
+ if short_sources:
230
+ short_desc = ", ".join(
231
+ f"{ds.symbol}@{ds.time_frame.value if ds.time_frame else '?'}" # noqa: E501
232
+ f" (warmup_window="
233
+ f"{ds.warmup_window if ds.warmup_window is not None else 'unset'})" # noqa: E501
234
+ for ds in short_sources
235
+ )
236
+ raise OperationalException(
237
+ f"Strategy '{self.strategy_id}' pipeline "
238
+ f"'{pipeline_cls.__name__}' requires a "
239
+ f"warmup window of {required} bars but the "
240
+ f"following OHLCV data source(s) have an "
241
+ f"insufficient warmup_window: {short_desc}. "
242
+ f"Set warmup_window>={required} on each OHLCV "
243
+ f"DataSource the pipeline relies on, otherwise "
244
+ f"the pipeline will emit NaN columns until "
245
+ f"enough bars accrue."
246
+ )
247
+
189
248
  # Initialize stop_losses as a new list per instance
190
249
  if stop_losses is not None:
191
250
  self.stop_losses = list(stop_losses)
@@ -250,3 +250,73 @@ def mcp(directory):
250
250
 
251
251
 
252
252
  cli.add_command(mcp)
253
+
254
+
255
+ @click.command(name="migrate-backtests")
256
+ @click.option(
257
+ "--src", "-s",
258
+ required=True,
259
+ type=click.Path(exists=True, file_okay=False, dir_okay=True),
260
+ help="Source directory containing legacy backtest sub-directories.",
261
+ )
262
+ @click.option(
263
+ "--dst", "-d",
264
+ required=True,
265
+ type=click.Path(file_okay=False, dir_okay=True),
266
+ help="Destination directory for the new ``.iafbt`` bundle files.",
267
+ )
268
+ @click.option(
269
+ "--workers", "-w", type=int, default=None,
270
+ help="Number of parallel workers (default: min(8, CPU count)).",
271
+ )
272
+ @click.option(
273
+ "--no-index", is_flag=True, default=False,
274
+ help="Skip writing index.parquet at the destination.",
275
+ )
276
+ @click.option(
277
+ "--include-ohlcv", is_flag=True, default=False,
278
+ help="Include OHLCV data in the destination bundles.",
279
+ )
280
+ @click.option(
281
+ "--no-skip-existing", is_flag=True, default=False,
282
+ help="Re-migrate even if the destination bundle already exists.",
283
+ )
284
+ @click.option(
285
+ "--delete-source", is_flag=True, default=False,
286
+ help=(
287
+ "Delete each source directory/bundle after its destination "
288
+ "has been written successfully. Use with care."
289
+ ),
290
+ )
291
+ def migrate_backtests_cmd(
292
+ src, dst, workers, no_index, include_ohlcv, no_skip_existing,
293
+ delete_source,
294
+ ):
295
+ """Convert a directory of legacy backtest folders into the bundled
296
+ binary format introduced in issue #487.
297
+
298
+ The new ``.iafbt`` format is a single zstd-compressed MessagePack
299
+ file per backtest. Loading bundled directories is dramatically
300
+ faster than the legacy multi-file layout for large batches.
301
+
302
+ Migration is streamed (load+save fused per worker) so memory
303
+ usage stays roughly constant regardless of source size, and
304
+ interrupted runs can be resumed (existing destination bundles
305
+ are skipped by default).
306
+ """
307
+ from investing_algorithm_framework.domain import migrate_backtests
308
+
309
+ n = migrate_backtests(
310
+ src,
311
+ dst,
312
+ workers=workers,
313
+ show_progress=True,
314
+ write_index=not no_index,
315
+ include_ohlcv=include_ohlcv,
316
+ skip_existing=not no_skip_existing,
317
+ delete_source=delete_source,
318
+ )
319
+ click.echo(f"Migrated {n} backtest(s) from {src} to {dst}")
320
+
321
+
322
+ cli.add_command(migrate_backtests_cmd)