Lumiwealth / lumibot

Backtesting and Trading Bots Made Easy for Crypto, Stocks, Options, Futures, FOREX and more
GNU General Public License v3.0
927 stars 176 forks source link

Did an upgrade, I believe there is a bug in the _strategy.py regarding polygone api key. #518

Open ghzgod opened 3 months ago

ghzgod commented 3 months ago

I did a pip upgrade and now If I run YahooDataBackTest for my strategy I get an error

Here is my code:

from login import get_api_key
from lumibot.traders import Trader
from lumibot.brokers import Alpaca
from datetime import datetime, timedelta
from lumibot.strategies.strategy import Strategy
from lumibot.backtesting import YahooDataBacktesting
from datetime import datetime

ALPACA_CONFIG = {
    # Put your own Alpaca key here:
    "API_KEY": get_api_key("alpaca_paper_api_key"),
    # Put your own Alpaca secret here:
    "API_SECRET": get_api_key("alpaca_paper_api_secret"),
    # Set this to False to use a live account
    "PAPER": True
}

class MyStrategy(Strategy):
    parameters = {
        "symbol": None,
        "quantity": 1,
        "side": "buy"
    }

    def initialize(self, symbol=""):
        self.sleeptime = "60M"

    def on_trading_iteration(self):
        symbol = self.parameters["symbol"]
        quantity = self.parameters["quantity"]
        side = self.parameters["side"]
        order = self.create_order(symbol, quantity, side)
        self.submit_order(order)

if __name__ == "__main__":
    trade = False
    if trade:
        broker = Alpaca(ALPACA_CONFIG)
        strategy = MyStrategy(broker=broker, parameters={"symbol": "NVDA"})
        trader = Trader()
        trader.add_strategy(strategy)
        trader.run_all()
    else:
        years_to_backest = 1 # Number of years before today
        end = datetime.today() - timedelta(days=1)
        start = end - timedelta(days=365 * years_to_backest)
        MyStrategy.backtest(
            YahooDataBacktesting,
            start,
            end,
            parameters={"symbol": "GME"}
        )

Here is my error

 File "python/stocks/profitprophet/archive/lumibot/lumibot_backtests.py", line 50, in <module>
    MyStrategy.backtest(
  File "python3.12/site-packages/lumibot/strategies/_strategy.py", line 1361, in backtest
    results, strategy = cls.run_backtest(
                        ^^^^^^^^^^^^^^^^^
  File "python3.12/site-packages/lumibot/strategies/_strategy.py", line 1050, in run_backtest
    data_source = datasource_class(
                  ^^^^^^^^^^^^^^^^^
  File "python3.12/site-packages/lumibot/backtesting/yahoo_backtesting.py", line 11, in __init__
    YahooData.__init__(self, datetime_start, datetime_end, **kwargs)
  File "python3.12/site-packages/lumibot/data_sources/yahoo_data.py", line 22, in __init__
    super().__init__(*args, **kwargs)
TypeError: DataSourceBacktesting.__init__() got an unexpected keyword argument 'polygon_api_key'

If I edit the _strategy.py file and comment out the polygone data (in the below example) it begins to work as intended. Is this a bug? Hopefully this is useful information.

        results, strategy = cls.run_backtest(
            *args,
            minutes_before_closing=minutes_before_closing,
            minutes_before_opening=minutes_before_opening,
            sleeptime=sleeptime,
            stats_file=stats_file,
            risk_free_rate=risk_free_rate,
            logfile=logfile,
            config=config,
            auto_adjust=auto_adjust,
            name=name,
            budget=budget,
            benchmark_asset=benchmark_asset,
            plot_file_html=plot_file_html,
            trades_file=trades_file,
            settings_file=settings_file,
            pandas_data=pandas_data,
            quote_asset=quote_asset,
            starting_positions=starting_positions,
            show_plot=show_plot,
            tearsheet_file=tearsheet_file,
            save_tearsheet=save_tearsheet,
            show_tearsheet=show_tearsheet,
            parameters=parameters,
            buy_trading_fees=buy_trading_fees,
            sell_trading_fees=sell_trading_fees,
            api_key=api_key,
            # polygon_api_key=polygon_api_key,  <------- HERE
            # polygon_has_paid_subscription=polygon_has_paid_subscription, <------- AND HERE
            indicators_file=indicators_file,
            show_indicators=show_indicators,
            save_logfile=save_logfile,
            **kwargs,
        )
ghzgod commented 3 months ago

I believe I fixed it by checking the datasource_class first and then adding polygone keys if needed

@classmethod
    def backtest(
        cls,
        datasource_class,  # Ensure datasource_class is passed as an argument
        *args,
        minutes_before_closing=1,
        minutes_before_opening=60,
        sleeptime=1,
        stats_file=None,
        risk_free_rate=None,
        logfile=None,
        config=None,
        auto_adjust=False,
        name=None,
        budget=None,
        benchmark_asset="SPY",
        plot_file_html=None,
        trades_file=None,
        settings_file=None,
        pandas_data=None,
        quote_asset=Asset(symbol="USD", asset_type="forex"),
        starting_positions=None,
        show_plot=True,
        tearsheet_file=None,
        save_tearsheet=True,
        show_tearsheet=True,
        parameters={},
        buy_trading_fees=[],
        sell_trading_fees=[],
        api_key=None,
        polygon_api_key=None,
        polygon_has_paid_subscription=None,  # Deprecated, remove in future versions.
        indicators_file=None,
        show_indicators=True,
        save_logfile=False,
        **kwargs,
    ):
        """Backtest a strategy.

        Parameters
        ----------
        datasource_class : class
            The datasource class to use. For example, if you want to use the yahoo finance datasource, then you
            would pass YahooDataBacktesting as the datasource_class.
        backtesting_start : datetime
            The start date of the backtesting period.
        backtesting_end : datetime
            The end date of the backtesting period.
        minutes_before_closing : int
            The number of minutes before closing that the minutes_before_closing strategy method will be called.
        minutes_before_opening : int
            The number of minutes before opening that the minutes_before_opening strategy method will be called.
        sleeptime : int
            The number of seconds to sleep between each iteration of the backtest.
        stats_file : str
            The file to write the stats to.
        risk_free_rate : float
            The risk free rate to use.
        logfile : str
            The file to write the log to.
        config : dict
            The config to use to set up the brokers in live trading.
        auto_adjust : bool
            Whether or not to automatically adjust the strategy.
        name : str
            The name of the strategy.
        budget : float
            The initial budget to use for the backtest.
        benchmark_asset : str or Asset
            The benchmark asset to use for the backtest to compare to. If it is a string then it will be converted
            to a stock Asset object.
        plot_file_html : str
            The file to write the plot html to.
        trades_file : str
            The file to write the trades to.
        pandas_data : list
            A list of Data objects that are used when the datasource_class object is set to PandasDataBacktesting.
            This contains all the data that will be used in backtesting.
        quote_asset : Asset (crypto)
            An Asset object for the crypto currency that will get used
            as a valuation asset for measuring overall porfolio values.
            Usually USDT, USD, USDC.
        starting_positions : dict
            A dictionary of starting positions for each asset. For example,
            if you want to start with $100 of SPY, and $200 of AAPL, then you
            would pass in starting_positions={'SPY': 100, 'AAPL': 200}.
        show_plot : bool
            Whether to show the plot.
        show_tearsheet : bool
            Whether to show the tearsheet.
        save_tearsheet : bool
            Whether to save the tearsheet.
        parameters : dict
            A dictionary of parameters to pass to the strategy. These parameters
            must be set up within the initialize() method.
        buy_trading_fees : list of TradingFee objects
            A list of TradingFee objects to apply to the buy orders during backtests.
        sell_trading_fees : list of TradingFee objects
            A list of TradingFee objects to apply to the sell orders during backtests.
        api_key : str
            The polygon api key to use for polygon data. Only required if you are using PolygonDataBacktesting as
            the datasource_class.
        polygon_api_key : str
            The polygon api key to use for polygon data. Only required if you are using PolygonDataBacktesting as
            the datasource_class. Depricated, please use 'api_key' instead.
        indicators_file : str
            The file to write the indicators to.
        show_indicators : bool
            Whether to show the indicators plot.
        save_logfile : bool
            Whether to save the logs to a file. If True, the logs will be saved to the logs directory. Defaults to False.
            Turning on this option will slow down the backtest.

        Returns
        -------
        result : dict
            A dictionary of the backtest results. Eg.

        Examples
        --------

        >>> from datetime import datetime
        >>> from lumibot.backtesting import YahooDataBacktesting
        >>> from lumibot.strategies import Strategy
        >>>
        >>> # A simple strategy that buys AAPL on the first day
        >>> class MyStrategy(Strategy):
        >>>    def on_trading_iteration(self):
        >>>        if self.first_iteration:
        >>>            order = self.create_order("AAPL", quantity=1, side="buy")
        >>>            self.submit_order(order)
        >>>
        >>> # Create a backtest
        >>> backtesting_start = datetime(2018, 1, 1)
        >>> backtesting_end = datetime(2018, 1, 31)
        >>>
        >>> # The benchmark asset to use for the backtest to compare to
        >>> benchmark_asset = Asset(symbol="QQQ", asset_type="stock")
        >>>
        >>> backtest = MyStrategy.backtest(
        >>>     datasource_class=YahooDataBacktesting,
        >>>     backtesting_start=backtesting_start,
        >>>     backtesting_end=backtesting_end,
        >>>     benchmark_asset=benchmark_asset,
        >>> )
        """
        """Backtest a strategy."""

        # Create a dictionary of keyword arguments to pass to the run_backtest method
        run_backtest_kwargs = {
            "minutes_before_closing": minutes_before_closing,
            "minutes_before_opening": minutes_before_opening,
            "sleeptime": sleeptime,
            "stats_file": stats_file,
            "risk_free_rate": risk_free_rate,
            "logfile": logfile,
            "config": config,
            "auto_adjust": auto_adjust,
            "name": name,
            "budget": budget,
            "benchmark_asset": benchmark_asset,
            "plot_file_html": plot_file_html,
            "trades_file": trades_file,
            "settings_file": settings_file,
            "pandas_data": pandas_data,
            "quote_asset": quote_asset,
            "starting_positions": starting_positions,
            "show_plot": show_plot,
            "tearsheet_file": tearsheet_file,
            "save_tearsheet": save_tearsheet,
            "show_tearsheet": show_tearsheet,
            "parameters": parameters,
            "buy_trading_fees": buy_trading_fees,
            "sell_trading_fees": sell_trading_fees,
            "api_key": api_key,
            "indicators_file": indicators_file,
            "show_indicators": show_indicators,
            "save_logfile": save_logfile,
            **kwargs,
        }

        # Include Polygon-specific arguments if the datasource_class is PolygonDataBacktesting
        if datasource_class == PolygonDataBacktesting:
            run_backtest_kwargs["polygon_api_key"] = polygon_api_key
            run_backtest_kwargs["polygon_has_paid_subscription"] = polygon_has_paid_subscription

        results, strategy = cls.run_backtest(
            datasource_class,  # Pass the datasource_class here
            *args,
            **run_backtest_kwargs,
        )

        return results, strategy
Al4ise commented 3 months ago

Hey, polygon_api_key and polygon_has_paid subscription are deprecated either way. Could you try what I've done in the PR and see if it works for you?

ghzgod commented 3 months ago

Seems to be working! Thanks for the quick commit. Just discovered this tool and it's pretty rad!

Al4ise commented 3 months ago

No worries :)