diff --git a/lumibot/backtesting/polygon_backtesting.py b/lumibot/backtesting/polygon_backtesting.py index 173c08ac6..a7bb152ec 100644 --- a/lumibot/backtesting/polygon_backtesting.py +++ b/lumibot/backtesting/polygon_backtesting.py @@ -2,6 +2,8 @@ import traceback from collections import OrderedDict, defaultdict from datetime import date, timedelta +from decimal import Decimal +from typing import Union from polygon.exceptions import BadResponse from termcolor import colored @@ -216,7 +218,7 @@ def get_historical_prices_between_dates( bars = self._parse_source_symbol_bars(response, asset, quote=quote) return bars - def get_last_price(self, asset, timestep="minute", quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, timestep="minute", quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: try: dt = self.get_datetime() self._update_pandas_data(asset, quote, 1, timestep, dt) diff --git a/lumibot/backtesting/thetadata_backtesting.py b/lumibot/backtesting/thetadata_backtesting.py index a7ee26ece..846cf0ddc 100644 --- a/lumibot/backtesting/thetadata_backtesting.py +++ b/lumibot/backtesting/thetadata_backtesting.py @@ -1,6 +1,9 @@ import logging import re import traceback +from decimal import Decimal +from typing import Union + import pandas as pd import subprocess from datetime import date, datetime, timedelta @@ -247,7 +250,7 @@ def get_historical_prices_between_dates( bars = self._parse_source_symbol_bars(response, asset, quote=quote) return bars - def get_last_price(self, asset, timestep="minute", quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, timestep="minute", quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: try: dt = self.get_datetime() self._update_pandas_data(asset, quote, 1, timestep, dt) diff --git a/lumibot/brokers/broker.py b/lumibot/brokers/broker.py index 3ce2fa0e2..4e64b9d63 100644 --- a/lumibot/brokers/broker.py +++ b/lumibot/brokers/broker.py @@ -304,7 +304,7 @@ def sync_positions(self, strategy): # =========Market functions======================= - def get_last_price(self, asset: Asset, quote=None, exchange=None) -> float: + def get_last_price(self, asset: Asset, quote=None, exchange=None) -> Union[float, Decimal, None]: """ Takes an asset and returns the last known price @@ -319,7 +319,7 @@ def get_last_price(self, asset: Asset, quote=None, exchange=None) -> float: Returns ------- - float + float or Decimal or None The last known price of the asset. """ if self.option_source and asset.asset_type == "option": diff --git a/lumibot/data_sources/alpaca_data.py b/lumibot/data_sources/alpaca_data.py index 953815254..bdaf24bff 100644 --- a/lumibot/data_sources/alpaca_data.py +++ b/lumibot/data_sources/alpaca_data.py @@ -1,5 +1,7 @@ import logging from datetime import datetime, timedelta, timezone +from decimal import Decimal +from typing import Union import pandas as pd from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient @@ -141,7 +143,7 @@ def get_chains(self, asset: Asset, quote=None, exchange: str = None): "feature, please use a different data source." ) - def get_last_price(self, asset, quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: if quote is not None: # If the quote is not None, we use it even if the asset is a tuple if type(asset) == Asset and asset.asset_type == "stock": diff --git a/lumibot/data_sources/ccxt_backtesting_data.py b/lumibot/data_sources/ccxt_backtesting_data.py index 6395909c7..e93e27732 100644 --- a/lumibot/data_sources/ccxt_backtesting_data.py +++ b/lumibot/data_sources/ccxt_backtesting_data.py @@ -12,7 +12,8 @@ from lumibot.tools import CcxtCacheDB from pandas import DataFrame -from typing import Union,Any +from typing import Union, Any, Dict + class CcxtBacktestingData(DataSourceBacktesting): """Use CcxtCacheDB to download and cache data. @@ -107,7 +108,7 @@ def _pull_source_symbol_bars( data = self._data_store[symbol_timestep] else: data = self._pull_source_bars([asset],length,timestep,timeshift,quote,include_after_hours) - if data is None or len(data) == 0: + if data is None or data[symbol] is None or data[symbol].empty: message = f"{self.SOURCE} did not return data for asset {symbol}. Make sure this symbol is valid." logging.error(message) return None @@ -125,10 +126,15 @@ def _pull_source_symbol_bars( return result_data.tail(length) - - def _pull_source_bars(self, assets:tuple[Asset,Asset], length:int, timestep:str=MIN_TIMESTEP, - timeshift:int=None, quote:Asset=None, include_after_hours:bool=False - )->DataFrame: + def _pull_source_bars( + self, + assets: tuple[Asset,Asset], + length: int, + timestep: str = MIN_TIMESTEP, + timeshift: int = None, + quote: Asset = None, + include_after_hours: bool = False + ) -> Dict: """pull broker bars for a list assets""" parsed_timestep = self._parse_source_timestep(timestep, reverse=True) @@ -150,17 +156,18 @@ def _pull_source_bars(self, assets:tuple[Asset,Asset], length:int, timestep:str= else: start_dt = start_dt - timedelta(minutes=self._download_start_dt_prebuffer) + data = self.cache_db.download_ohlcv( + symbol,parsed_timestep, + start_dt, + end_dt + ) - data = self.cache_db.download_ohlcv(symbol,parsed_timestep, - start_dt,end_dt) - - data.index = data.index.tz_localize("UTC") + data.index = data.index.tz_localize("UTC") data.index = data.index.tz_convert(LUMIBOT_DEFAULT_PYTZ) result[symbol] = data return result - def get_historical_prices(self, asset:tuple[Asset,Asset], length:int, timestep:str=None, timeshift:int=None, quote:Asset=None, exchange:Any=None, include_after_hours:bool=True )->Bars: @@ -231,8 +238,7 @@ def _parse_source_symbol_bars(self, response:DataFrame, asset:tuple[Asset,Asset] bars = Bars(response, self.SOURCE, asset, quote=quote, raw=response) return bars - - def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: """Takes an asset and returns the last known price of close""" if timestep is None: timestep = self.get_timestep() diff --git a/lumibot/data_sources/ccxt_data.py b/lumibot/data_sources/ccxt_data.py index 6ae6416dc..5036a9a4d 100644 --- a/lumibot/data_sources/ccxt_data.py +++ b/lumibot/data_sources/ccxt_data.py @@ -1,6 +1,8 @@ import datetime import logging import time +from decimal import Decimal +from typing import Union import ccxt import pandas as pd @@ -209,7 +211,7 @@ def _parse_source_symbol_bars(self, response, asset, quote=None, length=None): bars = Bars(response, self.SOURCE, asset, quote=quote, raw=response) return bars - def get_last_price(self, asset, quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: if quote is not None: symbol = f"{asset.symbol}/{quote.symbol}" else: diff --git a/lumibot/data_sources/data_source.py b/lumibot/data_sources/data_source.py index be85f47cb..ea9d6261a 100644 --- a/lumibot/data_sources/data_source.py +++ b/lumibot/data_sources/data_source.py @@ -4,6 +4,8 @@ from datetime import datetime, timedelta import traceback import time +from decimal import Decimal +from typing import Union import pandas as pd @@ -101,7 +103,7 @@ def get_historical_prices( pass @abstractmethod - def get_last_price(self, asset, quote=None, exchange=None) -> float: + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: """ Takes an asset and returns the last known price @@ -116,7 +118,7 @@ def get_last_price(self, asset, quote=None, exchange=None) -> float: Returns ------- - float + float or Decimal or None The last known price of the asset. """ pass diff --git a/lumibot/data_sources/example_broker_data.py b/lumibot/data_sources/example_broker_data.py index ef6c7945e..ade271c76 100644 --- a/lumibot/data_sources/example_broker_data.py +++ b/lumibot/data_sources/example_broker_data.py @@ -1,4 +1,7 @@ import logging +from decimal import Decimal +from typing import Union + from termcolor import colored from lumibot.entities import Asset, Bars from lumibot.data_sources import DataSource @@ -25,6 +28,6 @@ def get_historical_prices( logging.error(colored("Method 'get_historical_prices' is not yet implemented.", "red")) return None # Return None as a placeholder - def get_last_price(self, asset, quote=None, exchange=None) -> float: + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: logging.error(colored("Method 'get_last_price' is not yet implemented.", "red")) return 0.0 # Return 0.0 as a placeholder \ No newline at end of file diff --git a/lumibot/data_sources/interactive_brokers_data.py b/lumibot/data_sources/interactive_brokers_data.py index f0d9304e0..e977b21e4 100644 --- a/lumibot/data_sources/interactive_brokers_data.py +++ b/lumibot/data_sources/interactive_brokers_data.py @@ -1,5 +1,7 @@ import datetime import math +from decimal import Decimal +from typing import Union import pandas as pd @@ -340,7 +342,7 @@ def get_historical_prices( bars = self._parse_source_symbol_bars(response, asset, quote=quote, length=length) return bars - def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: if exchange is None: exchange = "SMART" diff --git a/lumibot/data_sources/interactive_brokers_rest_data.py b/lumibot/data_sources/interactive_brokers_rest_data.py index 64183170c..4ac00574a 100644 --- a/lumibot/data_sources/interactive_brokers_rest_data.py +++ b/lumibot/data_sources/interactive_brokers_rest_data.py @@ -1,4 +1,7 @@ import logging +from decimal import Decimal +from typing import Union + from termcolor import colored from lumibot import LUMIBOT_DEFAULT_PYTZ @@ -842,7 +845,7 @@ def get_historical_prices( return bars - def get_last_price(self, asset, quote=None, exchange=None) -> float | None: + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: field = "last_price" response = self.get_market_snapshot(asset, [field]) # TODO add exchange diff --git a/lumibot/data_sources/pandas_data.py b/lumibot/data_sources/pandas_data.py index e3c544c79..d88e1a096 100644 --- a/lumibot/data_sources/pandas_data.py +++ b/lumibot/data_sources/pandas_data.py @@ -1,6 +1,8 @@ import logging from collections import defaultdict, OrderedDict from datetime import timedelta +from decimal import Decimal +from typing import Union import pandas as pd from lumibot.data_sources import DataSourceBacktesting @@ -178,7 +180,7 @@ def update_date_index(self): return dt_index - def get_last_price(self, asset, quote=None, exchange=None): + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: # Takes an asset and returns the last known price tuple_to_find = self.find_asset_in_data_store(asset, quote) diff --git a/lumibot/data_sources/tradier_data.py b/lumibot/data_sources/tradier_data.py index 3bc90720a..8df4425ca 100644 --- a/lumibot/data_sources/tradier_data.py +++ b/lumibot/data_sources/tradier_data.py @@ -1,6 +1,8 @@ import logging from collections import defaultdict from datetime import datetime, date, timedelta +from decimal import Decimal +from typing import Union import pandas as pd @@ -255,7 +257,7 @@ def get_historical_prices( return bars - def get_last_price(self, asset, quote=None, exchange=None): + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: """ This function returns the last price of an asset. Parameters @@ -269,7 +271,7 @@ def get_last_price(self, asset, quote=None, exchange=None): Returns ------- - float + float or Decimal or none Price of the asset """ diff --git a/lumibot/data_sources/yahoo_data.py b/lumibot/data_sources/yahoo_data.py index 1c9907495..cc46f9d52 100644 --- a/lumibot/data_sources/yahoo_data.py +++ b/lumibot/data_sources/yahoo_data.py @@ -1,6 +1,7 @@ import logging from datetime import timedelta from decimal import Decimal +from typing import Union import numpy @@ -131,7 +132,7 @@ def _parse_source_symbol_bars(self, response, asset, quote=None, length=None): bars = Bars(response, self.SOURCE, asset, raw=response) return bars - def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs): + def get_last_price(self, asset, timestep=None, quote=None, exchange=None, **kwargs) -> Union[float, Decimal, None]: """Takes an asset and returns the last known price""" if timestep is None: timestep = self.get_timestep() diff --git a/lumibot/entities/bars.py b/lumibot/entities/bars.py index 48e084cc8..3f1047de2 100644 --- a/lumibot/entities/bars.py +++ b/lumibot/entities/bars.py @@ -1,5 +1,7 @@ import logging from datetime import datetime +from decimal import Decimal +from typing import Union import pandas as pd @@ -168,7 +170,7 @@ def split(self): return result - def get_last_price(self): + def get_last_price(self) -> Union[float, Decimal, None]: """Return the last price of the last bar Parameters @@ -177,7 +179,7 @@ def get_last_price(self): Returns ------- - float + float, Decimal or None """ return self.df["close"].iloc[-1] diff --git a/lumibot/entities/data.py b/lumibot/entities/data.py index b040e7e1b..ab25136b1 100644 --- a/lumibot/entities/data.py +++ b/lumibot/entities/data.py @@ -1,6 +1,8 @@ import datetime import logging import re +from decimal import Decimal +from typing import Union import pandas as pd from lumibot import LUMIBOT_DEFAULT_PYTZ as DEFAULT_PYTZ @@ -383,7 +385,7 @@ def checker(self, *args, **kwargs): return checker @check_data - def get_last_price(self, dt, length=1, timeshift=0): + def get_last_price(self, dt, length=1, timeshift=0) -> Union[float, Decimal, None]: """Returns the last known price of the data. Parameters @@ -399,7 +401,7 @@ def get_last_price(self, dt, length=1, timeshift=0): Returns ------- - float + float or Decimal or None """ iter_count = self.get_iter_count(dt) open_price = self.datalines["open"].dataline[iter_count] diff --git a/lumibot/example_strategies/ccxt_backtesting_example.py b/lumibot/example_strategies/ccxt_backtesting_example.py index a699f6485..7d81b46ac 100644 --- a/lumibot/example_strategies/ccxt_backtesting_example.py +++ b/lumibot/example_strategies/ccxt_backtesting_example.py @@ -24,11 +24,13 @@ def initialize(self, asset:tuple[Asset,Asset] = None, def _position_sizing(self): cash = self.get_cash() last_price = self.get_last_price(asset=self.asset,quote=self.quote) + if last_price is None: + return cash, last_price, 0.0 quantity = round(cash * self.cash_at_risk / last_price,0) return cash, last_price, quantity def _get_historical_prices(self): - return self.get_historical_prices(asset=self.asset,length=None, + return self.get_historical_prices(asset=self.asset,length=self.window, timestep="day",quote=self.quote).df def _get_bbands(self,history_df:DataFrame): diff --git a/lumibot/strategies/strategy.py b/lumibot/strategies/strategy.py index 87fa850a9..9e269d5d3 100644 --- a/lumibot/strategies/strategy.py +++ b/lumibot/strategies/strategy.py @@ -1651,7 +1651,7 @@ def sell_all(self, cancel_open_orders: bool = True, is_multileg: bool = False): """ self.broker.sell_all(self.name, cancel_open_orders=cancel_open_orders, strategy=self, is_multileg=is_multileg) - def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None): + def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None) -> Union[float, Decimal, None]: """Takes an asset and returns the last known price Makes an active call to the market to retrieve the last price. diff --git a/tests/backtest/test_example_strategies.py b/tests/backtest/test_example_strategies.py index 749390659..edc0d51ee 100644 --- a/tests/backtest/test_example_strategies.py +++ b/tests/backtest/test_example_strategies.py @@ -245,8 +245,7 @@ def test_options_hold_to_expiry(self): assert round(cash_settled_orders.iloc[0]["price"], 0) == 0 assert cash_settled_orders.iloc[0]["filled_quantity"] == 10 - @pytest.mark.skip(reason="This test is skipped because the CCXTBackTesting data source is not currently " - "returning data for this date range.") + @pytest.mark.skip() # Skip this test; it works locally but i can't get it to work on github actions def test_ccxt_backtesting(self): """ Test the example strategy StockBracket by running a backtest and checking that the strategy object is returned diff --git a/tests/test_brokers_handle_crypto.py b/tests/test_brokers_handle_crypto.py index 93de6f026..ecc976aec 100644 --- a/tests/test_brokers_handle_crypto.py +++ b/tests/test_brokers_handle_crypto.py @@ -2,8 +2,9 @@ from datetime import datetime, timedelta from typing import assert_type +import pytz from lumibot.entities import Asset, Order, Bars -from lumibot.backtesting import BacktestingBroker, PolygonDataBacktesting, YahooDataBacktesting +from lumibot.backtesting import BacktestingBroker, PolygonDataBacktesting, YahooDataBacktesting, CcxtBacktesting from lumibot.brokers.alpaca import Alpaca from lumibot.credentials import ALPACA_CONFIG, POLYGON_CONFIG @@ -48,7 +49,7 @@ def test_yahoo_backtesting_with_symbol(self): strategy="test", asset=asset, quantity=1, - side="buy", + side=Order.OrderSide.BUY, limit_price=limit_price, ) assert order.status == "unprocessed" @@ -88,7 +89,7 @@ def test_yahoo_backtesting_with_base_and_quote(self): strategy="test", asset=self.base, quantity=1, - side="buy", + side=Order.OrderSide.BUY, limit_price=limit_price, quote=self.quote ) @@ -148,7 +149,7 @@ def test_polygon_backtesting_with_base_and_quote(self): strategy="test", asset=self.base, quantity=1, - side="buy", + side=Order.OrderSide.BUY, limit_price=limit_price, quote=self.quote ) @@ -195,7 +196,57 @@ def test_alpaca_broker_with_base_and_quote(self): strategy="test", asset=self.base, quantity=1, - side="buy", + side=Order.OrderSide.BUY, + limit_price=limit_price, + quote=self.quote + ) + assert order.status == "unprocessed" + broker.submit_order(order) + assert order.status == "new" + broker.cancel_order(order) + + def test_ccxt_backtesting_with_base_and_quote(self): + start = (datetime.now() - timedelta(days=4)).replace(hour=0, minute=0, second=0, microsecond=0) + end = (datetime.now() - timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0) + kwargs = { + # "max_data_download_limit":10000, # optional + "exchange_id": "kraken" #"kucoin" #"bybit" #"okx" #"bitmex" # "binance" + } + data_source = CcxtBacktesting( + datetime_start=start, + datetime_end=end, + **kwargs + ) + broker = BacktestingBroker(data_source=data_source) + + # test_get_last_price + last_price = broker.data_source.get_last_price(asset=self.base, quote=self.quote) + assert_type(last_price, float) + assert last_price > 0.0 + + # test_get_historical_prices + bars = broker.data_source.get_historical_prices( + asset=self.base, + length=self.length, + timestep=self.timestep, + quote=self.quote + ) + + assert_type(bars, Bars) + assert len(bars.df) == self.length + # get the date of the last bar, which should be the day before the start date + last_date = bars.df.index[-1] + assert last_date.date() == (start - timedelta(days=1)).date() + last_price = bars.df['close'].iloc[-1] + assert last_price > 0.0 + + # test_submit_limit_order + limit_price = 1.0 # Make sure we never hit this price + order = Order( + strategy="test", + asset=self.base, + quantity=1, + side=Order.OrderSide.BUY, limit_price=limit_price, quote=self.quote ) diff --git a/tests/test_data_source.py b/tests/test_data_source.py index 6f242d93f..aa0966c03 100644 --- a/tests/test_data_source.py +++ b/tests/test_data_source.py @@ -1,3 +1,6 @@ +from decimal import Decimal +from typing import Union + from lumibot.data_sources.data_source import DataSource from lumibot.entities import Asset @@ -9,7 +12,7 @@ def __init__(self, api_key): def get_chains(self, asset: Asset, quote: Asset = None) -> dict: return {} - def get_last_price(self, asset, quote=None, exchange=None): + def get_last_price(self, asset, quote=None, exchange=None) -> Union[float, Decimal, None]: return 0.0 def get_historical_prices( diff --git a/tests/test_drift_rebalancer.py b/tests/test_drift_rebalancer.py index f0426bd73..76af7ef35 100644 --- a/tests/test_drift_rebalancer.py +++ b/tests/test_drift_rebalancer.py @@ -44,7 +44,7 @@ def __init__( fractional_shares=fractional_shares ) - def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None): + def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None) -> Union[float, Decimal, None]: return Decimal(100.0) # Mock price def update_broker_balances(self, force_update: bool = False) -> None: @@ -866,7 +866,7 @@ def __init__( fractional_shares=fractional_shares ) - def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None): + def get_last_price(self, asset: Union[Asset, str], quote=None, exchange=None) -> Union[float, Decimal, None]: return Decimal(100.0) # Mock price def update_broker_balances(self, force_update: bool = False) -> None: diff --git a/tests/test_get_historical_prices.py b/tests/test_get_historical_prices.py index 39b777eb9..55d6fe5aa 100644 --- a/tests/test_get_historical_prices.py +++ b/tests/test_get_historical_prices.py @@ -8,7 +8,7 @@ import pytz from pandas.testing import assert_series_equal -from lumibot.backtesting import PolygonDataBacktesting, YahooDataBacktesting +from lumibot.backtesting import PolygonDataBacktesting, YahooDataBacktesting, CcxtBacktesting from lumibot.data_sources import AlpacaData, TradierData, PandasData from tests.fixtures import pandas_data_fixture from lumibot.tools import print_full_pandas_dataframes, set_pandas_float_display_precision @@ -76,6 +76,21 @@ def get_first_trading_day_after_long_weekend(self, year): first_trading_day = mlk_date + timedelta(days=1) return first_trading_day + # noinspection PyMethodMayBeStatic + def check_date_of_last_bar_is_date_of_day_before_backtest_start_for_crypto( + self, bars: Bars, + backtesting_start: datetime + ): + # The current behavior of the backtesting data sources is to return the data for the + # last trading day before now. + # To simulate this, we set backtesting_start date to what we want "now" to be. + # So based on the backtesting_start date, the last bar should be the bar from the previous day + # before the backtesting_start date. Since this is crypto and it trades 24/7, we don't care about + # trading days. + + previous_day_date = backtesting_start - timedelta(days=1) + assert bars.df.index[-1].date() == previous_day_date.date() + # noinspection PyMethodMayBeStatic def check_date_of_last_bar_is_date_of_last_trading_date_before_backtest_start( self, bars: Bars, @@ -179,6 +194,7 @@ def test_get_first_trading_day_after_long_weekend(self): first_trading_day_after_mlk = self.get_first_trading_day_after_long_weekend(2023) assert first_trading_day_after_mlk == datetime(2023, 1, 17) + # @pytest.mark.skip() def test_pandas_backtesting_data_source_get_historical_prices_daily_bars_dividends_and_adj_returns(self, pandas_data_fixture): """ This tests that the pandas data_source calculates adjusted returns for bars and that they @@ -199,6 +215,7 @@ def test_pandas_backtesting_data_source_get_historical_prices_daily_bars_dividen ) self.check_dividends_and_adjusted_returns(bars) + # @pytest.mark.skip() def test_pandas_backtesting_data_source_get_historical_prices_daily_bars_for_backtesting_broker( self, pandas_data_fixture @@ -226,6 +243,7 @@ def test_pandas_backtesting_data_source_get_historical_prices_daily_bars_for_bac backtesting_start=backtesting_start ) + # @pytest.mark.skip() def test_pandas_backtesting_data_source_get_historical_prices_daily_bars_over_long_weekend( self, pandas_data_fixture @@ -327,6 +345,7 @@ def test_polygon_backtesting_data_source_get_historical_prices_daily_bars_over_l backtesting_start=backtesting_start ) + # @pytest.mark.skip() def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_dividends_and_adj_returns( self, pandas_data_fixture @@ -350,6 +369,7 @@ def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_dividend backtesting_start=backtesting_start ) + # @pytest.mark.skip() def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_for_backtesting_broker( self, pandas_data_fixture @@ -379,6 +399,7 @@ def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_for_back backtesting_start=backtesting_start ) + # @pytest.mark.skip() def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_over_long_weekend( self, pandas_data_fixture @@ -404,8 +425,39 @@ def test_yahoo_backtesting_data_source_get_historical_prices_daily_bars_over_lon backtesting_start=backtesting_start ) + def test_kraken_ccxt_backtesting_data_source_get_historical_prices_daily_bars( + self + ): + """ + This tests that the kraken ccxt data_source gets the right bars + """ + backtesting_start = (datetime.now() - timedelta(days=4)).replace(hour=0, minute=0, second=0, microsecond=0) + backtesting_end = (datetime.now() - timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0) + base = Asset(symbol='BTC', asset_type='crypto') + quote = Asset(symbol='USD', asset_type='forex') + timestep = "day" + kwargs = { + # "max_data_download_limit":10000, # optional + "exchange_id": "kraken" # "kucoin" #"bybit" #"okx" #"bitmex" # "binance" + } + data_source = CcxtBacktesting( + datetime_start=backtesting_start, + datetime_end=backtesting_end, + **kwargs + ) + bars = data_source.get_historical_prices( + asset=(base,quote), + length=self.length, + timestep=self.timestep + ) + check_bars(bars=bars, length=self.length) + self.check_date_of_last_bar_is_date_of_day_before_backtest_start_for_crypto( + bars, + backtesting_start=backtesting_start + ) + -@pytest.mark.skip() +# @pytest.mark.skip() class TestDatasourceGetHistoricalPricesDailyData: """These tests check the daily Bars returned from get_historical_prices for live data sources."""