From 71379aba8e79a5acde1977383f6084f5b9761702 Mon Sep 17 00:00:00 2001 From: moshferatu Date: Fri, 15 Sep 2023 13:09:39 -0700 Subject: [PATCH] Initial commit of IQFeed API client --- iqfeed/__init__.py | 0 iqfeed/iqfeed.py | 102 +++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + setup.py | 10 +++++ 4 files changed, 113 insertions(+) create mode 100644 iqfeed/__init__.py create mode 100644 iqfeed/iqfeed.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/iqfeed/__init__.py b/iqfeed/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/iqfeed/iqfeed.py b/iqfeed/iqfeed.py new file mode 100644 index 0000000..1a93384 --- /dev/null +++ b/iqfeed/iqfeed.py @@ -0,0 +1,102 @@ +import pandas as pd +import socket + +from datetime import datetime +from io import StringIO + +def read_data_from_socket(sock, recv_buffer=4096): + buffer = b'' + data = b'' + while True: + data = sock.recv(recv_buffer) + buffer += data + + if b'!ENDMSG!' in buffer: + break + + # Remove the ENDMSG string + buffer = buffer[:-12] + return buffer.decode('utf-8') + +def _get_daily_data(symbol: str, max_days: int) -> pd.DataFrame: + host = '127.0.0.1' + port = 9100 # Historical data socket port + + message = 'HDX,{symbol},{max_days},1\n'.format( + symbol=symbol, + max_days=max_days + ) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((host, port)) + + sock.sendall(message.encode('utf-8')) + data = read_data_from_socket(sock) + sock.close() + + # Remove extraneous line endings + data = ''.join(data.split('\r')) + data = data.replace(',\n','\n')[:-1] + + data = StringIO(data) + return pd.read_csv(data, index_col=False, + names=['Date', 'High', 'Low', 'Open', 'Close', 'Total Volume', 'Period Volume']) + +def get_daily_data(symbol: str, start_date: datetime, end_date: datetime) -> pd.DataFrame: + max_days = (datetime.today() - start_date).days + data = _get_daily_data(symbol, max_days) + + # Filter data between start_date and end_date + data['Date'] = pd.to_datetime(data['Date']) + data['Date'] = data['Date'].dt.date + data = data[(data['Date'] >= start_date.date()) & (data['Date'] <= end_date.date())] + data = data.reset_index(drop=True) + return data + +def seconds(value): + return value + +def minutes(value): + return value * 60 + +def hours(value): + return value * 3600 + +def get_historical_data(symbol: str, interval: int, + start_date: datetime, end_date: datetime = None, + start_filter_time: datetime = datetime.strptime('093000', '%H%M%S').time(), + end_filter_time: datetime = datetime.strptime('160000', '%H%M%S').time()) -> pd.DataFrame: + host = '127.0.0.1' + port = 9100 # Historical data socket port + + message = 'HIT,{symbol},{interval},{begin},{end},,{begin_filter},{end_filter},1\n'.format( + symbol=symbol, + interval=interval, + begin=start_date.strftime('%Y%m%d %H%M%S'), + end=end_date.strftime('%Y%m%d %H%M%S') if end_date else '', + begin_filter=start_filter_time.strftime('%H%M%S') if start_filter_time else '', + end_filter=end_filter_time.strftime('%H%M%S') if end_filter_time else '' + ) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((host, port)) + + sock.sendall(message.encode('utf-8')) + data = read_data_from_socket(sock) + sock.close() + + # Remove extraneous line endings + data = ''.join(data.split('\r')) + data = data.replace(',\n','\n')[:-1] + + data = StringIO(data) + return pd.read_csv(data, index_col=False, + names=['Date', 'High', 'Low', 'Open', 'Close', 'Total Volume', 'Period Volume']) + +if __name__ == '__main__': + symbols = ['SPY'] + start_date = datetime(2023, 1, 1, 7, 50) + end_date = datetime(2023, 1, 31, 16, 0) + for symbol in symbols: + data = get_historical_data(symbol, minutes(5), start_date, end_date) + data.to_csv(symbol + '.csv') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1411a4a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pandas \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0702bb9 --- /dev/null +++ b/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup, find_packages + +setup( + name="iqfeed", + version="1.0", + packages=find_packages(), + install_requires=[ + 'pandas' + ] +) \ No newline at end of file