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.
- {lumibot-4.2.3/lumibot.egg-info → lumibot-4.2.6}/PKG-INFO +2 -2
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting_pandas.py +3 -3
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/thetadata_backtesting_pandas.py +1 -1
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/options_helper.py +86 -23
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/_strategy.py +36 -13
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/strategy_executor.py +1 -3
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/ccxt_data_store.py +1 -1
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/databento_helper.py +3 -3
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/thetadata_helper.py +255 -59
- {lumibot-4.2.3 → lumibot-4.2.6/lumibot.egg-info}/PKG-INFO +2 -2
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/SOURCES.txt +2 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/requires.txt +1 -1
- {lumibot-4.2.3 → lumibot-4.2.6}/setup.py +2 -2
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_data_source_env.py +3 -0
- lumibot-4.2.6/tests/test_backtesting_datetime_normalization.py +94 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_options_helper.py +45 -3
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_timestep_alias.py +1 -2
- lumibot-4.2.6/tests/test_strategy_price_guard.py +50 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_helper.py +187 -63
- {lumibot-4.2.3 → lumibot-4.2.6}/LICENSE +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/MANIFEST.in +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/README.md +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/alpaca_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/alpha_vantage_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/backtesting_broker.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/ccxt_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/databento_backtesting_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/fix_debug.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/interactive_brokers_rest_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/pandas_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/polygon_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/thetadata_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/backtesting/yahoo_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/alpaca.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/bitunix.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/broker.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/ccxt.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/example_broker.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/interactive_brokers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/interactive_brokers_rest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/projectx.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/schwab.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/tradier.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/brokers/tradovate.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/configs_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/drift_rebalancer_logic.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/grok_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/perplexity_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/quiver_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/components/vix_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/constants.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/credentials.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/alpaca_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/alpha_vantage_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/bitunix_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/ccxt_backtesting_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/ccxt_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/data_source.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/data_source_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data_pandas.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/databento_data_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/example_broker_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/exceptions.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/interactive_brokers_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/interactive_brokers_rest_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/pandas_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polars_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polars_mixin.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/polygon_data_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/projectx_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/schwab_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/tradier_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/tradovate_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/yahoo_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/data_sources/yahoo_data_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/asset.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/bar.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/bars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/chains.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/data_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/dataline.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/order.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/position.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/quote.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/entities/trading_fee.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/bitunix_futures_example.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/ccxt_backtesting_example.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/classic_60_40.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/classic_60_40_config.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_50_50.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_50_50_config.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/crypto_important_functions.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/drift_rebalancer.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/forex_hold_to_expiry.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/futures_hold_to_expiry.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/lifecycle_logger.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/options_hold_to_expiry.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/schedule_function.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/simple_start_single_file.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_bracket.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_buy_and_hold.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_diversified_leverage.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_limit_and_trailing_stops.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_momentum.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/stock_oco.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/strangle.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/example_strategies/test_broker_functions.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/resources/ThetaTerminal.jar +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/resources/conf.yaml +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/session_manager.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/strategies/strategy.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/alpaca_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/backtest_cache.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/bitunix_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/black_scholes.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/databento_helper_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/debugers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/decorators.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/futures_roll.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/futures_symbols.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/indicators.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/lumibot_logger.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/lumibot_time.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/pandas.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polars_utils.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper_async.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/polygon_helper_polars_optimized.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/projectx_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/schwab_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/types.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/yahoo_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/tools/yahoo_helper_polars_optimized.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/debug_log_trader.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/traders/trader.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/custom_stream.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot/trading_builtins/safe_list.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/dependency_links.txt +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/lumibot.egg-info/top_level.txt +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/pyproject.toml +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/setup.cfg +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/__init__.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/conftest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/performance_tracker.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/profile_thetadata_vs_polygon.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_accuracy_verification.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_backtesting_broker_processing.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_buy_hold_quiet_logs_full_run.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_crypto_cash_regressions.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_daily_data_timestamp_comparison.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento_comprehensive_trading.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_databento_parity.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_debug_avg_fill_price.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_dividends.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_example_strategies.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_failing_backtest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_edge_cases.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_single_trade.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_futures_ultra_simple.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_index_data_verification.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_multileg_backtest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_pandas_backtest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_passing_trader_into_backtest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_polars_lru_eviction.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_polygon.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_strategy_executor.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata_comprehensive.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_thetadata_vs_polygon.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/backtest/test_yahoo.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/conftest.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/fixtures.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_auth_fix.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_multileg_fix.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_alpaca_oauth.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_apscheduler_warnings.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_asset.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_asset_auto_expiry.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_auto_market_inference.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtest_cache_manager.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker_await_close.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_broker_time_advance.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_crypto_cash_unit.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_flow_control.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_multileg_unit.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_backtesting_quiet_logs_complete.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_aggregate_frequency_normalization.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_aggregation_timeunits.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_bars_frequency_flex.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_botspot_handler.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_botspot_logger.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_bitunix.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_cleanup.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_broker_initialization.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_brokers_handle_crypto.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_cash.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_ccxt.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_ccxt_store.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_configs_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures_integration.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_continuous_futures_resolution.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_data_polars_parity.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_data_source.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_asset_validation.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_auto_expiry_integration.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_backtesting.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_backtesting_polars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_live.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_databento_timezone_fixes.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_drift_rebalancer.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_futures_integration.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_futures_roll.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_get_historical_prices.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_indicator_subplots.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_integration_tests.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_interactive_brokers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_live_trading_resilience.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_logger_env_vars.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_logging.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_lumibot_logger.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_market_infinite_loop_bug.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_mes_symbols.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_momentum.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_order.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_order_serialization.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_pandas_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_polars_resample.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_polygon_helper.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_position_serialization.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_bracket_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_bracket_lifecycle_unit.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_datetime_columns.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_datetime_index.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_helpers.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_lifecycle.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_lifecycle_unit.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_live_flow.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_projectx_url_mappings.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_buy_and_hold.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_comprehensive.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_functionality.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_quiet_logs_requirements.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_session_manager.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_strategy_methods.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_backwards_compat.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_thetadata_pandas_verification.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradier.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradier_data.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradingfee.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_tradovate.py +0 -0
- {lumibot-4.2.3 → lumibot-4.2.6}/tests/test_unified_logger.py +0 -0
- {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
|
+
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.
|
|
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
|
-
|
|
458
|
-
if not
|
|
459
|
-
price = float(
|
|
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].
|
|
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
|
-
|
|
482
|
-
|
|
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
|
|
493
|
-
if mid
|
|
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
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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 =
|
|
1399
|
-
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
|
|
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"{
|
|
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(
|
|
1640
|
-
if
|
|
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 {
|
|
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.
|
|
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="
|
|
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
|
-
|
|
1053
|
-
if
|
|
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
|
|