lumibot 4.2.3__tar.gz → 4.2.6__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.

Potentially problematic release.


This version of lumibot might be problematic. Click here for more details.

Files changed (280) hide show
  1. {lumibot-4.2.3/lumibot.egg-info → lumibot-4.2.6}/PKG-INFO +2 -2
  2. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting_pandas.py +3 -3
  3. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/thetadata_backtesting_pandas.py +1 -1
  4. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/options_helper.py +86 -23
  5. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/_strategy.py +36 -13
  6. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/strategy_executor.py +1 -3
  7. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/ccxt_data_store.py +1 -1
  8. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/databento_helper.py +3 -3
  9. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/thetadata_helper.py +255 -59
  10. {lumibot-4.2.3 → lumibot-4.2.6/lumibot.egg-info}/PKG-INFO +2 -2
  11. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/SOURCES.txt +2 -0
  12. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/requires.txt +1 -1
  13. {lumibot-4.2.3 → lumibot-4.2.6}/setup.py +2 -2
  14. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_data_source_env.py +3 -0
  15. lumibot-4.2.6/tests/test_backtesting_datetime_normalization.py +94 -0
  16. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_options_helper.py +45 -3
  17. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_timestep_alias.py +1 -2
  18. lumibot-4.2.6/tests/test_strategy_price_guard.py +50 -0
  19. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_helper.py +187 -63
  20. {lumibot-4.2.3 → lumibot-4.2.6}/LICENSE +0 -0
  21. {lumibot-4.2.3 → lumibot-4.2.6}/MANIFEST.in +0 -0
  22. {lumibot-4.2.3 → lumibot-4.2.6}/README.md +0 -0
  23. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/__init__.py +0 -0
  24. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/__init__.py +0 -0
  25. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/alpaca_backtesting.py +0 -0
  26. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/alpha_vantage_backtesting.py +0 -0
  27. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/backtesting_broker.py +0 -0
  28. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/ccxt_backtesting.py +0 -0
  29. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting.py +0 -0
  30. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting_polars.py +0 -0
  31. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/fix_debug.py +0 -0
  32. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/interactive_brokers_rest_backtesting.py +0 -0
  33. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/pandas_backtesting.py +0 -0
  34. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/polygon_backtesting.py +0 -0
  35. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/thetadata_backtesting.py +0 -0
  36. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/yahoo_backtesting.py +0 -0
  37. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/__init__.py +0 -0
  38. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/alpaca.py +0 -0
  39. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/bitunix.py +0 -0
  40. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/broker.py +0 -0
  41. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/ccxt.py +0 -0
  42. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/example_broker.py +0 -0
  43. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/interactive_brokers.py +0 -0
  44. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/interactive_brokers_rest.py +0 -0
  45. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/projectx.py +0 -0
  46. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/schwab.py +0 -0
  47. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/tradier.py +0 -0
  48. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/tradovate.py +0 -0
  49. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/__init__.py +0 -0
  50. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/configs_helper.py +0 -0
  51. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/drift_rebalancer_logic.py +0 -0
  52. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/grok_helper.py +0 -0
  53. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/perplexity_helper.py +0 -0
  54. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/quiver_helper.py +0 -0
  55. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/vix_helper.py +0 -0
  56. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/constants.py +0 -0
  57. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/credentials.py +0 -0
  58. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/__init__.py +0 -0
  59. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/alpaca_data.py +0 -0
  60. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/alpha_vantage_data.py +0 -0
  61. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/bitunix_data.py +0 -0
  62. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/ccxt_backtesting_data.py +0 -0
  63. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/ccxt_data.py +0 -0
  64. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/data_source.py +0 -0
  65. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/data_source_backtesting.py +0 -0
  66. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data.py +0 -0
  67. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data_pandas.py +0 -0
  68. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data_polars.py +0 -0
  69. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/example_broker_data.py +0 -0
  70. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/exceptions.py +0 -0
  71. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/interactive_brokers_data.py +0 -0
  72. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/interactive_brokers_rest_data.py +0 -0
  73. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/pandas_data.py +0 -0
  74. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polars_data.py +0 -0
  75. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polars_mixin.py +0 -0
  76. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polygon_data_polars.py +0 -0
  77. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/projectx_data.py +0 -0
  78. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/schwab_data.py +0 -0
  79. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/tradier_data.py +0 -0
  80. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/tradovate_data.py +0 -0
  81. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/yahoo_data.py +0 -0
  82. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/yahoo_data_polars.py +0 -0
  83. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/__init__.py +0 -0
  84. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/asset.py +0 -0
  85. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/bar.py +0 -0
  86. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/bars.py +0 -0
  87. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/chains.py +0 -0
  88. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/data.py +0 -0
  89. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/data_polars.py +0 -0
  90. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/dataline.py +0 -0
  91. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/order.py +0 -0
  92. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/position.py +0 -0
  93. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/quote.py +0 -0
  94. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/trading_fee.py +0 -0
  95. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/__init__.py +0 -0
  96. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/bitunix_futures_example.py +0 -0
  97. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/ccxt_backtesting_example.py +0 -0
  98. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/classic_60_40.py +0 -0
  99. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/classic_60_40_config.py +0 -0
  100. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_50_50.py +0 -0
  101. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_50_50_config.py +0 -0
  102. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_important_functions.py +0 -0
  103. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/drift_rebalancer.py +0 -0
  104. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/forex_hold_to_expiry.py +0 -0
  105. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/futures_hold_to_expiry.py +0 -0
  106. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/lifecycle_logger.py +0 -0
  107. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/options_hold_to_expiry.py +0 -0
  108. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/schedule_function.py +0 -0
  109. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/simple_start_single_file.py +0 -0
  110. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_bracket.py +0 -0
  111. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_buy_and_hold.py +0 -0
  112. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_diversified_leverage.py +0 -0
  113. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_limit_and_trailing_stops.py +0 -0
  114. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_momentum.py +0 -0
  115. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_oco.py +0 -0
  116. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/strangle.py +0 -0
  117. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/test_broker_functions.py +0 -0
  118. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/resources/ThetaTerminal.jar +0 -0
  119. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/resources/conf.yaml +0 -0
  120. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/__init__.py +0 -0
  121. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/session_manager.py +0 -0
  122. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/strategy.py +0 -0
  123. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/__init__.py +0 -0
  124. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/alpaca_helpers.py +0 -0
  125. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/backtest_cache.py +0 -0
  126. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/bitunix_helpers.py +0 -0
  127. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/black_scholes.py +0 -0
  128. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/databento_helper_polars.py +0 -0
  129. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/debugers.py +0 -0
  130. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/decorators.py +0 -0
  131. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/futures_roll.py +0 -0
  132. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/futures_symbols.py +0 -0
  133. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/helpers.py +0 -0
  134. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/indicators.py +0 -0
  135. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/lumibot_logger.py +0 -0
  136. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/lumibot_time.py +0 -0
  137. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/pandas.py +0 -0
  138. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polars_utils.py +0 -0
  139. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper.py +0 -0
  140. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper_async.py +0 -0
  141. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper_polars_optimized.py +0 -0
  142. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/projectx_helpers.py +0 -0
  143. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/schwab_helper.py +0 -0
  144. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/types.py +0 -0
  145. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/yahoo_helper.py +0 -0
  146. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/yahoo_helper_polars_optimized.py +0 -0
  147. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/__init__.py +0 -0
  148. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/debug_log_trader.py +0 -0
  149. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/trader.py +0 -0
  150. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/__init__.py +0 -0
  151. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/custom_stream.py +0 -0
  152. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/safe_list.py +0 -0
  153. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/dependency_links.txt +0 -0
  154. {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/top_level.txt +0 -0
  155. {lumibot-4.2.3 → lumibot-4.2.6}/pyproject.toml +0 -0
  156. {lumibot-4.2.3 → lumibot-4.2.6}/setup.cfg +0 -0
  157. {lumibot-4.2.3 → lumibot-4.2.6}/tests/__init__.py +0 -0
  158. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/__init__.py +0 -0
  159. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/conftest.py +0 -0
  160. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/performance_tracker.py +0 -0
  161. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/profile_thetadata_vs_polygon.py +0 -0
  162. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_accuracy_verification.py +0 -0
  163. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_backtesting_broker_processing.py +0 -0
  164. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_buy_hold_quiet_logs_full_run.py +0 -0
  165. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_crypto_cash_regressions.py +0 -0
  166. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_daily_data_timestamp_comparison.py +0 -0
  167. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento.py +0 -0
  168. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento_comprehensive_trading.py +0 -0
  169. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento_parity.py +0 -0
  170. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_debug_avg_fill_price.py +0 -0
  171. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_dividends.py +0 -0
  172. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_example_strategies.py +0 -0
  173. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_failing_backtest.py +0 -0
  174. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_edge_cases.py +0 -0
  175. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_single_trade.py +0 -0
  176. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_ultra_simple.py +0 -0
  177. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_index_data_verification.py +0 -0
  178. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_multileg_backtest.py +0 -0
  179. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_pandas_backtest.py +0 -0
  180. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_passing_trader_into_backtest.py +0 -0
  181. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_polars_lru_eviction.py +0 -0
  182. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_polygon.py +0 -0
  183. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_strategy_executor.py +0 -0
  184. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata.py +0 -0
  185. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata_comprehensive.py +0 -0
  186. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata_vs_polygon.py +0 -0
  187. {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_yahoo.py +0 -0
  188. {lumibot-4.2.3 → lumibot-4.2.6}/tests/conftest.py +0 -0
  189. {lumibot-4.2.3 → lumibot-4.2.6}/tests/fixtures.py +0 -0
  190. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca.py +0 -0
  191. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_auth_fix.py +0 -0
  192. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_backtesting.py +0 -0
  193. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_data.py +0 -0
  194. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_helpers.py +0 -0
  195. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_multileg_fix.py +0 -0
  196. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_oauth.py +0 -0
  197. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_apscheduler_warnings.py +0 -0
  198. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_asset.py +0 -0
  199. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_asset_auto_expiry.py +0 -0
  200. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_auto_market_inference.py +0 -0
  201. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtest_cache_manager.py +0 -0
  202. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker.py +0 -0
  203. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker_await_close.py +0 -0
  204. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker_time_advance.py +0 -0
  205. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_crypto_cash_unit.py +0 -0
  206. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_flow_control.py +0 -0
  207. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_multileg_unit.py +0 -0
  208. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_quiet_logs_complete.py +0 -0
  209. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_aggregate_frequency_normalization.py +0 -0
  210. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_aggregation_timeunits.py +0 -0
  211. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_frequency_flex.py +0 -0
  212. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_botspot_handler.py +0 -0
  213. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_botspot_logger.py +0 -0
  214. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_bitunix.py +0 -0
  215. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_cleanup.py +0 -0
  216. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_initialization.py +0 -0
  217. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_brokers_handle_crypto.py +0 -0
  218. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_cash.py +0 -0
  219. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_ccxt.py +0 -0
  220. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_ccxt_store.py +0 -0
  221. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_configs_helper.py +0 -0
  222. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures.py +0 -0
  223. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures_integration.py +0 -0
  224. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures_resolution.py +0 -0
  225. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_data_polars_parity.py +0 -0
  226. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_data_source.py +0 -0
  227. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_asset_validation.py +0 -0
  228. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_auto_expiry_integration.py +0 -0
  229. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_backtesting.py +0 -0
  230. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_backtesting_polars.py +0 -0
  231. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_data.py +0 -0
  232. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_helper.py +0 -0
  233. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_live.py +0 -0
  234. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_timezone_fixes.py +0 -0
  235. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_drift_rebalancer.py +0 -0
  236. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_futures_integration.py +0 -0
  237. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_futures_roll.py +0 -0
  238. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_get_historical_prices.py +0 -0
  239. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_helpers.py +0 -0
  240. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_indicator_subplots.py +0 -0
  241. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_integration_tests.py +0 -0
  242. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_interactive_brokers.py +0 -0
  243. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_live_trading_resilience.py +0 -0
  244. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_logger_env_vars.py +0 -0
  245. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_logging.py +0 -0
  246. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_lumibot_logger.py +0 -0
  247. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_market_infinite_loop_bug.py +0 -0
  248. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_mes_symbols.py +0 -0
  249. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_momentum.py +0 -0
  250. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_order.py +0 -0
  251. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_order_serialization.py +0 -0
  252. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_pandas_data.py +0 -0
  253. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_polars_resample.py +0 -0
  254. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_polygon_helper.py +0 -0
  255. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_position_serialization.py +0 -0
  256. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx.py +0 -0
  257. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_bracket_helpers.py +0 -0
  258. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_bracket_lifecycle_unit.py +0 -0
  259. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_data.py +0 -0
  260. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_datetime_columns.py +0 -0
  261. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_datetime_index.py +0 -0
  262. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_helpers.py +0 -0
  263. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_lifecycle.py +0 -0
  264. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_lifecycle_unit.py +0 -0
  265. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_live_flow.py +0 -0
  266. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_url_mappings.py +0 -0
  267. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_buy_and_hold.py +0 -0
  268. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_comprehensive.py +0 -0
  269. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_functionality.py +0 -0
  270. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_requirements.py +0 -0
  271. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_session_manager.py +0 -0
  272. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_strategy_methods.py +0 -0
  273. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_backwards_compat.py +0 -0
  274. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_pandas_verification.py +0 -0
  275. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradier.py +0 -0
  276. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradier_data.py +0 -0
  277. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradingfee.py +0 -0
  278. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradovate.py +0 -0
  279. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_unified_logger.py +0 -0
  280. {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_vix_helper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lumibot
3
- Version: 4.2.3
3
+ Version: 4.2.6
4
4
  Summary: Backtesting and Trading Library, Made by Lumiwealth
5
5
  Home-page: https://github.com/Lumiwealth/lumibot
6
6
  Author: Robert Grzesik
@@ -51,7 +51,7 @@ Requires-Dist: schwab-py>=1.5.0
51
51
  Requires-Dist: Flask>=2.3
52
52
  Requires-Dist: free-proxy
53
53
  Requires-Dist: requests-oauthlib
54
- Requires-Dist: boto3>=1.28.0
54
+ Requires-Dist: boto3>=1.40.64
55
55
  Dynamic: author
56
56
  Dynamic: author-email
57
57
  Dynamic: classifier
@@ -454,9 +454,9 @@ class DataBentoDataBacktestingPandas(PandasData):
454
454
  filtered_df = df[df.index <= current_dt_aware]
455
455
 
456
456
  if not filtered_df.empty:
457
- last_price = filtered_df['close'].iloc[-1]
458
- if not pd.isna(last_price):
459
- price = float(last_price)
457
+ valid_closes = filtered_df['close'].dropna()
458
+ if not valid_closes.empty:
459
+ price = float(valid_closes.iloc[-1])
460
460
  # OPTIMIZATION: Cache the result
461
461
  self._last_price_cache[cache_key] = price
462
462
  return price
@@ -771,7 +771,7 @@ class ThetaDataBacktestingPandas(PandasData):
771
771
  quote_columns = ['bid', 'ask', 'bid_size', 'ask_size', 'bid_condition', 'ask_condition', 'bid_exchange', 'ask_exchange']
772
772
  existing_quote_cols = [col for col in quote_columns if col in df.columns]
773
773
  if existing_quote_cols:
774
- df[existing_quote_cols] = df[existing_quote_cols].fillna(method='ffill')
774
+ df[existing_quote_cols] = df[existing_quote_cols].ffill()
775
775
 
776
776
  # Log how much forward filling occurred
777
777
  if 'bid' in df.columns and 'ask' in df.columns:
@@ -1,5 +1,7 @@
1
1
  from dataclasses import dataclass
2
2
  from datetime import date, datetime, timedelta
3
+ from decimal import Decimal
4
+ import math
3
5
  from typing import Any, Dict, List, Optional, Tuple, Union
4
6
  import warnings
5
7
 
@@ -23,6 +25,7 @@ class OptionMarketEvaluation:
23
25
  sell_price: Optional[float]
24
26
  used_last_price_fallback: bool
25
27
  max_spread_pct: Optional[float]
28
+ data_quality_flags: List[str]
26
29
 
27
30
 
28
31
  class OptionsHelper:
@@ -58,6 +61,54 @@ class OptionsHelper:
58
61
  self._liquidity_deprecation_warned = False
59
62
  self.strategy.log_message("OptionsHelper initialized.", color="blue")
60
63
 
64
+ @staticmethod
65
+ def _coerce_price(value: Any, field_name: str, flags: List[str], notes: List[str]) -> Optional[float]:
66
+ """Normalize quote values and record data quality issues."""
67
+ raw_value = value
68
+
69
+ if value is None:
70
+ flags.append(f"{field_name}_missing")
71
+ return None
72
+
73
+ try:
74
+ if isinstance(value, Decimal):
75
+ value = float(value)
76
+ else:
77
+ value = float(value) # type: ignore[arg-type]
78
+ except (TypeError, ValueError):
79
+ flags.append(f"{field_name}_non_numeric")
80
+ notes.append(f"{field_name} value {raw_value!r} is non-numeric; dropping.")
81
+ return None
82
+
83
+ if math.isnan(value) or math.isinf(value):
84
+ flags.append(f"{field_name}_non_finite")
85
+ notes.append(f"{field_name} value {value!r} is not finite; dropping.")
86
+ return None
87
+
88
+ if value <= 0:
89
+ flags.append(f"{field_name}_non_positive")
90
+ notes.append(f"{field_name} value {value!r} is non-positive; dropping.")
91
+ return None
92
+
93
+ return value
94
+
95
+ @staticmethod
96
+ def has_actionable_price(evaluation: Optional["OptionMarketEvaluation"]) -> bool:
97
+ """Return True when the evaluation contains a usable buy price."""
98
+ if evaluation is None:
99
+ return False
100
+
101
+ price = evaluation.buy_price
102
+ if price is None:
103
+ return False
104
+
105
+ try:
106
+ price = float(price)
107
+ except (TypeError, ValueError):
108
+ return False
109
+
110
+ return math.isfinite(price) and price > 0 and not evaluation.spread_too_wide
111
+
61
112
  # ============================================================
62
113
  # Basic Utility Functions
63
114
  # ============================================================
@@ -467,6 +518,9 @@ class OptionsHelper:
467
518
  buy_price: Optional[float] = None
468
519
  sell_price: Optional[float] = None
469
520
 
521
+ data_quality_flags: List[str] = []
522
+ sanitization_notes: List[str] = []
523
+
470
524
  # Attempt to get quotes first
471
525
  quote = None
472
526
  try:
@@ -478,24 +532,20 @@ class OptionsHelper:
478
532
  )
479
533
 
480
534
  if quote and quote.bid is not None and quote.ask is not None:
481
- try:
482
- bid = float(quote.bid)
483
- ask = float(quote.ask)
484
- except (TypeError, ValueError):
485
- bid = quote.bid
486
- ask = quote.ask
535
+ bid = self._coerce_price(quote.bid, "bid", data_quality_flags, sanitization_notes)
536
+ ask = self._coerce_price(quote.ask, "ask", data_quality_flags, sanitization_notes)
487
537
  has_bid_ask = bid is not None and ask is not None
488
538
 
489
539
  if has_bid_ask and bid is not None and ask is not None:
490
540
  buy_price = ask
491
541
  sell_price = bid
492
- mid = (ask + bid) / 2 if (ask is not None and bid is not None) else None
493
- if mid and mid > 0:
542
+ mid = (ask + bid) / 2
543
+ if not math.isfinite(mid) or mid <= 0:
544
+ spread_pct = None
545
+ else:
494
546
  spread_pct = (ask - bid) / mid
495
547
  if max_spread_pct is not None:
496
548
  spread_too_wide = spread_pct > max_spread_pct
497
- else:
498
- spread_pct = None
499
549
  else:
500
550
  missing_bid_ask = True
501
551
 
@@ -510,6 +560,10 @@ class OptionsHelper:
510
560
 
511
561
  if last_price is None:
512
562
  missing_last_price = True
563
+ else:
564
+ last_price = self._coerce_price(last_price, "last_price", data_quality_flags, sanitization_notes)
565
+ if last_price is None:
566
+ missing_last_price = True
513
567
 
514
568
  if not has_bid_ask and allow_fallback and last_price is not None:
515
569
  buy_price = last_price
@@ -519,6 +573,14 @@ class OptionsHelper:
519
573
  f"Using last-price fallback for {option_asset} due to missing bid/ask quotes.",
520
574
  color="yellow",
521
575
  )
576
+ elif not has_bid_ask and allow_fallback and last_price is None:
577
+ data_quality_flags.append("last_price_unusable")
578
+
579
+ if buy_price is not None and (not math.isfinite(buy_price) or buy_price <= 0):
580
+ sanitization_notes.append(f"buy_price {buy_price!r} is not actionable; clearing.")
581
+ data_quality_flags.append("buy_price_non_finite")
582
+ buy_price = None
583
+ sell_price = None
522
584
 
523
585
  # Compose log message
524
586
  spread_str = f"{spread_pct:.2%}" if spread_pct is not None else "None"
@@ -526,6 +588,12 @@ class OptionsHelper:
526
588
  log_color = "red" if spread_too_wide else (
527
589
  "yellow" if (missing_bid_ask or missing_last_price or used_last_price_fallback) else "blue"
528
590
  )
591
+ if sanitization_notes:
592
+ note_summary = "; ".join(sanitization_notes)
593
+ self.strategy.log_message(
594
+ f"Option data sanitization for {option_asset}: {note_summary}",
595
+ color="yellow",
596
+ )
529
597
  self.strategy.log_message(
530
598
  (
531
599
  f"Option market evaluation for {option_asset}: "
@@ -533,7 +601,8 @@ class OptionsHelper:
533
601
  f"max_spread={max_spread_str}, missing_bid_ask={missing_bid_ask}, "
534
602
  f"missing_last_price={missing_last_price}, spread_too_wide={spread_too_wide}, "
535
603
  f"used_last_price_fallback={used_last_price_fallback}, "
536
- f"buy_price={buy_price}, sell_price={sell_price}"
604
+ f"buy_price={buy_price}, sell_price={sell_price}, "
605
+ f"data_quality_flags={data_quality_flags}"
537
606
  ),
538
607
  color=log_color,
539
608
  )
@@ -551,6 +620,7 @@ class OptionsHelper:
551
620
  sell_price=sell_price,
552
621
  used_last_price_fallback=used_last_price_fallback,
553
622
  max_spread_pct=max_spread_pct,
623
+ data_quality_flags=data_quality_flags,
554
624
  )
555
625
 
556
626
  def check_option_liquidity(self, option_asset: Asset, max_spread_pct: float) -> bool:
@@ -721,18 +791,11 @@ class OptionsHelper:
721
791
  self.strategy.log_message(f"Cannot validate data without underlying symbol, returning {exp_date}", color="yellow")
722
792
  return exp_date
723
793
 
724
- # No future expirations with valid data; log and check last available
725
- if expiration_dates:
726
- # Check the last available expiry for data
727
- for exp_str, exp_date in reversed(expiration_dates):
728
- strikes = specific_chain.get(exp_str)
729
- if strikes and len(strikes) > 0:
730
- self.strategy.log_message(
731
- f"No valid expirations on or after {dt}; using latest available {exp_date} for {call_or_put_caps}.",
732
- color="yellow",
733
- )
734
- return exp_date
735
-
794
+ # No future expirations with tradeable data; let the caller skip entries gracefully.
795
+ self.strategy.log_message(
796
+ f"No valid expirations on or after {dt} with tradeable data for {call_or_put_caps}; skipping.",
797
+ color="yellow",
798
+ )
736
799
  return None
737
800
 
738
801
  # ============================================================
@@ -124,6 +124,23 @@ class Vars:
124
124
 
125
125
 
126
126
  class _Strategy:
127
+ @staticmethod
128
+ def _normalize_backtest_datetime(value):
129
+ """Ensure backtest boundary datetimes are timezone-aware.
130
+
131
+ Naive datetimes are localized to the LumiBot default timezone; timezone-aware
132
+ inputs are returned unchanged so their original offsets are preserved.
133
+ """
134
+ if value is None:
135
+ return None
136
+ if isinstance(value, datetime.datetime):
137
+ tzinfo = value.tzinfo
138
+ if tzinfo is None or tzinfo.utcoffset(value) is None:
139
+ return to_datetime_aware(value)
140
+ if not hasattr(tzinfo, "zone"):
141
+ return value.astimezone(LUMIBOT_DEFAULT_PYTZ)
142
+ return value
143
+
127
144
  @property
128
145
  def is_backtesting(self) -> bool:
129
146
  """Boolean flag indicating whether the strategy is running in backtesting mode."""
@@ -1388,15 +1405,9 @@ class _Strategy:
1388
1405
  if use_other_option_source and not isinstance(optionsource_class, type):
1389
1406
  raise ValueError(f"`optionsource_class` must be a class. You passed in {optionsource_class}")
1390
1407
 
1391
- self.verify_backtest_inputs(backtesting_start, backtesting_end)
1392
-
1393
- if not self.IS_BACKTESTABLE:
1394
- get_logger(__name__).warning(f"Strategy {name + ' ' if name is not None else ''}cannot be " f"backtested at the moment")
1395
- return None
1396
-
1397
1408
  try:
1398
- backtesting_start = to_datetime_aware(backtesting_start)
1399
- backtesting_end = to_datetime_aware(backtesting_end)
1409
+ backtesting_start = self._normalize_backtest_datetime(backtesting_start)
1410
+ backtesting_end = self._normalize_backtest_datetime(backtesting_end)
1400
1411
  except AttributeError:
1401
1412
  get_logger(__name__).error(
1402
1413
  "`backtesting_start` and `backtesting_end` must be datetime objects. \n"
@@ -1405,6 +1416,15 @@ class _Strategy:
1405
1416
  )
1406
1417
  return None
1407
1418
 
1419
+ get_logger(__name__).info("Backtest start = %s", backtesting_start)
1420
+ get_logger(__name__).info("Backtest end = %s", backtesting_end)
1421
+
1422
+ self.verify_backtest_inputs(backtesting_start, backtesting_end)
1423
+
1424
+ if not self.IS_BACKTESTABLE:
1425
+ get_logger(__name__).warning(f"Strategy {name + ' ' if name is not None else ''}cannot be " f"backtested at the moment")
1426
+ return None
1427
+
1408
1428
  if BACKTESTING_QUIET_LOGS is not None:
1409
1429
  quiet_logs = BACKTESTING_QUIET_LOGS
1410
1430
 
@@ -1628,18 +1648,21 @@ class _Strategy:
1628
1648
  if not isinstance(backtesting_end, datetime.datetime):
1629
1649
  raise ValueError(f"`backtesting_end` must be a datetime object. You passed in {backtesting_end}")
1630
1650
 
1651
+ start_dt = cls._normalize_backtest_datetime(backtesting_start)
1652
+ end_dt = cls._normalize_backtest_datetime(backtesting_end)
1653
+
1631
1654
  # Check that backtesting end is after backtesting start
1632
- if backtesting_end <= backtesting_start:
1655
+ if end_dt <= start_dt:
1633
1656
  raise ValueError(
1634
1657
  f"`backtesting_end` must be after `backtesting_start`. You passed in "
1635
- f"{backtesting_end} and {backtesting_start}"
1658
+ f"{end_dt} and {start_dt}"
1636
1659
  )
1637
1660
 
1638
1661
  # Check that backtesting_end is not in the future
1639
- now = datetime.datetime.now(backtesting_end.tzinfo) if backtesting_end.tzinfo else datetime.datetime.now()
1640
- if backtesting_end > now:
1662
+ now = datetime.datetime.now(end_dt.tzinfo) if end_dt.tzinfo else datetime.datetime.now()
1663
+ if end_dt > now:
1641
1664
  raise ValueError(
1642
- f"`backtesting_end` cannot be in the future. You passed in {backtesting_end}, now is {now}"
1665
+ f"`backtesting_end` cannot be in the future. You passed in {end_dt}, now is {now}"
1643
1666
  )
1644
1667
 
1645
1668
  def send_update_to_cloud(self):
@@ -13,8 +13,6 @@ import pandas_market_calendars as mcal
13
13
  from apscheduler.jobstores.memory import MemoryJobStore
14
14
  from apscheduler.schedulers.background import BackgroundScheduler
15
15
  from apscheduler.triggers.cron import CronTrigger
16
- from termcolor import colored
17
-
18
16
  from lumibot.constants import LUMIBOT_DEFAULT_PYTZ
19
17
  from lumibot.entities import Asset, Order
20
18
  from lumibot.entities import Asset
@@ -1166,7 +1164,7 @@ class StrategyExecutor(Thread):
1166
1164
  # For live trading, stop when market closes
1167
1165
  return False
1168
1166
 
1169
- self.strategy.log_message(colored(f"Sleeping for {strategy_sleeptime} seconds", color="blue"))
1167
+ self.strategy.logger.debug("Sleeping for %s seconds", strategy_sleeptime)
1170
1168
 
1171
1169
  # Run process orders at the market close time first (if not continuous market)
1172
1170
  if not is_continuous_market:
@@ -445,7 +445,7 @@ class CcxtCacheDB:
445
445
  if freq == "1d":
446
446
  dt_range = pd.date_range(start=df.index.min(), end=df.index.max(), freq="D")
447
447
  else:
448
- dt_range = pd.date_range(start=df.index.min(), end=df.index.max(), freq="T")
448
+ dt_range = pd.date_range(start=df.index.min(), end=df.index.max(), freq="min")
449
449
 
450
450
  df_complete = df.reindex(dt_range).ffill()
451
451
  df_complete['missing'] = np.where(df_complete.index.isin(df.index), 0, 1)
@@ -1047,10 +1047,10 @@ def get_last_price_from_databento(
1047
1047
  df = pd.DataFrame(data)
1048
1048
 
1049
1049
  if not df.empty:
1050
- # Get the last available price (close price of most recent bar)
1051
1050
  if 'close' in df.columns:
1052
- price = df['close'].iloc[-1]
1053
- if pd.notna(price):
1051
+ closes = df['close'].dropna()
1052
+ if not closes.empty:
1053
+ price = closes.iloc[-1]
1054
1054
  logger.info(f"✓ SUCCESS: Got last price for {symbol_to_use}: {price}")
1055
1055
  return float(price)
1056
1056