siglab-py 0.1.11__tar.gz → 0.1.13__tar.gz
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.
- {siglab_py-0.1.11 → siglab_py-0.1.13}/PKG-INFO +1 -1
- {siglab_py-0.1.11 → siglab_py-0.1.13}/pyproject.toml +1 -1
- {siglab_py-0.1.11 → siglab_py-0.1.13}/setup.cfg +1 -1
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/orderbooks_provider.py +18 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/ordergateway/client.py +22 -3
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/ordergateway/gateway.py +29 -2
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/ordergateway/test_ordergateway.py +7 -4
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py.egg-info/PKG-INFO +1 -1
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/constants.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/exchanges/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/exchanges/any_exchange.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/candles_provider.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/test_provider.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/ordergateway/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/tests/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/tests/integration/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/tests/unit/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/tests/unit/analytic_util_tests.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/util/__init__.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/util/analytic_util.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/util/aws_util.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/util/market_data_util.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/util/retry_util.py +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py.egg-info/SOURCES.txt +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py.egg-info/dependency_links.txt +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py.egg-info/requires.txt +0 -0
- {siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "siglab_py"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.13"
|
|
8
8
|
description = "Market data fetches, TA calculations and generic order gateway."
|
|
9
9
|
authors = [{name = "r0bbarh00d", email = "r0bbarh00d@gmail.com"}]
|
|
10
10
|
license = {text = "MIT"}
|
{siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/orderbooks_provider.py
RENAMED
|
@@ -290,6 +290,9 @@ async def _handle_ticker(
|
|
|
290
290
|
task : ThreadTask
|
|
291
291
|
):
|
|
292
292
|
exchange = await instantiate_exhange(exchange_name=exchange_name, old_exchange=None)
|
|
293
|
+
|
|
294
|
+
asyncio.create_task(send_heartbeat(exchange))
|
|
295
|
+
|
|
293
296
|
ob = OrderBook(ticker=ticker, exchange_name=exchange_name)
|
|
294
297
|
candles_publish_topic = candles_publish_topic.replace("$SYMBOL$", ticker)
|
|
295
298
|
candles_publish_topic = candles_publish_topic.replace("$EXCHANGE$", exchange_name)
|
|
@@ -318,6 +321,21 @@ async def _handle_ticker(
|
|
|
318
321
|
exchange = await instantiate_exhange(exchange_name=exchange_name, old_exchange=exchange) # type: ignore Otherwise, Error: Argument of type "Coroutine[Any, Any, Exchange] | Exchange" cannot be assigned to parameter "old_exchange" of type "Exchange | None" in function "instantiate_exhange"
|
|
319
322
|
ob = OrderBook(ticker=ticker, exchange_name=exchange_name)
|
|
320
323
|
|
|
324
|
+
async def send_heartbeat(exchange):
|
|
325
|
+
|
|
326
|
+
await asyncio.sleep(10)
|
|
327
|
+
|
|
328
|
+
while True:
|
|
329
|
+
try:
|
|
330
|
+
first_ws_url = next(iter(exchange.clients))
|
|
331
|
+
client = exchange.clients[first_ws_url]
|
|
332
|
+
message = exchange.ping(client)
|
|
333
|
+
await client.send(message)
|
|
334
|
+
log('Heartbeat sent')
|
|
335
|
+
except Exception as hb_error:
|
|
336
|
+
log(f'Failed to send heartbeat: {hb_error}')
|
|
337
|
+
finally:
|
|
338
|
+
await asyncio.sleep(30)
|
|
321
339
|
|
|
322
340
|
async def main():
|
|
323
341
|
parse_args()
|
|
@@ -31,13 +31,17 @@ class Order:
|
|
|
31
31
|
side : str, # buy/sell
|
|
32
32
|
amount : float,
|
|
33
33
|
order_type : str, # market/limit
|
|
34
|
-
leg_room_bps : float = 0
|
|
34
|
+
leg_room_bps : float = 0,
|
|
35
|
+
reduce_only : bool = False,
|
|
36
|
+
fees_ccy : Union[str, None] = None
|
|
35
37
|
) -> None:
|
|
36
38
|
self.ticker = ticker
|
|
37
39
|
self.side = side.strip().lower()
|
|
38
40
|
self.amount = amount
|
|
39
41
|
self.order_type = order_type.strip().lower()
|
|
40
42
|
self.leg_room_bps = leg_room_bps
|
|
43
|
+
self.reduce_only = reduce_only
|
|
44
|
+
self.fees_ccy = fees_ccy
|
|
41
45
|
|
|
42
46
|
def to_dict(self) -> Dict[JSON_SERIALIZABLE_TYPES, JSON_SERIALIZABLE_TYPES]:
|
|
43
47
|
return {
|
|
@@ -45,7 +49,9 @@ class Order:
|
|
|
45
49
|
"side" : self.side,
|
|
46
50
|
"amount" : self.amount,
|
|
47
51
|
"order_type" : self.order_type,
|
|
48
|
-
"leg_room_bps" : self.leg_room_bps
|
|
52
|
+
"leg_room_bps" : self.leg_room_bps,
|
|
53
|
+
"reduce_only" : self.reduce_only,
|
|
54
|
+
"fees_ccy" : self.fees_ccy
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
'''
|
|
@@ -60,15 +66,18 @@ class DivisiblePosition(Order):
|
|
|
60
66
|
amount : float,
|
|
61
67
|
order_type : str, # market/limit
|
|
62
68
|
leg_room_bps : float,
|
|
69
|
+
reduce_only : bool = False,
|
|
70
|
+
fees_ccy : Union[str, None] = None,
|
|
63
71
|
slices : int = 1,
|
|
64
72
|
wait_fill_threshold_ms : float = -1
|
|
65
73
|
) -> None:
|
|
66
|
-
super().__init__(ticker, side, amount, order_type, leg_room_bps)
|
|
74
|
+
super().__init__(ticker, side, amount, order_type, leg_room_bps, reduce_only, fees_ccy)
|
|
67
75
|
self.slices = slices
|
|
68
76
|
self.wait_fill_threshold_ms = wait_fill_threshold_ms
|
|
69
77
|
self.multiplier = 1
|
|
70
78
|
self.filled_amount : Union[float, None] = None
|
|
71
79
|
self.average_cost : Union[float, None] = None
|
|
80
|
+
self.fees : Union[float, None] = None
|
|
72
81
|
self.pos : Union[float, None] = None # in base ccy, after execution. (Not in USDT or quote ccy, Not in # contracts)
|
|
73
82
|
|
|
74
83
|
self.executions : Dict[str, Dict[str, Any]] = {}
|
|
@@ -86,6 +95,8 @@ class DivisiblePosition(Order):
|
|
|
86
95
|
side=self.side,
|
|
87
96
|
amount=slice_amount_in_base_ccy,
|
|
88
97
|
leg_room_bps=self.leg_room_bps,
|
|
98
|
+
reduce_only=self.reduce_only,
|
|
99
|
+
fees_ccy=self.fees_ccy,
|
|
89
100
|
order_type=self.order_type)
|
|
90
101
|
|
|
91
102
|
else:
|
|
@@ -95,6 +106,8 @@ class DivisiblePosition(Order):
|
|
|
95
106
|
side=self.side,
|
|
96
107
|
amount=remaining_amount_in_base_ccy,
|
|
97
108
|
leg_room_bps=self.leg_room_bps,
|
|
109
|
+
reduce_only=self.reduce_only,
|
|
110
|
+
fees_ccy=self.fees_ccy,
|
|
98
111
|
order_type=self.order_type)
|
|
99
112
|
|
|
100
113
|
slices.append(slice)
|
|
@@ -171,6 +184,12 @@ class DivisiblePosition(Order):
|
|
|
171
184
|
average_cost = average_cost / sum([ self.executions[order_id]['amount'] for order_id in self.executions ])
|
|
172
185
|
return average_cost
|
|
173
186
|
|
|
187
|
+
def get_fees(self) -> float:
|
|
188
|
+
fees : float = 0
|
|
189
|
+
if self.fees_ccy:
|
|
190
|
+
fees = sum([ float(self.executions[order_id]['fee']['cost']) for order_id in self.executions if self.executions[order_id]['fee'] and self.executions[order_id]['fee']['currency'].strip().upper()==self.fees_ccy.strip().upper() ])
|
|
191
|
+
return fees
|
|
192
|
+
|
|
174
193
|
def to_dict(self) -> Dict[JSON_SERIALIZABLE_TYPES, JSON_SERIALIZABLE_TYPES]:
|
|
175
194
|
rv = super().to_dict()
|
|
176
195
|
rv['slices'] = self.slices
|
|
@@ -184,7 +184,8 @@ param : Dict = {
|
|
|
184
184
|
"incoming_orders_topic_regex" : r"ordergateway_pending_orders_$GATEWAY_ID$",
|
|
185
185
|
"executions_publish_topic" : r"ordergateway_executions_$GATEWAY_ID$",
|
|
186
186
|
|
|
187
|
-
"
|
|
187
|
+
"default_fees_ccy" : None,
|
|
188
|
+
"loop_freq_ms" : 500, # reduce this if you need trade faster
|
|
188
189
|
|
|
189
190
|
'mds' : {
|
|
190
191
|
'topics' : {
|
|
@@ -261,6 +262,9 @@ def parse_args():
|
|
|
261
262
|
parser.add_argument("--default_type", help="default_type: spot, linear, inverse, futures ...etc", default='linear')
|
|
262
263
|
parser.add_argument("--rate_limit_ms", help="rate_limit_ms: Check your exchange rules", default=100)
|
|
263
264
|
|
|
265
|
+
parser.add_argument("--default_fees_ccy", help="If you're trading crypto, CEX fees USDT, DEX fees USDC in many cases. Default None, in which case gateway won't aggregatge fees from executions for you.", default=None)
|
|
266
|
+
parser.add_argument("--loop_freq_ms", help="Loop delays. Reduce this if you want to trade faster.", default=500)
|
|
267
|
+
|
|
264
268
|
parser.add_argument("--encrypt_decrypt_with_aws_kms", help="Y or N. If encrypt_decrypt_with_aws_kms=N, pass in apikey, secret and passphrase unencrypted (Not recommended, for testing only). If Y, they will be decrypted using AMS KMS key.", default='N')
|
|
265
269
|
parser.add_argument("--aws_kms_key_id", help="AWS KMS key ID", default=None)
|
|
266
270
|
parser.add_argument("--apikey", help="Exchange apikey", default=None)
|
|
@@ -280,6 +284,8 @@ def parse_args():
|
|
|
280
284
|
|
|
281
285
|
param['default_type'] = args.default_type
|
|
282
286
|
param['rate_limit_ms'] = int(args.rate_limit_ms)
|
|
287
|
+
param['default_fees_ccy'] = args.default_fees_ccy
|
|
288
|
+
param['loop_freq_ms'] = int(args.loop_freq_ms)
|
|
283
289
|
|
|
284
290
|
if args.encrypt_decrypt_with_aws_kms:
|
|
285
291
|
if args.encrypt_decrypt_with_aws_kms=='Y':
|
|
@@ -422,6 +428,22 @@ async def watch_orders_task(
|
|
|
422
428
|
|
|
423
429
|
await asyncio.sleep(int(param['loop_freq_ms']/1000))
|
|
424
430
|
|
|
431
|
+
async def send_heartbeat(exchange):
|
|
432
|
+
|
|
433
|
+
await asyncio.sleep(10)
|
|
434
|
+
|
|
435
|
+
while True:
|
|
436
|
+
try:
|
|
437
|
+
first_ws_url = next(iter(exchange.clients))
|
|
438
|
+
client = exchange.clients[first_ws_url]
|
|
439
|
+
message = exchange.ping(client)
|
|
440
|
+
await client.send(message)
|
|
441
|
+
log('Heartbeat sent')
|
|
442
|
+
except Exception as hb_error:
|
|
443
|
+
log(f'Failed to send heartbeat: {hb_error}')
|
|
444
|
+
finally:
|
|
445
|
+
await asyncio.sleep(30)
|
|
446
|
+
|
|
425
447
|
async def execute_one_position(
|
|
426
448
|
exchange : AnyExchange,
|
|
427
449
|
position : DivisiblePosition,
|
|
@@ -649,6 +671,7 @@ async def execute_one_position(
|
|
|
649
671
|
|
|
650
672
|
position.filled_amount = position.get_filled_amount()
|
|
651
673
|
position.average_cost = position.get_average_cost()
|
|
674
|
+
position.fees = position.get_fees()
|
|
652
675
|
|
|
653
676
|
balances = await exchange.fetch_balance() # type: ignore
|
|
654
677
|
if param['default_type']!='spot':
|
|
@@ -673,10 +696,12 @@ async def work(
|
|
|
673
696
|
|
|
674
697
|
# This is how we avoid reprocess same message twice. We check message hash and cache it.
|
|
675
698
|
processed_hash_queue = deque(maxlen=10)
|
|
676
|
-
|
|
699
|
+
|
|
677
700
|
executions : Dict[str, Dict[str, Any]] = {}
|
|
678
701
|
asyncio.create_task(watch_orders_task(exchange, executions))
|
|
679
702
|
|
|
703
|
+
asyncio.create_task(send_heartbeat(exchange))
|
|
704
|
+
|
|
680
705
|
while True:
|
|
681
706
|
try:
|
|
682
707
|
keys = redis_client.keys()
|
|
@@ -700,6 +725,8 @@ async def work(
|
|
|
700
725
|
amount=order['amount'],
|
|
701
726
|
order_type=order['order_type'],
|
|
702
727
|
leg_room_bps=order['leg_room_bps'],
|
|
728
|
+
reduce_only=order['reduce_only'],
|
|
729
|
+
fees_ccy=order['fees_ccy'] if 'fees_ccy' in order else param['default_fees_ccy'],
|
|
703
730
|
slices=order['slices'],
|
|
704
731
|
wait_fill_threshold_ms=order['wait_fill_threshold_ms']
|
|
705
732
|
)
|
|
@@ -69,6 +69,7 @@ if __name__ == '__main__':
|
|
|
69
69
|
side = 'buy',
|
|
70
70
|
amount = 10,
|
|
71
71
|
leg_room_bps = 5,
|
|
72
|
+
reduce_only=False,
|
|
72
73
|
order_type = 'limit',
|
|
73
74
|
slices=5,
|
|
74
75
|
wait_fill_threshold_ms=15000
|
|
@@ -77,12 +78,13 @@ if __name__ == '__main__':
|
|
|
77
78
|
|
|
78
79
|
positions_2 : List[DivisiblePosition] = [
|
|
79
80
|
DivisiblePosition(
|
|
80
|
-
ticker = 'SUSHI/
|
|
81
|
-
side = '
|
|
82
|
-
amount =
|
|
81
|
+
ticker = 'SUSHI/USDT:USDT',
|
|
82
|
+
side = 'buy',
|
|
83
|
+
amount = 10,
|
|
83
84
|
leg_room_bps = 5,
|
|
85
|
+
reduce_only=False,
|
|
84
86
|
order_type = 'limit',
|
|
85
|
-
slices=
|
|
87
|
+
slices=5,
|
|
86
88
|
wait_fill_threshold_ms=15000
|
|
87
89
|
),
|
|
88
90
|
]
|
|
@@ -93,6 +95,7 @@ if __name__ == '__main__':
|
|
|
93
95
|
side = 'sell',
|
|
94
96
|
amount = 0.01,
|
|
95
97
|
leg_room_bps = 5,
|
|
98
|
+
reduce_only=False,
|
|
96
99
|
order_type = 'limit',
|
|
97
100
|
slices=1,
|
|
98
101
|
wait_fill_threshold_ms=60000
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{siglab_py-0.1.11 → siglab_py-0.1.13}/siglab_py/market_data_providers/candles_ta_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|