siglab-py 0.1.11__py3-none-any.whl → 0.1.13__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.

@@ -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
- "loop_freq_ms" : 500,
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/USDC:USDC',
81
- side = 'sell',
82
- amount = 15,
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=1,
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab-py
3
- Version: 0.1.11
3
+ Version: 0.1.13
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -7,13 +7,13 @@ siglab_py/market_data_providers/aggregated_orderbook_provider.py,sha256=FZRobEBN
7
7
  siglab_py/market_data_providers/candles_provider.py,sha256=fqHJjlECsBiBlpgyywrc4gTgxiROPNzZM8KxQBB5cOg,14139
8
8
  siglab_py/market_data_providers/candles_ta_provider.py,sha256=uiAhbEZZdTF-YulBHpSLwabos5LHCKU91NTiTmpUc0w,12001
9
9
  siglab_py/market_data_providers/deribit_options_expiry_provider.py,sha256=e9Ee8TmC8pXaid8-jouSLKIpuW6_JBBgwRTieI665yQ,8684
10
- siglab_py/market_data_providers/orderbooks_provider.py,sha256=cBp-HYCups2Uiwzw0SaUwxrg4unJvnm2TDqIK8f4hUg,15674
10
+ siglab_py/market_data_providers/orderbooks_provider.py,sha256=olt-3LIkoyzQWfNNQRhJtKibLbkTutt_q_rCCTM7i1g,16216
11
11
  siglab_py/market_data_providers/test_provider.py,sha256=wBLCgcWjs7FGZJXWsNyn30lkOLa_cgpuvqRakMC0wbA,2221
12
12
  siglab_py/ordergateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- siglab_py/ordergateway/client.py,sha256=1Y0g2AT_Sh7Y-2YdEpnrGHwu2Ml1iqKFmj8_LOoF8YU,8301
13
+ siglab_py/ordergateway/client.py,sha256=4X5rR_jebh7rkw5ANGMHSmoONXbFHzxX5SPt6ZzeOdA,9257
14
14
  siglab_py/ordergateway/encrypt_keys_util.py,sha256=-qi87db8To8Yf1WS1Q_Cp2Ya7ZqgWlRqSHfNXCM7wE4,1339
15
- siglab_py/ordergateway/gateway.py,sha256=N0XrsQDsycSjRDMC9jnC5B1q9LAfz4er0vjlDaGOhYc,35446
16
- siglab_py/ordergateway/test_ordergateway.py,sha256=vYdB6IypDI8-9Ts3-7g6F9CMKf4nUVG4qQalS4fUu-8,3806
15
+ siglab_py/ordergateway/gateway.py,sha256=9y60GXrvRnzOO28rhsa4mBV-eV0vetnBAj1Gr3azCFQ,36747
16
+ siglab_py/ordergateway/test_ordergateway.py,sha256=_Gz2U_VqljogGWqGyNDYYls1INqUiig9veyPttfGRpg,3901
17
17
  siglab_py/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  siglab_py/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  siglab_py/tests/integration/market_data_util_tests.py,sha256=HFESIXEdQmrbDV3DvLiDLq0iE-4itysxSFur_Th4jjE,5207
@@ -24,7 +24,7 @@ siglab_py/util/analytic_util.py,sha256=QLabbEMqM4rKKH2PE_LqxIyo-BUdCRhkLybLATBIm
24
24
  siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
25
25
  siglab_py/util/market_data_util.py,sha256=3qTq71xGvQXj0ORKJV50IN5FP_mCBF_gvdmlPyhdyco,16439
26
26
  siglab_py/util/retry_util.py,sha256=mxYuRFZRZoaQQjENcwPmxhxixtd1TFvbxIdPx4RwfRc,743
27
- siglab_py-0.1.11.dist-info/METADATA,sha256=PLGWh1JwOPtp8Ai8kIZvCxgCMbpV0SkAvLB2Ey5Qj5g,1097
28
- siglab_py-0.1.11.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
29
- siglab_py-0.1.11.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
30
- siglab_py-0.1.11.dist-info/RECORD,,
27
+ siglab_py-0.1.13.dist-info/METADATA,sha256=K059FaL9RhsHiKNV3rKYnOArB9K93ePUg8r39ZIPJrQ,1097
28
+ siglab_py-0.1.13.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
29
+ siglab_py-0.1.13.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
30
+ siglab_py-0.1.13.dist-info/RECORD,,