siglab-py 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of siglab-py might be problematic. Click here for more details.

@@ -0,0 +1,140 @@
1
+ from enum import Enum
2
+ import argparse
3
+ import time
4
+ from datetime import datetime
5
+ from typing import Any, Dict, List, Union
6
+ import logging
7
+ import json
8
+ import pandas as pd
9
+ import numpy as np
10
+ from redis import StrictRedis
11
+
12
+ from ordergateway.client import DivisiblePosition
13
+
14
+ '''
15
+ set PYTHONPATH=%PYTHONPATH%;D:\dev\siglab\siglab_py
16
+ python test_ordergateway.py --gateway_id bybit_01
17
+ '''
18
+
19
+ param : Dict[str, str] = {
20
+ 'gateway_id' : '---'
21
+ }
22
+
23
+ def parse_args():
24
+ parser = argparse.ArgumentParser() # type: ignore
25
+ parser.add_argument("--gateway_id", help="gateway_id: Where are you sending your order?", default=None)
26
+
27
+ args = parser.parse_args()
28
+ param['gateway_id'] = args.gateway_id
29
+
30
+ def init_redis_client() -> StrictRedis:
31
+ redis_client : StrictRedis = StrictRedis(
32
+ host = 'localhost',
33
+ port = 6379,
34
+ db = 0,
35
+ ssl = False
36
+ )
37
+ try:
38
+ redis_client.keys()
39
+ except ConnectionError as redis_conn_error:
40
+ err_msg = f"Failed to connect to redis: {'localhost'}, port: {6379}"
41
+ raise ConnectionError(err_msg)
42
+
43
+ return redis_client
44
+
45
+ def execute_positions(
46
+ redis_client : StrictRedis,
47
+ positions : List[DivisiblePosition],
48
+ ordergateway_pending_orders_topic : str):
49
+ # https://redis.io/commands/publish/
50
+ _positions = [ position.to_dict() for position in positions ]
51
+ redis_client.set(name=ordergateway_pending_orders_topic, value=json.dumps(_positions).encode('utf-8'), ex=60*15)
52
+
53
+ print(f"{ordergateway_pending_orders_topic}: Orders sent {_positions}.")
54
+
55
+ if __name__ == '__main__':
56
+ parse_args()
57
+
58
+ gateway_id : str = param['gateway_id']
59
+ ordergateway_pending_orders_topic = 'ordergateway_pending_orders_$GATEWAY_ID$'
60
+ ordergateway_pending_orders_topic = ordergateway_pending_orders_topic.replace("$GATEWAY_ID$", gateway_id)
61
+
62
+ ordergateway_executions_topic = "ordergateway_executions_$GATEWAY_ID$"
63
+ ordergateway_executions_topic = ordergateway_executions_topic.replace("$GATEWAY_ID$", gateway_id)
64
+
65
+ redis_client : StrictRedis = init_redis_client()
66
+
67
+ # Example, enter into a pair position long SUSHI, short DYDX
68
+ positions_1 : List[DivisiblePosition] = [
69
+ DivisiblePosition(
70
+ ticker = 'SUSHI/USDT:USDT',
71
+ side = 'sell',
72
+ amount = 10,
73
+ leg_room_bps = 5,
74
+ order_type = 'limit',
75
+ slices=5,
76
+ wait_fill_threshold_ms=15000
77
+ ),
78
+ DivisiblePosition(
79
+ ticker = 'DYDX/USDT:USDT',
80
+ side = 'buy',
81
+ amount = 10,
82
+ leg_room_bps = 5,
83
+ order_type = 'limit',
84
+ slices=5,
85
+ wait_fill_threshold_ms=15000
86
+ )
87
+ ]
88
+
89
+ positions_2 : List[DivisiblePosition] = [
90
+ DivisiblePosition(
91
+ ticker = 'SUSHI/USDT:USDT',
92
+ side = 'buy',
93
+ amount = 10,
94
+ leg_room_bps = 5,
95
+ order_type = 'limit',
96
+ slices=5,
97
+ wait_fill_threshold_ms=15000
98
+ ),
99
+ ]
100
+
101
+ positions_3 : List[DivisiblePosition] = [
102
+ DivisiblePosition(
103
+ ticker = 'BTC/USDT:USDT',
104
+ side = 'sell',
105
+ amount = 0.01,
106
+ leg_room_bps = 5,
107
+ order_type = 'limit',
108
+ slices=1,
109
+ wait_fill_threshold_ms=60000
110
+ )
111
+ ]
112
+ execute_positions(
113
+ redis_client=redis_client,
114
+ positions=positions_2,
115
+ ordergateway_pending_orders_topic=ordergateway_pending_orders_topic)
116
+
117
+ # Wait for fills
118
+ fills_received : bool = False
119
+ while not fills_received:
120
+ try:
121
+ keys = redis_client.keys()
122
+ for key in keys:
123
+ s_key : str = key.decode("utf-8")
124
+ if s_key==ordergateway_executions_topic:
125
+ message = redis_client.get(key)
126
+ if message:
127
+ message = message.decode('utf-8')
128
+ positions = json.loads(message)
129
+
130
+ for position in positions:
131
+ print(f"{position['ticker']} {position['side']} amount: {position['amount']} leg_room_bps: {position['leg_room_bps']} slices: {position['slices']}, filled_amount: {position['filled_amount']}, average_cost: {position['average_cost']}, # executions: {len(position['executions'])}")
132
+ fills_received = True
133
+ break
134
+
135
+ except Exception as loop_err:
136
+ print(f"Oops {loop_err}")
137
+ finally:
138
+ time.sleep(1)
139
+
140
+
File without changes
File without changes
@@ -0,0 +1,123 @@
1
+ import unittest
2
+ from datetime import datetime
3
+ from typing import Union
4
+ from pathlib import Path
5
+
6
+ from util.market_data_util import *
7
+
8
+ from ccxt.binance import binance
9
+ from ccxt.bybit import bybit
10
+ from ccxt.okx import okx
11
+ from ccxt.deribit import deribit
12
+ from ccxt.base.exchange import Exchange
13
+
14
+ # @unittest.skip("Skip all integration tests.")
15
+ class MarketDataGizmoTests(unittest.TestCase):
16
+
17
+ def test_fetch_candles_yahoo(self):
18
+ start_date : datetime = datetime(2024, 1,1)
19
+ end_date : datetime = datetime(2024,12,31)
20
+
21
+ exchange = YahooExchange()
22
+ normalized_symbols = [ 'AAPL' ]
23
+ pd_candles: Union[pd.DataFrame, None] = fetch_candles(
24
+ start_ts=start_date.timestamp(),
25
+ end_ts=end_date.timestamp(),
26
+ exchange=exchange,
27
+ normalized_symbols=normalized_symbols,
28
+ candle_size='1h'
29
+ )[normalized_symbols[0]]
30
+
31
+ assert pd_candles is not None
32
+
33
+ if pd_candles is not None:
34
+ assert len(pd_candles) > 0, "No candles returned."
35
+ expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
36
+ assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
37
+ assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
38
+ assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
39
+
40
+ def test_fetch_candles_nasdaq(self):
41
+ start_date : datetime = datetime(2023,1,1)
42
+ end_date : datetime = datetime(2023,12,31)
43
+
44
+ '''
45
+ Folder structure:
46
+ \ siglab
47
+ \ siglab_py <-- python project root
48
+ \ sigab_py
49
+ __init__.py
50
+ \ util
51
+ __init__.py
52
+ market_data_util.py
53
+ \ tests
54
+ \ integration
55
+ __init__.py
56
+ market_data_util_tests.py <-- Tests here
57
+
58
+ \ siglab_rs <-- Rust project root
59
+ \ data <-- Data files here!
60
+ '''
61
+ data_dir : Union[str, None] = str(Path(__file__).resolve().parents[3] / "data/nasdaq")
62
+ exchange : NASDAQExchange = NASDAQExchange(data_dir = data_dir)
63
+
64
+ # CSV from NASDAQ: https://www.nasdaq.com/market-activity/quotes/historical
65
+ normalized_symbols = [ 'AAPL' ]
66
+ # normalized_symbols = [ 'BABA', 'SPY', 'VOO', 'IVV', 'AAPL', 'AMZN', 'GOOG_classC', 'META', 'MSFT', 'MSTR', 'TSLA' ]
67
+
68
+ pd_candles: Union[pd.DataFrame, None] = fetch_candles(
69
+ start_ts=start_date.timestamp(),
70
+ end_ts=end_date.timestamp(),
71
+ exchange=exchange,
72
+ normalized_symbols=normalized_symbols,
73
+ candle_size='1h'
74
+ )[normalized_symbols[0]]
75
+
76
+ assert pd_candles is not None
77
+
78
+ if pd_candles is not None:
79
+ assert len(pd_candles) > 0, "No candles returned."
80
+ expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
81
+ assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
82
+ assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
83
+ assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
84
+
85
+ def test_fetch_candles_ccxt(self):
86
+ start_date : datetime = datetime(2024,1,1)
87
+ end_date : datetime = datetime(2024,12,31)
88
+
89
+ param = {
90
+ 'apiKey' : None,
91
+ 'secret' : None,
92
+ 'password' : None, # Other exchanges dont require this! This is saved in exchange.password!
93
+ 'subaccount' : None,
94
+ 'rateLimit' : 100, # In ms
95
+ 'options' : {
96
+ 'defaultType': 'swap', # Should test linear instead
97
+ 'leg_room_bps' : 5,
98
+ 'trade_fee_bps' : 3,
99
+
100
+ 'list_ts_field' : 'listTime' # list_ts_field: Response field in exchange.markets[symbol] to indiate timestamp of symbol's listing date in ms. For bybit, markets['launchTime'] is list date. For okx, it's markets['listTime'].
101
+ }
102
+ }
103
+
104
+ exchange : Exchange = okx(param)
105
+ normalized_symbols = [ 'BTC/USDT:USDT' ]
106
+ pd_candles: Union[pd.DataFrame, None] = fetch_candles(
107
+ start_ts=start_date.timestamp(),
108
+ end_ts=end_date.timestamp(),
109
+ exchange=exchange,
110
+ normalized_symbols=normalized_symbols,
111
+ candle_size='1h'
112
+ )[normalized_symbols[0]]
113
+
114
+ assert pd_candles is not None
115
+
116
+ if pd_candles is not None:
117
+ assert len(pd_candles) > 0, "No candles returned."
118
+ expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
119
+ assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
120
+ assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
121
+ assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
122
+
123
+
File without changes
File without changes