@@ -63,6 +63,16 @@ class Client(object):
6363 ORDER_RESP_TYPE_RESULT = 'RESULT'
6464 ORDER_RESP_TYPE_FULL = 'FULL'
6565
66+ # For accessing the data returned by Client.aggregate_trades().
67+ AGG_ID = 'a'
68+ AGG_PRICE = 'p'
69+ AGG_QUANTITY = 'q'
70+ AGG_FIRST_TRADE_ID = 'f'
71+ AGG_LAST_TRADE_ID = 'l'
72+ AGG_TIME = 'T'
73+ AGG_BUYER_MAKES = 'm'
74+ AGG_BEST_MATCH = 'M'
75+
6676 def __init__ (self , api_key , api_secret , requests_params = None ):
6777 """Binance API Client constructor
6878
@@ -565,6 +575,77 @@ def get_aggregate_trades(self, **params):
565575 """
566576 return self ._get ('aggTrades' , data = params )
567577
578+ def aggregate_trade_iter (self , symbol , start_str = None , last_id = None ):
579+ """Iterate over aggregate trade data from (start_time or last_id) to
580+ the end of the history so far.
581+
582+ If start_time is specified, start with the first trade after
583+ start_time. Meant to initialise a local cache of trade data.
584+
585+ If last_id is specified, start with the trade after it. This is meant
586+ for updating a pre-existing local trade data cache.
587+
588+ Only allows start_str or last_id—not both. Not guaranteed to work
589+ right if you're running more than one of these simultaneously. You
590+ will probably hit your rate limit.
591+
592+ See dateparser docs for valid start and end string formats http://dateparser.readthedocs.io/en/latest/
593+
594+ If using offset strings for dates add "UTC" to date string e.g. "now UTC", "11 hours ago UTC"
595+
596+ :param symbol: Symbol string e.g. ETHBTC
597+ :type symbol: str
598+ :param start_str: Start date string in UTC format. The iterator will
599+ return the first trade occurring later than this time.
600+ :type start_str: str
601+ :param last_id: aggregate trade ID of the last known aggregate trade.
602+ Not a regular trade ID. See https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md#compressedaggregate-trades-list.
603+
604+ :returns: an iterator of JSON objects, one per trade. The format of
605+ each object is identical to Client.aggregate_trades().
606+
607+ :type last_id: int
608+ """
609+ if start_str is not None and last_id is not None :
610+ raise ValueError (
611+ 'start_time and last_id may not be simultaneously specified.' )
612+
613+ # If there's no last_id, get one.
614+ if last_id is None :
615+ # Without a last_id, we actually need the first trade. Normally,
616+ # we'd get rid of it. See the next loop.
617+ if start_str is None :
618+ trades = self .get_aggregate_trades (symbol = symbol , fromId = 0 )
619+ else :
620+ # It doesn't matter what the end time is, as long as it's less
621+ # than a day and the result set contains at least one trade.
622+ # A half a day should be fine.
623+ start_ts = date_to_milliseconds (start_str )
624+ trades = self .get_aggregate_trades (
625+ symbol = symbol ,
626+ startTime = start_ts ,
627+ endTime = start_ts + (1000 * 3600 ))
628+ for t in trades :
629+ yield t
630+ last_id = trades [- 1 ][self .AGG_ID ]
631+
632+ while True :
633+ # There is no need to wait between queries, to avoid hitting the
634+ # rate limit. We're using blocking IO, and as long as we're the
635+ # only thread running calls like this, Binance will automatically
636+ # add the right delay time on their end, forcing us to wait for
637+ # data. That really simplifies this function's job. Binance is
638+ # fucking awesome.
639+ trades = self .get_aggregate_trades (symbol = symbol , fromId = last_id )
640+ # fromId=n returns a set starting with id n, but we already have
641+ # that one. So get rid of the first item in the result set.
642+ trades = trades [1 :]
643+ if len (trades ) == 0 :
644+ return
645+ for t in trades :
646+ yield t
647+ last_id = trades [- 1 ][self .AGG_ID ]
648+
568649 def get_klines (self , ** params ):
569650 """Kline/candlestick bars for a symbol. Klines are uniquely identified by their open time.
570651
0 commit comments