Skip to content

Commit be80c86

Browse files
authored
Merge pull request #395 from coding-kitties/squad/350-add-ccxt-ticker-data-provider
feat: add CCXTTickerDataProvider for TICKER data sources (#350)
2 parents b4dc1c8 + 50ac874 commit be80c86

File tree

6 files changed

+382
-9
lines changed

6 files changed

+382
-9
lines changed

investing_algorithm_framework/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
SnapshotInterval, AWS_S3_STATE_BUCKET_NAME, BacktestEvaluationFocus, \
2525
save_backtests_to_directory, BacktestMetrics, DATA_DIRECTORY
2626
from .infrastructure import AzureBlobStorageStateHandler, \
27-
CSVOHLCVDataProvider, CCXTOHLCVDataProvider, PandasOHLCVDataProvider, \
27+
CSVOHLCVDataProvider, CCXTOHLCVDataProvider, CCXTTickerDataProvider, \
28+
PandasOHLCVDataProvider, \
2829
AWSS3StorageStateHandler
2930
from .create_app import create_app
3031
from .download_data import download, download_v2, DownloadResult, \
@@ -110,6 +111,7 @@
110111
'DataType',
111112
'CSVOHLCVDataProvider',
112113
"CCXTOHLCVDataProvider",
114+
"CCXTTickerDataProvider",
113115
"DataProvider",
114116
"get_annual_volatility",
115117
"get_sortino_ratio",

investing_algorithm_framework/infrastructure/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
BacktestService
1313
from .data_providers import CSVOHLCVDataProvider, get_default_data_providers, \
1414
get_default_ohlcv_data_providers, CCXTOHLCVDataProvider, \
15-
PandasOHLCVDataProvider
15+
CCXTTickerDataProvider, PandasOHLCVDataProvider
1616
from .order_executors import CCXTOrderExecutor, BacktestOrderExecutor
1717
from .portfolio_providers import CCXTPortfolioProvider
1818

@@ -46,6 +46,7 @@
4646
"get_default_ohlcv_data_providers",
4747
"AWSS3StorageStateHandler",
4848
"CCXTOHLCVDataProvider",
49+
"CCXTTickerDataProvider",
4950
"BacktestOrderExecutor",
5051
"PandasOHLCVDataProvider",
5152
"BacktestService",

investing_algorithm_framework/infrastructure/data_providers/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .ccxt import CCXTOHLCVDataProvider
1+
from .ccxt import CCXTOHLCVDataProvider, CCXTTickerDataProvider
22
from .csv import CSVOHLCVDataProvider
33
from .pandas import PandasOHLCVDataProvider
44

@@ -12,6 +12,7 @@ def get_default_data_providers():
1212
"""
1313
return [
1414
CCXTOHLCVDataProvider(),
15+
CCXTTickerDataProvider(),
1516
]
1617

1718

@@ -30,6 +31,7 @@ def get_default_ohlcv_data_providers():
3031
__all__ = [
3132
'CSVOHLCVDataProvider',
3233
'CCXTOHLCVDataProvider',
34+
'CCXTTickerDataProvider',
3335
'get_default_data_providers',
3436
'get_default_ohlcv_data_providers',
3537
'PandasOHLCVDataProvider',

investing_algorithm_framework/infrastructure/data_providers/ccxt.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,3 +1256,162 @@ def get_data_source_file_path(self) -> Union[str, None]:
12561256
locally, otherwise None.
12571257
"""
12581258
return self.data_file_path
1259+
1260+
1261+
class CCXTTickerDataProvider(DataProvider):
1262+
"""
1263+
Data provider for ticker data using the CCXT library.
1264+
1265+
Fetches real-time ticker data (bid, ask, last price, volume, etc.)
1266+
for a given symbol and market via CCXT's fetch_ticker API.
1267+
1268+
In backtest mode, ticker data is derived from OHLCV data
1269+
(handled by the DataProviderService fallback), so this provider
1270+
only serves live/non-backtest use cases.
1271+
"""
1272+
data_type = DataType.TICKER
1273+
data_provider_identifier = "ccxt_ticker_data_provider"
1274+
1275+
def __init__(
1276+
self,
1277+
symbol: str = None,
1278+
market: str = None,
1279+
data_provider_identifier: str = None,
1280+
config=None
1281+
):
1282+
if data_provider_identifier is None:
1283+
data_provider_identifier = self.data_provider_identifier
1284+
1285+
super().__init__(
1286+
symbol=symbol,
1287+
market=market,
1288+
data_provider_identifier=data_provider_identifier,
1289+
config=config
1290+
)
1291+
1292+
def has_data(
1293+
self,
1294+
data_source: DataSource,
1295+
start_date: datetime = None,
1296+
end_date: datetime = None,
1297+
) -> bool:
1298+
data_type = data_source.data_type
1299+
market = data_source.market
1300+
symbol = data_source.symbol
1301+
1302+
if not DataType.TICKER.equals(data_type):
1303+
return False
1304+
1305+
if market is None:
1306+
market = "binance"
1307+
1308+
try:
1309+
market = market.lower()
1310+
exchange_class = getattr(ccxt, market)
1311+
exchange = exchange_class()
1312+
symbols = list(exchange.load_markets().keys())
1313+
return symbol in symbols
1314+
except ccxt.NetworkError:
1315+
return False
1316+
except Exception as e:
1317+
logger.error(e)
1318+
return False
1319+
1320+
def prepare_backtest_data(
1321+
self,
1322+
backtest_start_date,
1323+
backtest_end_date,
1324+
fill_missing_data: bool = False,
1325+
show_progress: bool = False,
1326+
) -> None:
1327+
# Ticker backtest data is derived from OHLCV by the
1328+
# DataProviderService fallback — nothing to prepare here.
1329+
pass
1330+
1331+
def get_backtest_data(
1332+
self,
1333+
backtest_index_date: datetime,
1334+
backtest_start_date: datetime = None,
1335+
backtest_end_date: datetime = None,
1336+
data_source: DataSource = None,
1337+
):
1338+
# Backtest ticker data is handled by DataProviderService
1339+
# falling back to OHLCV data.
1340+
return None
1341+
1342+
def get_data(
1343+
self,
1344+
date: datetime = None,
1345+
start_date: datetime = None,
1346+
end_date: datetime = None,
1347+
save: bool = False,
1348+
) -> dict:
1349+
if self.market is None:
1350+
raise OperationalException(
1351+
"Market is not set. Please set the market "
1352+
"before calling get_data."
1353+
)
1354+
1355+
if self.symbol is None:
1356+
raise OperationalException(
1357+
"Symbol is not set. Please set the symbol "
1358+
"before calling get_data."
1359+
)
1360+
1361+
market_credential = self.get_credential(self.market)
1362+
exchange = CCXTOHLCVDataProvider.initialize_exchange(
1363+
self.market, market_credential
1364+
)
1365+
ticker = exchange.fetch_ticker(self.symbol)
1366+
1367+
return {
1368+
"symbol": self.symbol,
1369+
"market": self.market,
1370+
"datetime": ticker.get("datetime"),
1371+
"high": ticker.get("high"),
1372+
"low": ticker.get("low"),
1373+
"bid": ticker.get("bid"),
1374+
"ask": ticker.get("ask"),
1375+
"open": ticker.get("open"),
1376+
"close": ticker.get("close"),
1377+
"last": ticker.get("last"),
1378+
"volume": ticker.get("baseVolume"),
1379+
}
1380+
1381+
def copy(self, data_source: DataSource) -> "CCXTTickerDataProvider":
1382+
if data_source.market is None or data_source.market == "":
1383+
raise OperationalException(
1384+
"DataSource has no `market` attribute specified. "
1385+
"Please specify the market attribute in the data source "
1386+
"specification before using the CCXT ticker data provider."
1387+
)
1388+
1389+
if data_source.symbol is None or data_source.symbol == "":
1390+
raise OperationalException(
1391+
"DataSource has no `symbol` attribute specified. "
1392+
"Please specify the symbol attribute in the data source "
1393+
"specification before using the CCXT ticker data provider."
1394+
)
1395+
1396+
return CCXTTickerDataProvider(
1397+
symbol=data_source.symbol,
1398+
market=data_source.market,
1399+
data_provider_identifier=data_source.data_provider_identifier,
1400+
config=self.config,
1401+
)
1402+
1403+
def get_number_of_data_points(
1404+
self, start_date: datetime, end_date: datetime
1405+
) -> int:
1406+
# Ticker data is a single point-in-time snapshot
1407+
return 1
1408+
1409+
def get_missing_data_dates(
1410+
self, start_date: datetime, end_date: datetime
1411+
) -> list:
1412+
# No stored data to have gaps in
1413+
return []
1414+
1415+
def get_data_source_file_path(self):
1416+
# Ticker data is not file-based
1417+
return None

0 commit comments

Comments
 (0)