siglab-py 0.1.12__tar.gz → 0.1.15__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.

Files changed (34) hide show
  1. {siglab_py-0.1.12 → siglab_py-0.1.15}/PKG-INFO +1 -1
  2. {siglab_py-0.1.12 → siglab_py-0.1.15}/pyproject.toml +1 -1
  3. {siglab_py-0.1.12 → siglab_py-0.1.15}/setup.cfg +1 -1
  4. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/orderbooks_provider.py +18 -0
  5. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/ordergateway/client.py +7 -1
  6. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/ordergateway/gateway.py +35 -4
  7. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/ordergateway/test_ordergateway.py +3 -0
  8. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py.egg-info/PKG-INFO +1 -1
  9. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/__init__.py +0 -0
  10. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/constants.py +0 -0
  11. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/exchanges/__init__.py +0 -0
  12. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/exchanges/any_exchange.py +0 -0
  13. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/__init__.py +0 -0
  14. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
  15. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/candles_provider.py +0 -0
  16. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
  17. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
  18. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/market_data_providers/test_provider.py +0 -0
  19. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/ordergateway/__init__.py +0 -0
  20. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
  21. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/tests/__init__.py +0 -0
  22. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/tests/integration/__init__.py +0 -0
  23. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
  24. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/tests/unit/__init__.py +0 -0
  25. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/tests/unit/analytic_util_tests.py +0 -0
  26. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/util/__init__.py +0 -0
  27. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/util/analytic_util.py +0 -0
  28. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/util/aws_util.py +0 -0
  29. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/util/market_data_util.py +0 -0
  30. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py/util/retry_util.py +0 -0
  31. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py.egg-info/SOURCES.txt +0 -0
  32. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py.egg-info/dependency_links.txt +0 -0
  33. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py.egg-info/requires.txt +0 -0
  34. {siglab_py-0.1.12 → siglab_py-0.1.15}/siglab_py.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab_py
3
- Version: 0.1.12
3
+ Version: 0.1.15
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "siglab_py"
7
- version = "0.1.12"
7
+ version = "0.1.15"
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"}
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = siglab_py
3
- version = 0.1.12
3
+ version = 0.1.15
4
4
  description = Market data fetches, TA calculations and generic order gateway.
5
5
  author = r0bbarh00d
6
6
  author_email = r0bbarh00d@gmail.com
@@ -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()
@@ -32,6 +32,7 @@ class Order:
32
32
  amount : float,
33
33
  order_type : str, # market/limit
34
34
  leg_room_bps : float = 0,
35
+ reduce_only : bool = False,
35
36
  fees_ccy : Union[str, None] = None
36
37
  ) -> None:
37
38
  self.ticker = ticker
@@ -39,6 +40,7 @@ class Order:
39
40
  self.amount = amount
40
41
  self.order_type = order_type.strip().lower()
41
42
  self.leg_room_bps = leg_room_bps
43
+ self.reduce_only = reduce_only
42
44
  self.fees_ccy = fees_ccy
43
45
 
44
46
  def to_dict(self) -> Dict[JSON_SERIALIZABLE_TYPES, JSON_SERIALIZABLE_TYPES]:
@@ -48,6 +50,7 @@ class Order:
48
50
  "amount" : self.amount,
49
51
  "order_type" : self.order_type,
50
52
  "leg_room_bps" : self.leg_room_bps,
53
+ "reduce_only" : self.reduce_only,
51
54
  "fees_ccy" : self.fees_ccy
52
55
  }
53
56
 
@@ -63,11 +66,12 @@ class DivisiblePosition(Order):
63
66
  amount : float,
64
67
  order_type : str, # market/limit
65
68
  leg_room_bps : float,
69
+ reduce_only : bool = False,
66
70
  fees_ccy : Union[str, None] = None,
67
71
  slices : int = 1,
68
72
  wait_fill_threshold_ms : float = -1
69
73
  ) -> None:
70
- super().__init__(ticker, side, amount, order_type, leg_room_bps, fees_ccy)
74
+ super().__init__(ticker, side, amount, order_type, leg_room_bps, reduce_only, fees_ccy)
71
75
  self.slices = slices
72
76
  self.wait_fill_threshold_ms = wait_fill_threshold_ms
73
77
  self.multiplier = 1
@@ -91,6 +95,7 @@ class DivisiblePosition(Order):
91
95
  side=self.side,
92
96
  amount=slice_amount_in_base_ccy,
93
97
  leg_room_bps=self.leg_room_bps,
98
+ reduce_only=self.reduce_only,
94
99
  fees_ccy=self.fees_ccy,
95
100
  order_type=self.order_type)
96
101
 
@@ -101,6 +106,7 @@ class DivisiblePosition(Order):
101
106
  side=self.side,
102
107
  amount=remaining_amount_in_base_ccy,
103
108
  leg_room_bps=self.leg_room_bps,
109
+ reduce_only=self.reduce_only,
104
110
  fees_ccy=self.fees_ccy,
105
111
  order_type=self.order_type)
106
112
 
@@ -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,
@@ -461,6 +483,9 @@ async def execute_one_position(
461
483
  rounded_limit_price : float = exchange.price_to_precision(position.ticker, limit_price) # type: ignore
462
484
  rounded_limit_price = float(rounded_limit_price)
463
485
 
486
+ order_params = {
487
+ 'reduceOnly': slice.reduce_only
488
+ }
464
489
  if position.order_type=='limit':
465
490
  if not param['dry_run']:
466
491
  executed_order = await exchange.create_order( # type: ignore
@@ -468,7 +493,8 @@ async def execute_one_position(
468
493
  type = position.order_type,
469
494
  amount = rounded_slice_amount_in_base_ccy,
470
495
  price = rounded_limit_price,
471
- side = position.side
496
+ side = position.side,
497
+ params = order_params
472
498
  )
473
499
  else:
474
500
  executed_order = DUMMY_EXECUTION.copy()
@@ -493,7 +519,8 @@ async def execute_one_position(
493
519
  symbol = position.ticker,
494
520
  type = position.order_type,
495
521
  amount = rounded_slice_amount_in_base_ccy,
496
- side = position.side
522
+ side = position.side,
523
+ params = order_params
497
524
  )
498
525
  else:
499
526
  executed_order = DUMMY_EXECUTION.copy()
@@ -674,10 +701,12 @@ async def work(
674
701
 
675
702
  # This is how we avoid reprocess same message twice. We check message hash and cache it.
676
703
  processed_hash_queue = deque(maxlen=10)
677
-
704
+
678
705
  executions : Dict[str, Dict[str, Any]] = {}
679
706
  asyncio.create_task(watch_orders_task(exchange, executions))
680
707
 
708
+ asyncio.create_task(send_heartbeat(exchange))
709
+
681
710
  while True:
682
711
  try:
683
712
  keys = redis_client.keys()
@@ -701,6 +730,8 @@ async def work(
701
730
  amount=order['amount'],
702
731
  order_type=order['order_type'],
703
732
  leg_room_bps=order['leg_room_bps'],
733
+ reduce_only=order['reduce_only'],
734
+ fees_ccy=order['fees_ccy'] if 'fees_ccy' in order else param['default_fees_ccy'],
704
735
  slices=order['slices'],
705
736
  wait_fill_threshold_ms=order['wait_fill_threshold_ms']
706
737
  )
@@ -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
@@ -81,6 +82,7 @@ if __name__ == '__main__':
81
82
  side = 'buy',
82
83
  amount = 10,
83
84
  leg_room_bps = 5,
85
+ reduce_only=False,
84
86
  order_type = 'limit',
85
87
  slices=5,
86
88
  wait_fill_threshold_ms=15000
@@ -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.12
3
+ Version: 0.1.15
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>