import struct from datetime import timedelta, datetime from lzma import LZMADecompressor, LZMAError, FORMAT_AUTO from .utils import is_dst def decompress_lzma(data): results = [] len(data) while True: decomp = LZMADecompressor(FORMAT_AUTO, None, None) try: res = decomp.decompress(data) except LZMAError: if results: break # Leftover data is not a valid LZMA/XZ stream; ignore it. else: raise # Error on the first iteration; bail out. results.append(res) data = decomp.unused_data if not data: break if not decomp.eof: raise LZMAError("Compressed data ended before the end-of-stream marker was reached") return b"".join(results) def tokenize(buffer): token_size = 20 size = int(len(buffer) / token_size) tokens = [] for i in range(0, size): tokens.append(struct.unpack('!IIIff', buffer[i * token_size: (i + 1) * token_size])) return tokens def add_hour(ticks): if len(ticks) is 0: return ticks hour_delta = 0 if ticks[0][0].weekday() == 6 or (ticks[0][0].day == 1 and ticks[0][0].month == 1): if is_dst(ticks[0][0].date()): hour_delta = 21 else: hour_delta = 22 for index, v in enumerate(ticks): if index != 0: if ticks[index - 1][0].minute > ticks[index][0].minute: hour_delta = ticks[index - 1][0].hour + 1 else: hour_delta = ticks[index - 1][0].hour ticks[index] = (v[0] + timedelta(hours=hour_delta), v[1], v[2], v[3], v[4]) return ticks def normalize(symbol, day, ticks): def norm(time, ask, bid, volume_ask, volume_bid): date = datetime(day.year, day.month, day.day) + timedelta(milliseconds=time) # date.replace(tzinfo=datetime.tzinfo("UTC")) point = 100000 if symbol.lower() in ['usdrub', 'xagusd', 'xauusd']: point = 1000 return date, ask / point, bid / point, round(volume_ask * 1000000), round(volume_bid * 1000000) return add_hour(list(map(lambda x: norm(*x), ticks))) def decompress(symbol, day, compressed_buffer): if compressed_buffer.nbytes == 0: return compressed_buffer return normalize(symbol, day, tokenize(decompress_lzma(compressed_buffer)))