Skip to content

Commit d2c8ba0

Browse files
committed
Fix deprecated tiingo crypto rest endpoint
1 parent b6f28e8 commit d2c8ba0

File tree

2 files changed

+55
-98
lines changed

2 files changed

+55
-98
lines changed

data_generator/tiingo_data_service.py

Lines changed: 51 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def get_closes_rest(self, trade_pairs: List[TradePair], time_ms, live=True, verb
367367
return tp_to_price
368368

369369
@exception_handler_decorator()
370-
def get_closes_equities(self, trade_pairs: List[TradePair], target_time_ms, live=True, verbose=False) -> dict[TradePair: PriceSource]:
370+
def get_closes_equities(self, trade_pairs: List[TradePair], target_time_ms: int, live: bool, verbose=False) -> dict[TradePair, PriceSource]:
371371
if not live:
372372
raise Exception('TODO')
373373
tp_to_price = {}
@@ -433,7 +433,7 @@ def target_ms_to_start_end_formatted(self, target_time_ms):
433433
return start_day_formatted, end_day_formatted
434434

435435
@exception_handler_decorator()
436-
def get_closes_forex(self, trade_pairs: List[TradePair], target_time_ms: int, live=True, verbose=False) -> dict:
436+
def get_closes_forex(self, trade_pairs: List[TradePair], target_time_ms: int, live: bool, verbose=False) -> dict:
437437

438438
def tickers_to_tiingo_forex_url(tickers: List[str]) -> str:
439439
if not live:
@@ -524,117 +524,75 @@ def tickers_to_tiingo_forex_url(tickers: List[str]) -> str:
524524
return tp_to_price
525525

526526
@exception_handler_decorator()
527-
def get_closes_crypto(self, trade_pairs: List[TradePair], target_time_ms: int, live=True, verbose=False) -> dict:
528-
tp_to_price = {}
529-
if not trade_pairs:
530-
return tp_to_price
531-
assert all(tp.trade_pair_category == TradePairCategory.CRYPTO for tp in trade_pairs), trade_pairs
527+
def get_closes_crypto(self, trade_pairs: List[TradePair], target_time_ms: int, live: bool, verbose=False) -> dict:
532528

533529
def tickers_to_crypto_url(tickers: List[str]) -> str:
534530
if not live:
535531
# YYYY-MM-DD format.
536532
start_day_formatted, end_day_formatted = self.target_ms_to_start_end_formatted(target_time_ms)
537533
# "https://api.tiingo.com/tiingo/crypto/prices?tickers=btcusd&startDate=2019-01-02&resampleFreq=5min&token=ffb55f7fdd167d4b8047539e6b62d82b92b25f91"
538534
return f"https://api.tiingo.com/tiingo/crypto/prices?tickers={','.join(tickers)}&startDate={start_day_formatted}&endDate={end_day_formatted}&resampleFreq=1min&token={self.config['api_key']}&exchanges={TIINGO_COINBASE_EXCHANGE_STR.upper()}"
539-
return f"https://api.tiingo.com/tiingo/crypto/top?tickers={','.join(tickers)}&token={self.config['api_key']}&exchanges={TIINGO_COINBASE_EXCHANGE_STR.upper()}"
535+
return f"https://api.tiingo.com/tiingo/crypto/prices?tickers={','.join(tickers)}&token={self.config['api_key']}&exchanges={TIINGO_COINBASE_EXCHANGE_STR.upper()}"
536+
537+
tp_to_price = {}
538+
if not trade_pairs:
539+
return tp_to_price
540+
541+
assert all(tp.trade_pair_category == TradePairCategory.CRYPTO for tp in trade_pairs), trade_pairs
540542

541543
url = tickers_to_crypto_url([self.trade_pair_to_tiingo_ticker(x) for x in trade_pairs])
542544
if verbose:
543545
print('hitting url', url)
544546

545547
requestResponse = requests.get(url, headers={'Content-Type': 'application/json'}, timeout=5)
548+
if requestResponse.status_code != 200:
549+
return {}
546550

547-
if requestResponse.status_code == 200:
548-
response_data = requestResponse.json()
551+
response_data = requestResponse.json()
552+
timespan_ms = self.timespan_to_ms['minute']
549553

550-
if not live:
551-
# Historical data has a different structure - the items are in data[0]['priceData']
552-
if not response_data or len(response_data) == 0:
553-
return tp_to_price
554-
for crypto_data in response_data:
555-
ticker = crypto_data['ticker']
556-
557-
# Skip if no price data available
558-
if not crypto_data.get('priceData') or len(crypto_data['priceData']) == 0:
559-
continue
554+
for crypto_data in response_data:
555+
ticker = crypto_data['ticker']
556+
price_data = crypto_data.get('priceData', None)
557+
if not price_data:
558+
continue
560559

561-
# Find the closest price data point to target_time_ms
562-
price_data = sorted(crypto_data['priceData'],
563-
key=lambda x: TimeUtil.parse_iso_to_ms(x['date']))
564-
565-
closest_data = min(price_data,
566-
key=lambda x: abs(TimeUtil.parse_iso_to_ms(x['date']) - target_time_ms))
567-
568-
data_time_ms = TimeUtil.parse_iso_to_ms(closest_data['date'])
569-
price = float(closest_data['close'])
570-
bid_price = ask_price = 0 # Bid/ask not provided in historical data
571-
572-
tp = TradePair.get_latest_trade_pair_from_trade_pair_id(ticker.upper())
573-
source_name = f'{TIINGO_PROVIDER_NAME}_{TIINGO_COINBASE_EXCHANGE_STR}_historical'
574-
exchange = TIINGO_COINBASE_EXCHANGE_STR
575-
576-
# Create PriceSource
577-
tp_to_price[tp] = PriceSource(
578-
source=source_name,
579-
timespan_ms=self.timespan_to_ms['minute'],
580-
open=float(closest_data['open']),
581-
close=price,
582-
vwap=price,
583-
high=float(closest_data['high']),
584-
low=float(closest_data['low']),
585-
start_ms=data_time_ms,
586-
websocket=False,
587-
lag_ms=target_time_ms - data_time_ms,
588-
bid=bid_price,
589-
ask=ask_price
590-
)
591-
592-
if verbose:
593-
self.log_price_info(tp, tp_to_price[tp], target_time_ms, data_time_ms,
594-
closest_data['date'], price, exchange, closest_data)
595-
else:
596-
now_ms = TimeUtil.now_in_millis()
597-
# Current data format (top endpoint)
598-
for crypto_data in response_data:
599-
ticker = crypto_data['ticker']
600-
if len(crypto_data['topOfBookData']) != 1:
601-
print('Tiingo unexpected data', crypto_data)
602-
continue
560+
# Find the closest price data point to target_time_ms
561+
# data time is start time, add timespan to match close price time
562+
closest_data = min(price_data,
563+
key=lambda x: abs(TimeUtil.parse_iso_to_ms(x['date']) + timespan_ms - target_time_ms))
564+
565+
data_time_ms = TimeUtil.parse_iso_to_ms(closest_data["date"]) + timespan_ms
566+
price_close = float(closest_data['close'])
567+
bid_price = ask_price = 0 # Bid/ask not provided in historical data
568+
569+
tp = TradePair.get_latest_trade_pair_from_trade_pair_id(ticker.upper())
570+
source_name = f'{TIINGO_PROVIDER_NAME}_{TIINGO_COINBASE_EXCHANGE_STR}'
571+
exchange = TIINGO_COINBASE_EXCHANGE_STR
572+
573+
# Create PriceSource
574+
tp_to_price[tp] = PriceSource(
575+
source=source_name,
576+
timespan_ms=timespan_ms,
577+
open=float(closest_data['open']),
578+
close=float(closest_data['close']),
579+
vwap=price_close,
580+
high=price_close,
581+
low=float(closest_data['low']),
582+
start_ms=data_time_ms,
583+
websocket=False,
584+
lag_ms=target_time_ms - data_time_ms,
585+
bid=bid_price,
586+
ask=ask_price,
587+
)
603588

604-
book_data = crypto_data['topOfBookData'][0]
605-
606-
# Determine the data source and timestamp
607-
data_time_ms, price, exchange, bid_price, ask_price = self.get_best_crypto_price_info(
608-
book_data, now_ms, TIINGO_COINBASE_EXCHANGE_STR
609-
)
610-
611-
# Create trade pair
612-
tp = TradePair.get_latest_trade_pair_from_trade_pair_id(ticker.upper())
613-
price = float(price)
614-
source_name = f'{TIINGO_PROVIDER_NAME}_{exchange}_rest'
615-
616-
# Create PriceSource
617-
tp_to_price[tp] = PriceSource(
618-
source=source_name,
619-
timespan_ms=self.timespan_to_ms['minute'],
620-
open=price,
621-
close=price,
622-
vwap=price,
623-
high=price,
624-
low=price,
625-
start_ms=data_time_ms,
626-
websocket=False,
627-
lag_ms=now_ms - data_time_ms,
628-
bid=bid_price,
629-
ask=ask_price
630-
)
631-
632-
if verbose:
633-
self.log_price_info(tp, tp_to_price[tp], now_ms, data_time_ms,
634-
book_data['quoteTimestamp'], price, exchange, book_data)
589+
if verbose:
590+
self.log_price_info(tp, tp_to_price[tp], target_time_ms, data_time_ms,
591+
closest_data["date"], price_close, exchange, closest_data)
635592

636593
return tp_to_price
637594

595+
# Previously used for deprecated tiingo top-of-book rest endpoint - not used anymore (06/24/2025)
638596
def get_best_crypto_price_info(self, book_data, now_ms, preferred_exchange):
639597
"""Helper function to determine the best price info from book data"""
640598
data_time_exchange_ms = TimeUtil.parse_iso_to_ms(book_data['lastSaleTimestamp'])

vali_objects/utils/live_price_fetcher.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,17 +91,17 @@ def get_latest_price(self, trade_pair: TradePair, time_ms=None) -> Tuple[float,
9191
winning_event = PriceSource.get_winning_event(price_sources, time_ms)
9292
return winning_event.parse_best_best_price_legacy(time_ms), price_sources
9393

94-
def get_sorted_price_sources_for_trade_pair(self, trade_pair: TradePair, time_ms:int=None, live=True) -> List[PriceSource] | None:
94+
def get_sorted_price_sources_for_trade_pair(self, trade_pair: TradePair, time_ms: int, live=True) -> List[PriceSource] | None:
9595
temp = self.get_tp_to_sorted_price_sources([trade_pair], time_ms, live)
9696
return temp.get(trade_pair)
9797

9898
@timeme
99-
def get_tp_to_sorted_price_sources(self, trade_pairs: List[TradePair], time_ms = None, live=True) -> Dict[TradePair, List[PriceSource]]:
99+
def get_tp_to_sorted_price_sources(self, trade_pairs: List[TradePair], time_ms: int, live=True) -> Dict[TradePair, List[PriceSource]]:
100100
"""
101101
Retrieves the latest prices for multiple trade pairs, leveraging both WebSocket and REST APIs as needed.
102102
"""
103-
# if not time_ms:
104-
# time_ms = TimeUtil.now_in_millis()
103+
if not time_ms:
104+
time_ms = TimeUtil.now_in_millis()
105105

106106
websocket_prices_polygon = self.polygon_data_service.get_closes_websocket(trade_pairs, time_ms)
107107
websocket_prices_tiingo_data = self.tiingo_data_service.get_closes_websocket(trade_pairs, time_ms)
@@ -287,7 +287,6 @@ def get_close_at_date(self, trade_pair, timestamp_ms, order=None):
287287
bt.logging.warning(
288288
f"Fell back to Tiingo get_date for price of {trade_pair.trade_pair} at {TimeUtil.timestamp_ms_to_eastern_time_str(timestamp_ms)}, ms: {timestamp_ms}")
289289

290-
291290
"""
292291
if price is None:
293292
price, time_delta = self.polygon_data_service.get_close_in_past_hour_fallback(trade_pair=trade_pair,

0 commit comments

Comments
 (0)