@@ -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' ])
0 commit comments