Lumiwealth / lumibot

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

sell_all() method has issue where it does not sell all positions and instead jacknifes because of variable "is_multileg" #608

Open andrewtcrooks opened 4 weeks ago

andrewtcrooks commented 4 weeks ago

/opt/homebrew/anaconda3/envs/stockbot/bin/python /Users/andrew/PyCharm/StockBot/stockbot/main.py 2024-11-01 19:41:28 | lumibot.credentials | INFO | .env file loaded from: /Users/andrew/PyCharm/StockBot/stockbot/.env 2024-11-01 19:41:30 | asyncio | INFO | [unknown] Waiting for the socket stream connection to be established, method _stream_established must be called 2024-11-01 19:41:30 | alpaca.trading.stream | INFO | started trading stream 2024-11-01 19:41:30 | alpaca.trading.stream | INFO | starting trading websocket connection 2024-11-01 19:41:30,471 | asyncio | INFO | [Transformer Strategy] Strategy Transformer Strategy is initializing 2024-11-01 19:41:30,471 | asyncio | INFO | [Transformer Strategy] Executing the initialize lifecycle method 2024-11-01 19:41:30,472 | asyncio | INFO | [Transformer Strategy] Initializing Transformer Strategy... 2024-11-01 19:41:30,472 | asyncio | INFO | [Transformer Strategy] Getting historical prices for [AAPL, MSFT], 65 bars, minute 2024-11-01 19:41:30,474 | alpaca.trading.stream | INFO | connected to: wss://paper-api.alpaca.markets/stream 2024-11-01 19:41:30,835 | root | ERROR | Could not get any pricing data from Alpaca for AAPL, the DataFrame came back empty 2024-11-01 19:41:31,057 | root | ERROR | Could not get any pricing data from Alpaca for MSFT, the DataFrame came back empty 2024-11-01 19:41:31,057 | asyncio | INFO | [Transformer Strategy] No historical data fetched during initialization for AAPL. 2024-11-01 19:41:31,057 | asyncio | INFO | [Transformer Strategy] No historical data fetched during initialization for MSFT. 2024-11-01 19:41:31,888 | asyncio | INFO | [Transformer Strategy] Sleeping until the market opens 2024-11-01 19:41:32,229 | asyncio | INFO | [Transformer Strategy] Executing the before_market_opens lifecycle method 2024-11-01 19:41:32,565 | asyncio | INFO | [Transformer Strategy] Sleeping until the market opens 2024-11-01 19:41:32,638 | asyncio | INFO | [Transformer Strategy] Executing the before_starting_trading lifecycle method 2024-11-01 19:41:32,780 | asyncio | INFO | [Transformer Strategy] Strategy will check in again at: 2024-11-01 19:42:00 Traceback (most recent call last): File "/Users/andrew/PyCharm/StockBot/stockbot/main.py", line 37, in main() File "/Users/andrew/PyCharm/StockBot/stockbot/main.py", line 34, in main trader.run_all() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/traders/trader.py", line 140, in run_all self._join_pool() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/traders/trader.py", line 229, in _join_pool strategy_thread.join() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/strategies/strategy_executor.py", line 287, in join super(StrategyExecutor, self).join(timeout) File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/threading.py", line 1096, in join self._wait_for_tstate_lock() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/threading.py", line 1116, in _wait_for_tstate_lock if lock.acquire(block, timeout): File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/traders/trader.py", line 241, in _stop_pool strategy_thread.stop() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/strategies/strategy_executor.py", line 284, in stop self._on_abrupt_closing(KeyboardInterrupt()) File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/strategies/strategy_executor.py", line 517, in _on_abrupt_closing self.strategy.on_abrupt_closing() File "/Users/andrew/PyCharm/StockBot/stockbot/strategies/strategy_transformer/strategy.py", line 206, in on_abrupt_closing self.sell_all() File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/strategies/strategy.py", line 1861, in sell_all self.broker.sell_all(self.name, cancel_open_orders=cancel_open_orders, strategy=self, is_multileg=is_multileg) File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/brokers/broker.py", line 1044, in sell_all self.submit_orders(orders, is_multileg=is_multileg) File "/opt/homebrew/anaconda3/envs/stockbot/lib/python3.10/site-packages/lumibot/brokers/broker.py", line 965, in submit_orders self._submit_orders(orders, **kwargs) TypeError: Broker._submit_orders() got an unexpected keyword argument 'is_multileg' 2024-11-01 19:41:45,097 | asyncio | INFO | [Transformer Strategy] Executing the on_abrupt_closing event method 2024-11-01 19:41:45,097 | asyncio | INFO | [Transformer Strategy] Closed Abruptly! Selling all positions. Abandon ship! 2024-11-01 19:41:45,097 | asyncio | WARNING | [Transformer Strategy] Selling all positions for Transformer Strategy strategy

Process finished with exit code 1

andrewtcrooks commented 4 weeks ago

And can anyone explain why this pulls data up until like 620PM PST and then suddenly stops until sometime before market opens the next day?

andrewtcrooks commented 4 weeks ago

Putting this method inside my custom strategy seems to fix it for now so that it will actually sell off the positions when sell_all is called, even if it's doing it one at a time instead of in a multileg order. I look forward to that functionality being added

  def sell_all(self, cancel_open_orders: bool = True, is_multileg: bool = False):
      """
      Sell all current positions in the portfolio.

      :param is_multileg:
      :param cancel_open_orders: Cancel all open orders if True, leave all orders in place if False. Default is True.
      """
      if cancel_open_orders:
          self.cancel_open_orders()
      positions = self.get_positions()  # Access current positions
      self.log_message(f"Selling all positions...")
      for symbol, position in positions:
          quantity = position.quantity
          if quantity > 0:
              try:
                  # Place a market sell order for the entire quantity
                  self.sell_order(symbol, quantity)
                  self.log_message(f"Placed sell order for {quantity} shares of {symbol}.")
              except Exception as e:
                  self.log_message(f"Error selling {symbol}: {e}")
andrewtcrooks commented 4 weeks ago

The source of the error is the _submit_order method inside broker.py not recognizing the "is_multileg" parameter being passed to it as kwargs.

This brings up an important question, why does the sell_all method need a "multileg" option? shouldn't it inherently sell all the assets with a multileg order to save on costs?

It sounds like fixing it might be an option but simply removing the redundancy might be more beneficial.

andrewtcrooks commented 1 week ago

Best of luck, I'm switching over to QuantConnect so I can just worry about the strategy part. This has been an very educational experience though. Thank you.