-1

I found this python script on the web, it gets OHLCV historical data from Binance api by wanted dates, assets and time intervals. The script currently returns the data for UTC time.

I want to modify it so it will return the data (daily/hourly) according to a specified timezone. I guess it takes only to change one function or add an argument but I can't manage to do it correctly.

How can I change it so it will return data for UTC+2 (or any other time zone)?

import time
import dateparser
import pytz
import os
from datetime import datetime
import binance
print(binance.__file__)
from binance.client import Client
import time
import pandas as pd


def date_to_milliseconds(date_str):
    """Convert UTC date to milliseconds.

    If using offset strings add "UTC" to date string e.g. "now UTC", "11 hours ago UTC"

    See dateparse docs for formats http://dateparser.readthedocs.io/en/latest/

    :param date_str: date in readable format, i.e. "January 01, 2018", "11 hours ago UTC", "now UTC"
    :type date_str: str
    """
    # get epoch value in UTC
    epoch = datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
    # parse our date string
    d = dateparser.parse(date_str)
    # if the date is not timezone aware apply UTC timezone
    if d.tzinfo is None or d.tzinfo.utcoffset(d) is None:
        d = d.replace(tzinfo=pytz.utc)

    # return the difference in time
    return int((d - epoch).total_seconds() * 1000.0)


def interval_to_milliseconds(interval):
    """Convert a Binance interval string to milliseconds

    :param interval: Binance interval string 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w
    :type interval: str

    :return:
         None if unit not one of m, h, d or w
         None if string not in correct format
         int value of interval in milliseconds
    """
    ms = None
    seconds_per_unit = {
        "m": 60,
        "h": 60 * 60,
        "d": 24 * 60 * 60,
        "w": 7 * 24 * 60 * 60
    }

    unit = interval[-1]
    if unit in seconds_per_unit:
        try:
            ms = int(interval[:-1]) * seconds_per_unit[unit] * 1000
        except ValueError:
            pass
    return ms

def GetUpdateData(kline):
     Time = time.strftime("%a, %d %b %Y %H:%M:%S", time.gmtime(kline[0]/1000))
     Open = kline[1]
     High = kline[2]
     Low = kline[3]
     Close = kline[4]
     Volume = kline[5]
     Close_time = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime(kline[6]/1000))
     Quote_asset_volume = kline[7]
     Number_of_trades = kline[8]
     Taker_buy_base_asset_volume = kline[9]
     Taker_buy_quote_asset_volume = kline[10]
     return Time,Open,High,Low,Close,Volume,Close_time,Quote_asset_volume,Number_of_trades,Taker_buy_base_asset_volume,Taker_buy_quote_asset_volume
def get_historical_klines(symbol, interval, start_str, end_str=None):
    """Get Historical Klines from Binance

    See dateparse docs for valid start and end string formats http://dateparser.readthedocs.io/en/latest/

    If using offset strings for dates add "UTC" to date string e.g. "now UTC", "11 hours ago UTC"

    :param symbol: Name of symbol pair e.g BNBBTC
    :type symbol: str
    :param interval: Biannce Kline interval
    :type interval: str
    :param start_str: Start date string in UTC format
    :type start_str: str
    :param end_str: optional - end date string in UTC format
    :type end_str: str

    :return: list of OHLCV values

    """
    # create the Binance client, no need for api key
    client = Client("", "")

    # init our list
    output_data = []

    # setup the max limit
    limit = 500

    # convert interval to useful value in seconds
    timeframe = interval_to_milliseconds(interval)

    # convert our date strings to milliseconds
    start_ts = date_to_milliseconds(start_str)

    # if an end time was passed convert it
    end_ts = None
    if end_str:
        end_ts = date_to_milliseconds(end_str)

    idx = 0
    # it can be difficult to know when a symbol was listed on Binance so allow start time to be before list date
    symbol_existed = False
    while True:
        # fetch the klines from start_ts up to max 500 entries or the end_ts if set
        temp_data = client.get_klines(
            symbol=symbol,
            interval=interval,
            limit=limit,
            startTime=start_ts,
            endTime=end_ts
        )

        # handle the case where our start date is before the symbol pair listed on Binance
        if not symbol_existed and len(temp_data):
            symbol_existed = True

        if symbol_existed:
            # append this loops data to our output data
            output_data += temp_data

            # update our start timestamp using the last value in the array and add the interval timeframe
            start_ts = temp_data[len(temp_data) - 1][0] + timeframe
        else:
            # it wasn't listed yet, increment our start date
            start_ts += timeframe

        idx += 1
        # check if we received less than the required limit and exit the loop
        if len(temp_data) < limit:
            # exit the while loop
            break

        # sleep after every 3rd call to be kind to the API
        if idx % 3 == 0:
            time.sleep(1)

    return output_data

start = "01 January, 2017"
end = "01 February, 2017"
symbols = ['ETHBTC']
interval = '1d'#Client.KLINE_INTERVAL_15MIN
for symbol in symbols:
    klines = get_historical_klines(symbol, interval, start, end)

    times = []
    Opens = []
    Highs = []
    Lows  = []
    Closes = []
    Volumes = []
    Close_times = []
    Quote_asset_volumes = []
    Number_of_tradess = []
    Taker_buy_base_asset_volumes = []
    Taker_buy_quote_asset_volumes = []
    for k in klines:
        Time,Open,High,Low,Close,Volume,Close_time,Quote_asset_volume,Number_of_trades,Taker_buy_base_asset_volume,Taker_buy_quote_asset_volume = GetUpdateData(k)
        times.append(Time)
        Opens.append(Open)
        Highs.append(High)
        Lows.append(Low)
        Closes.append(Close)
        Volumes.append(Volume)
        Close_times.append(Close_time)
        Quote_asset_volumes.append(Quote_asset_volume)
        Number_of_tradess.append(Number_of_trades)
        Taker_buy_base_asset_volumes.append(Taker_buy_base_asset_volume)
        Taker_buy_quote_asset_volumes.append(Taker_buy_quote_asset_volume)


    DataStruct = pd.DataFrame()
    DataStruct['time'] = times
    DataStruct['Open'] = Opens
    DataStruct['High'] = Highs
    DataStruct['Low'] = Lows
    DataStruct['Close'] = Closes
    DataStruct['Volume'] = Volumes
    DataStruct['Close_time'] = Close_times
    DataStruct['Quote_asset_volume'] = Quote_asset_volumes
    DataStruct['Number_of_trades'] = Number_of_tradess
    DataStruct['Taker_buy_base_asset_volume'] = Taker_buy_base_asset_volumes
    DataStruct['Taker_buy_quote_asset_volume'] = Taker_buy_quote_asset_volumes

    FileName = symbol+ '_' + start+ '_' + end + ' .csv'
    FileName = FileName.replace(' ','_')
    FileName = FileName.replace(',','')
    Path2Save = os.path.normpath(r'')
    SaveStrFile = os.path.normpath(Path2Save+ '\\' +FileName)
    #save FeatureWeights to CSV file
    D_S_header = ['time','Open','High','Low','Close','Volume','Close_time','Quote_asset_volume','Number_of_trades','Taker_buy_base_asset_volume','Taker_buy_quote_asset_volume']
    DataStruct.to_csv(path_or_buf = SaveStrFile, header = D_S_header )
alper
  • 2,919
  • 9
  • 53
  • 102
Guy
  • 15
  • 1
  • 2

1 Answers1

0

In these lines you see the timezone being defined:

# get epoch value in UTC
epoch = datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)

Just redefine the timezone there. For a list of timezones supported by pytz you can get a list using pytz.all_timezones.

gosuto
  • 5,422
  • 6
  • 36
  • 57
  • Guess the question is about how to fetching data with appropriate timezone from binance, not converting the timezone of data using codes. Read the title of this question once again and you will notice. – Eiffelbear May 10 '20 at 06:19
  • 1
    Can you explain the difference between retrieving a date and time that is timezone aware or retrieving a timestamp and converting it to a specific timezone? The reason I react like that is because I don't believe there is a difference between the two and that it is really up to the author to explain his question. – gosuto May 10 '20 at 10:10
  • I can see your point. But actually there is a difference in computational cost-wise. For example, let's say you are living in the UK and ask Binance give 10 year long daily price data. Then Binance gives you that data following the UK time. Then you need to convert it on your local computer or server, which takes some time, if what you really wanted was the US timezone daily price data. If it needs to be done real time, you might not want to waste some micro seconds to reconvert the data's timeframe. It might be critical in trading real time. – Eiffelbear May 10 '20 at 10:33
  • 1
    Again; please don't speak for the author of the question. The way I read it his question is about *historical* data, which does not bear the computational dependency that you describe. Also I believe that in a real-time scenario one would work with a stream of data which would be timezone independent by definition: it is happening live... – gosuto May 11 '20 at 15:31
  • I think Binance does not have a parameter to set the time zone for the requested data. It always comes in UTC. The OP asked about a simple change to his script which would give him the data in his timezone. And since there's already a parameter for UTC set in the original script I don't see any overhead with this solution which just changes this parameter. This answer is perfectly reasonable, and I don't think there is any better way. Lots of useless assumptions by @Eiffelbear – trollkotze Dec 07 '21 at 11:52