From dbdd8498424794b5c037812ae8b4d7d21d512258 Mon Sep 17 00:00:00 2001 From: Denis Tereshkin Date: Tue, 25 May 2021 06:35:34 +0700 Subject: [PATCH 1/7] Time zone handling --- hap_csv_upload.py | 51 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/hap_csv_upload.py b/hap_csv_upload.py index 01a8201..88f4f0d 100644 --- a/hap_csv_upload.py +++ b/hap_csv_upload.py @@ -8,7 +8,9 @@ import json import csv import datetime import struct +import re +from pytz import timezone def sec_from_period(period): if period == "M1": @@ -24,21 +26,35 @@ def sec_from_period(period): elif period == "D": return 86400 + +def get_month_code(month): + if month < 1 or month > 12: + return None + codes = ['F', 'G', 'H', 'J', 'K', 'M', 'N', 'Q', 'U', 'V', 'X', 'Z'] + return codes[month - 1] + def main(): parser = argparse.ArgumentParser(description='Finam quote downloader') parser.add_argument('-i', '--input-file', action='store', dest='input_file', help='Input filename', required=True) parser.add_argument('-p', '--timeframe', action='store', dest='timeframe', help='Data timeframe', required=True) parser.add_argument('-o', '--hap', action='store', dest='hap', help='HAP endpoint', required=True) parser.add_argument('-y', '--hap-symbol', action='store', dest='hap_symbol', help='HAP symbol', required=True) - parser.add_argument('-d', '--time-delta', action='store', dest='time_delta', help='Time delta (hours)') + parser.add_argument('-d', '--time-delta', action='store', dest='time_delta', help='Time delta (seconds)') parser.add_argument('-f', '--force-from', action='store', dest='force_from', help='Force period start') parser.add_argument('-t', '--force-to', action='store', dest='force_to', help='Force period end') + parser.add_argument('-z', '--timezone', action='store', dest='timezone', help='Timestamps timezone') args = parser.parse_args() period = args.timeframe + utc_tz = timezone('UTC') + if args.timezone is None: + tz = utc_tz + else: + tz = timezone(args.timezone) + out_symbol = args.hap_symbol ctx = zmq.Context.instance() @@ -52,11 +68,18 @@ def main(): time_delta = datetime.timedelta(seconds=int(args.time_delta)) print('Applying delta:', time_delta) line_count = 0 + ticker = None with open(args.input_file, 'r') as f: reader = csv.reader(f, delimiter=',') next(reader) for line in reader: line_count += 1 + if ticker is None: + ticker = line[0] + elif ticker != line[0]: + print('Different tickers in file, aborting') + break + date = line[2] time = line[3] open_ = line[4] @@ -72,8 +95,9 @@ def main(): minute = int(time[2:4]) second = int(time[4:6]) - dt = datetime.datetime(year, month, day, hour, minute, second, 0, datetime.timezone.utc) - time_delta + dt = datetime.datetime(year, month, day, hour, minute, second, 0, tz) - time_delta + dt = dt.astimezone(utc_tz) serialized_bars.write(struct.pack(" Date: Wed, 26 May 2021 21:40:20 +0700 Subject: [PATCH 2/7] New tool for stitching futures contracts --- stitch_futures.py | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 stitch_futures.py diff --git a/stitch_futures.py b/stitch_futures.py new file mode 100644 index 0000000..f8510c1 --- /dev/null +++ b/stitch_futures.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +import sys +import argparse +import csv +import os +import datetime + +def parse_date(x): + return datetime.datetime.strptime(x, '%Y%m%d').date() + +def read_file(f): + reader = csv.reader(f, delimiter=',') + ticker = None + next(reader) + result = { 'bars' : [] } + + for line in reader: + result['bars'].append(line) + if ticker is None: + ticker = line[0] + result['ticker'] = ticker + + return result + +def write_to_file(writer, bars, ticker): + for bar in bars: + if ticker is not None: + bar[0] = ticker + writer.writerow(bar) + + +def main(): + parser = argparse.ArgumentParser(description='Stitch futures') + parser.add_argument('-i', '--input-directory', action='store', dest='input_directory', help='Input directory', required=True) + parser.add_argument('-o', '--output-file', action='store', dest='output_file', help='Output filename', required=True) + parser.add_argument('-d', '--stitch-delta', action='store', dest='stitch_delta', help='Offset at which stitching occurs (days)', required=False) + parser.add_argument('-t', '--ticker', action='store', dest='replace_ticker', help='Replace ticker') + + args = parser.parse_args() + + input_directory = args.input_directory + output_file = args.output_file + delta = int(args.stitch_delta) + + ticker = args.replace_ticker + + data = [] + for filename in os.listdir(input_directory): + full_name = os.path.join(input_directory, filename) + print("Reading {}".format(full_name)) + with open(full_name, 'r') as f: + data.append(read_file(f)) + + + for f in data: + print("Cutting off trailing data: {}".format(f['ticker'])) + end_date = parse_date(f['bars'][-1][2]) + cutoff_date = datetime.date.fromordinal(end_date.toordinal() - delta) + cutoff_date_num = cutoff_date.year * 10000 + cutoff_date.month * 100 + cutoff_date.day + + f['bars'] = [s for s in f['bars'] if int(s[2]) <= cutoff_date_num] + f['end_date'] = cutoff_date + + data.sort(key=lambda x: x['end_date']) + + for i in range(1, len(data)): + print("Cutting off starting data: {}".format(data[i]['ticker'])) + start_date = parse_date(data[i - 1]['bars'][-1][2]) + start_date_num = start_date.year * 10000 + start_date.month * 100 + start_date.day + data[i]['bars'] = [s for s in data[i]['bars'] if int(s[2]) > start_date_num] + + with open(args.output_file, 'w+') as f: + writer = csv.writer(f) + writer.writerow(['', '', '', '