diff --git a/lumibot/backtesting/backtesting_broker.py b/lumibot/backtesting/backtesting_broker.py index 06b9b07bc..394caad57 100644 --- a/lumibot/backtesting/backtesting_broker.py +++ b/lumibot/backtesting/backtesting_broker.py @@ -580,6 +580,8 @@ def process_pending_orders(self, strategy): ############################# # Get OHLCV data for the asset ############################# + bid = None + ask = None # Get the OHLCV data for the asset if we're using the YAHOO, CCXT data source data_source_name = self.data_source.SOURCE.upper() @@ -637,13 +639,21 @@ def process_pending_orders(self, strategy): low = df["low"].iloc[0] close = df["close"].iloc[0] volume = df["volume"].iloc[0] + if 'bid' in df.columns and 'ask' in df.columns: + bid = df["bid"].iloc[0] + ask = df["ask"].iloc[0] ############################# # Determine transaction price. ############################# if order.type == "market": - price = open + if order.side == "buy" and ask is not None: + price = ask + elif order.side == "sell" and bid is not None: + price = bid + else: + price = open elif order.type == "limit": price = self.limit_order(order.limit_price, order.side, open, high, low) diff --git a/lumibot/entities/data.py b/lumibot/entities/data.py index 393aab694..f47b80876 100644 --- a/lumibot/entities/data.py +++ b/lumibot/entities/data.py @@ -555,13 +555,6 @@ def get_bars(self, dt, length=1, timestep=MIN_TIMESTEP, timeshift=0): if timestep != "minute" and timestep != "day": raise ValueError(f"Only minute and day are supported for timestep. You provided: {timestep}") - agg_column_map = { - "open": "first", - "high": "max", - "low": "min", - "close": "last", - "volume": "sum", - } if timestep == "day" and self.timestep == "minute": # If the data is minute data and we are requesting daily data then multiply the length by 1440 length = length * 1440 @@ -576,6 +569,20 @@ def get_bars(self, dt, length=1, timestep=MIN_TIMESTEP, timeshift=0): if data is None: return None + agg_column_map = { + "open": "first", + "high": "max", + "low": "min", + "close": "last", + "volume": "sum", + } + # check if 'bid' and 'ask' are in the data and add them to the agg_column_map + if 'bid' in data and 'ask' in data: + agg_column_map['bid'] = "max" + agg_column_map['ask'] = "min" + agg_column_map['bid_size'] = "sum" + agg_column_map['ask_size'] = "sum" + df = pd.DataFrame(data).assign(datetime=lambda df: pd.to_datetime(df['datetime'])).set_index('datetime') df_result = df.resample(f"{quantity}{unit}").agg(agg_column_map) diff --git a/tests/backtest/test_thetadata.py b/tests/backtest/test_thetadata.py index 1509b4426..4aaba4963 100644 --- a/tests/backtest/test_thetadata.py +++ b/tests/backtest/test_thetadata.py @@ -284,7 +284,7 @@ def verify_backtest_results(self, theta_strat_obj): assert 130.0 < theta_strat_obj.prices[asset_order_id] < 140.0, "Valid asset price between 130 and 140" assert 130.0 < stock_order.get_fill_price() < 140.0, "Valid asset price between 130 and 140" assert theta_strat_obj.prices[option_order_id] == 4.5, "Price is $4.5 on 08/01/2023 12:30pm" - assert option_order.get_fill_price() == 4.5, "Fills at 1st candle open price of $4.10 on 08/01/2023" + assert option_order.get_fill_price() == 4.55, "Fills at 1st candle open price of $4.10 on 08/01/2023" assert option_order.is_filled()