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

@@ -26,7 +26,7 @@ from siglab_py.util.analytic_util import compute_candles_stats
26
26
  '''
27
27
  Usage:
28
28
  set PYTHONPATH=%PYTHONPATH%;D:\dev\siglab\siglab_py
29
- python ccxt_candles_ta_to_csv.py --exchange_name bybit --symbol BTC/USDT:USDT --end_date "2025-03-25 0:0:0" --start_date "2021-03-11 0:0:0" --default_type linear --compute_ta Y --pypy_compatible N
29
+ python ccxt_candles_ta_to_csv.py --exchange_name bybit --symbol BTC/USDT:USDT --end_date "2025-03-11 0:0:0" --start_date "2021-03-11 0:0:0" --default_type linear --compute_ta Y --pypy_compatible N
30
30
 
31
31
  (Remember: python -mpip install siglab_py)
32
32
 
@@ -225,7 +225,6 @@ async def main():
225
225
  boillenger_std_multiples=param['boillenger_std_multiples'],
226
226
  sliding_window_how_many_candles=param['ma_long_intervals'],
227
227
  slow_fast_interval_ratio=(param['ma_long_intervals']/param['ma_short_intervals']),
228
- hurst_exp_window_how_many_candles=param['ma_short_intervals'],
229
228
  pypy_compat=param['pypy_compatible']
230
229
  )
231
230
  compute_candles_stats_elapsed_ms = int((time.time() - start) *1000)
@@ -91,6 +91,9 @@ class DivisiblePosition(Order):
91
91
  self.fees : Union[float, None] = None
92
92
  self.pos : Union[float, None] = None # in base ccy, after execution. (Not in USDT or quote ccy, Not in # contracts)
93
93
 
94
+ self.done : bool = False
95
+ self.execution_err : str = ""
96
+
94
97
  self.dispatched_slices : List[Order] = []
95
98
  self.executions : Dict[str, Dict[str, Any]] = {}
96
99
 
@@ -325,6 +328,8 @@ class DivisiblePosition(Order):
325
328
  rv['filled_amount'] = self.filled_amount
326
329
  rv['average_cost'] = self.average_cost
327
330
  rv['pos'] = self.pos
331
+ rv['done'] = self.done
332
+ rv['execution_err'] = self.execution_err
328
333
  return rv
329
334
 
330
335
  def execute_positions(
@@ -446,251 +446,258 @@ async def execute_one_position(
446
446
  param : Dict,
447
447
  executions : Dict[str, Dict[str, Any]]
448
448
  ):
449
- market : Dict[str, Any] = exchange.markets[position.ticker] if position.ticker in exchange.markets else None # type: ignore
450
- if not market:
451
- raise ArgumentError(f"Market not found for {position.ticker} under {exchange.name}") # type: ignore
452
-
453
- min_amount = float(market['limits']['amount']['min']) if market['limits']['amount']['min'] else 0 # type: ignore
454
- multiplier = market['contractSize'] if 'contractSize' in market and market['contractSize'] else 1
455
- position.multiplier = multiplier
456
-
457
- slices : List[Order] = position.to_slices()
458
- i = 0
459
- for slice in slices:
460
- try:
461
- dt_now : datetime = datetime.now()
462
-
463
- slice_amount_in_base_ccy : float = slice.amount
464
- rounded_slice_amount_in_base_ccy = slice_amount_in_base_ccy / multiplier # After divided by multiplier, rounded_slice_amount_in_base_ccy in number of contracts actually (Not in base ccy).
465
- rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, rounded_slice_amount_in_base_ccy) # type: ignore
466
- rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy) if rounded_slice_amount_in_base_ccy else 0
467
- rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
468
-
469
- if rounded_slice_amount_in_base_ccy==0:
470
- log(f"{position.ticker} Slice amount rounded to zero?! slice amount before rounding: {slice.amount}")
471
- continue
472
-
473
- orderbook = await exchange.fetch_order_book(symbol=position.ticker, limit=3) # type: ignore
474
- if position.side=='buy':
475
- asks = [ ask[0] for ask in orderbook['asks'] ]
476
- best_asks = min(asks)
477
- limit_price : float= best_asks * (1 + position.leg_room_bps/10000)
478
- else:
479
- bids = [ bid[0] for bid in orderbook['bids'] ]
480
- best_bid = max(bids)
481
- limit_price : float = best_bid * (1 - position.leg_room_bps/10000)
482
-
483
- rounded_limit_price : float = exchange.price_to_precision(position.ticker, limit_price) # type: ignore
484
- rounded_limit_price = float(rounded_limit_price)
485
-
486
- order_params = {
487
- 'reduceOnly': slice.reduce_only
488
- }
489
- if position.order_type=='limit':
490
- if not param['dry_run']:
491
- executed_order = await exchange.create_order( # type: ignore
492
- symbol = position.ticker,
493
- type = position.order_type,
494
- amount = rounded_slice_amount_in_base_ccy,
495
- price = rounded_limit_price,
496
- side = position.side,
497
- params = order_params
498
- )
499
- else:
500
- executed_order = DUMMY_EXECUTION.copy()
501
- executed_order['clientOrderId'] = str(uuid.uuid4())
502
- executed_order['timestamp'] = dt_now.timestamp()
503
- executed_order['datetime'] = dt_now
504
- executed_order['symbol'] = position.ticker
505
- executed_order['type'] = position.order_type
506
- executed_order['side'] = position.side
507
- executed_order['price'] = rounded_limit_price
508
- executed_order['average'] = rounded_limit_price
509
- executed_order['cost'] = 0
510
- executed_order['amount'] = rounded_slice_amount_in_base_ccy
511
- executed_order['filled'] = rounded_slice_amount_in_base_ccy
512
- executed_order['remaining'] = 0
513
- executed_order['status'] = 'closed'
514
- executed_order['multiplier'] = position.multiplier
515
-
516
- else:
517
- if not param['dry_run']:
518
- executed_order = await exchange.create_order( # type: ignore
519
- symbol = position.ticker,
520
- type = position.order_type,
521
- amount = rounded_slice_amount_in_base_ccy,
522
- side = position.side,
523
- params = order_params
524
- )
449
+ try:
450
+ market : Dict[str, Any] = exchange.markets[position.ticker] if position.ticker in exchange.markets else None # type: ignore
451
+ if not market:
452
+ raise ArgumentError(f"Market not found for {position.ticker} under {exchange.name}") # type: ignore
453
+
454
+ min_amount = float(market['limits']['amount']['min']) if market['limits']['amount']['min'] else 0 # type: ignore
455
+ multiplier = market['contractSize'] if 'contractSize' in market and market['contractSize'] else 1
456
+ position.multiplier = multiplier
457
+
458
+ slices : List[Order] = position.to_slices()
459
+ i = 0
460
+ for slice in slices:
461
+ try:
462
+ dt_now : datetime = datetime.now()
463
+
464
+ slice_amount_in_base_ccy : float = slice.amount
465
+ rounded_slice_amount_in_base_ccy = slice_amount_in_base_ccy / multiplier # After divided by multiplier, rounded_slice_amount_in_base_ccy in number of contracts actually (Not in base ccy).
466
+ rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, rounded_slice_amount_in_base_ccy) # type: ignore
467
+ rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy) if rounded_slice_amount_in_base_ccy else 0
468
+ rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
469
+
470
+ if rounded_slice_amount_in_base_ccy==0:
471
+ log(f"{position.ticker} Slice amount rounded to zero?! slice amount before rounding: {slice.amount}")
472
+ continue
473
+
474
+ orderbook = await exchange.fetch_order_book(symbol=position.ticker, limit=3) # type: ignore
475
+ if position.side=='buy':
476
+ asks = [ ask[0] for ask in orderbook['asks'] ]
477
+ best_asks = min(asks)
478
+ limit_price : float= best_asks * (1 + position.leg_room_bps/10000)
525
479
  else:
526
- executed_order = DUMMY_EXECUTION.copy()
527
- executed_order['clientOrderId'] = str(uuid.uuid4())
528
- executed_order['timestamp'] = dt_now.timestamp()
529
- executed_order['datetime'] = dt_now
530
- executed_order['symbol'] = position.ticker
531
- executed_order['type'] = position.order_type
532
- executed_order['side'] = position.side
533
- executed_order['price'] = rounded_limit_price
534
- executed_order['average'] = rounded_limit_price
535
- executed_order['cost'] = 0
536
- executed_order['amount'] = rounded_slice_amount_in_base_ccy
537
- executed_order['filled'] = rounded_slice_amount_in_base_ccy
538
- executed_order['remaining'] = 0
539
- executed_order['status'] = 'closed'
540
- executed_order['multiplier'] = position.multiplier
541
-
542
- executed_order['slice_id'] = i
543
-
544
- '''
545
- Format of executed_order:
546
- executed_order
547
- {'info': {'clOrdId': 'xxx', 'ordId': '2245241151525347328', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'xxx', 'ts': '1739415800635'}, 'id': '2245241151525347328', 'clientOrderId': 'xxx', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'SUSHI/USDT:USDT', 'type': 'limit', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopLossPrice': None, 'takeProfitPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': False, 'fees': [], 'stopPrice': None}
548
- special variables:
549
- function variables:
550
- 'info': {'clOrdId': 'xxx', 'ordId': '2245241151525347328', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'xxx', 'ts': '1739415800635'}
551
- 'id': '2245241151525347328'
552
- 'clientOrderId': 'xxx'
553
- 'timestamp': None
554
- 'datetime': None
555
- 'lastTradeTimestamp': None
556
- 'lastUpdateTimestamp': None
557
- 'symbol': 'SUSHI/USDT:USDT'
558
- 'type': 'limit'
559
- 'timeInForce': None
560
- 'postOnly': None
561
- 'side': 'buy'
562
- 'price': None
563
- 'stopLossPrice': None
564
- 'takeProfitPrice': None
565
- 'triggerPrice': None
566
- 'average': None
567
- 'cost': None
568
- 'amount': None
569
- 'filled': None
570
- 'remaining': None
571
- 'status': None
572
- 'fee': None
573
- 'trades': []
574
- 'reduceOnly': False
575
- 'fees': []
576
- 'stopPrice': None
577
- '''
578
- order_id = executed_order['id']
579
- order_status = executed_order['status']
580
- filled_amount = executed_order['filled']
581
- remaining_amount = executed_order['remaining']
582
- executed_order['multiplier'] = multiplier
583
- position.append_execution(order_id, executed_order)
584
-
585
- log(f"Order dispatched: {order_id}. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
586
-
587
- if not order_status or order_status!='closed':
588
- start_time = time.time()
589
- wait_threshold_sec = position.wait_fill_threshold_ms / 1000
590
-
591
- elapsed_sec = time.time() - start_time
592
- while elapsed_sec < wait_threshold_sec:
593
- order_update = None
594
- if order_id in executions:
595
- order_update = executions[order_id]
480
+ bids = [ bid[0] for bid in orderbook['bids'] ]
481
+ best_bid = max(bids)
482
+ limit_price : float = best_bid * (1 - position.leg_room_bps/10000)
596
483
 
597
- if order_update:
598
- order_status = order_update['status']
599
- filled_amount = order_update['filled']
600
- remaining_amount = order_update['remaining']
601
- order_update['multiplier'] = multiplier
602
- position.append_execution(order_id, order_update)
603
-
604
- if remaining_amount <= 0:
605
- log(f"Limit order fully filled: {order_id}", log_level=LogLevel.INFO)
606
- break
484
+ rounded_limit_price : float = exchange.price_to_precision(position.ticker, limit_price) # type: ignore
485
+ rounded_limit_price = float(rounded_limit_price)
486
+
487
+ order_params = {
488
+ 'reduceOnly': slice.reduce_only
489
+ }
490
+ if position.order_type=='limit':
491
+ if not param['dry_run']:
492
+ executed_order = await exchange.create_order( # type: ignore
493
+ symbol = position.ticker,
494
+ type = position.order_type,
495
+ amount = rounded_slice_amount_in_base_ccy,
496
+ price = rounded_limit_price,
497
+ side = position.side,
498
+ params = order_params
499
+ )
500
+ else:
501
+ executed_order = DUMMY_EXECUTION.copy()
502
+ executed_order['clientOrderId'] = str(uuid.uuid4())
503
+ executed_order['timestamp'] = dt_now.timestamp()
504
+ executed_order['datetime'] = dt_now
505
+ executed_order['symbol'] = position.ticker
506
+ executed_order['type'] = position.order_type
507
+ executed_order['side'] = position.side
508
+ executed_order['price'] = rounded_limit_price
509
+ executed_order['average'] = rounded_limit_price
510
+ executed_order['cost'] = 0
511
+ executed_order['amount'] = rounded_slice_amount_in_base_ccy
512
+ executed_order['filled'] = rounded_slice_amount_in_base_ccy
513
+ executed_order['remaining'] = 0
514
+ executed_order['status'] = 'closed'
515
+ executed_order['multiplier'] = position.multiplier
607
516
 
608
- await asyncio.sleep(int(param['loop_freq_ms']/1000))
609
-
610
-
611
- # Cancel hung limit order, resend as market
612
- if order_status!='closed':
613
- # If no update from websocket, do one last fetch via REST
614
- order_update = await exchange.fetch_order(order_id, position.ticker) # type: ignore
615
- order_status = order_update['status']
616
- filled_amount = order_update['filled']
617
- remaining_amount = order_update['remaining']
618
- order_update['multiplier'] = multiplier
619
- position.append_execution(order_id, order_update)
620
-
621
- if order_status!='closed':
517
+ else:
518
+ if not param['dry_run']:
519
+ executed_order = await exchange.create_order( # type: ignore
520
+ symbol = position.ticker,
521
+ type = position.order_type,
522
+ amount = rounded_slice_amount_in_base_ccy,
523
+ side = position.side,
524
+ params = order_params
525
+ )
526
+ else:
527
+ executed_order = DUMMY_EXECUTION.copy()
528
+ executed_order['clientOrderId'] = str(uuid.uuid4())
529
+ executed_order['timestamp'] = dt_now.timestamp()
530
+ executed_order['datetime'] = dt_now
531
+ executed_order['symbol'] = position.ticker
532
+ executed_order['type'] = position.order_type
533
+ executed_order['side'] = position.side
534
+ executed_order['price'] = rounded_limit_price
535
+ executed_order['average'] = rounded_limit_price
536
+ executed_order['cost'] = 0
537
+ executed_order['amount'] = rounded_slice_amount_in_base_ccy
538
+ executed_order['filled'] = rounded_slice_amount_in_base_ccy
539
+ executed_order['remaining'] = 0
540
+ executed_order['status'] = 'closed'
541
+ executed_order['multiplier'] = position.multiplier
542
+
543
+ executed_order['slice_id'] = i
544
+
545
+ '''
546
+ Format of executed_order:
547
+ executed_order
548
+ {'info': {'clOrdId': 'xxx', 'ordId': '2245241151525347328', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'xxx', 'ts': '1739415800635'}, 'id': '2245241151525347328', 'clientOrderId': 'xxx', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'lastUpdateTimestamp': None, 'symbol': 'SUSHI/USDT:USDT', 'type': 'limit', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopLossPrice': None, 'takeProfitPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': False, 'fees': [], 'stopPrice': None}
549
+ special variables:
550
+ function variables:
551
+ 'info': {'clOrdId': 'xxx', 'ordId': '2245241151525347328', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'xxx', 'ts': '1739415800635'}
552
+ 'id': '2245241151525347328'
553
+ 'clientOrderId': 'xxx'
554
+ 'timestamp': None
555
+ 'datetime': None
556
+ 'lastTradeTimestamp': None
557
+ 'lastUpdateTimestamp': None
558
+ 'symbol': 'SUSHI/USDT:USDT'
559
+ 'type': 'limit'
560
+ 'timeInForce': None
561
+ 'postOnly': None
562
+ 'side': 'buy'
563
+ 'price': None
564
+ 'stopLossPrice': None
565
+ 'takeProfitPrice': None
566
+ 'triggerPrice': None
567
+ 'average': None
568
+ 'cost': None
569
+ 'amount': None
570
+ 'filled': None
571
+ 'remaining': None
572
+ 'status': None
573
+ 'fee': None
574
+ 'trades': []
575
+ 'reduceOnly': False
576
+ 'fees': []
577
+ 'stopPrice': None
578
+ '''
579
+ order_id = executed_order['id']
580
+ order_status = executed_order['status']
581
+ filled_amount = executed_order['filled']
582
+ remaining_amount = executed_order['remaining']
583
+ executed_order['multiplier'] = multiplier
584
+ position.append_execution(order_id, executed_order)
585
+
586
+ log(f"Order dispatched: {order_id}. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
587
+
588
+ if not order_status or order_status!='closed':
589
+ start_time = time.time()
590
+ wait_threshold_sec = position.wait_fill_threshold_ms / 1000
591
+
592
+ elapsed_sec = time.time() - start_time
593
+ while elapsed_sec < wait_threshold_sec:
594
+ order_update = None
595
+ if order_id in executions:
596
+ order_update = executions[order_id]
597
+
598
+ if order_update:
599
+ order_status = order_update['status']
600
+ filled_amount = order_update['filled']
601
+ remaining_amount = order_update['remaining']
602
+ order_update['multiplier'] = multiplier
603
+ position.append_execution(order_id, order_update)
604
+
605
+ if remaining_amount <= 0:
606
+ log(f"Limit order fully filled: {order_id}", log_level=LogLevel.INFO)
607
+ break
608
+
609
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
610
+
611
+
612
+ # Cancel hung limit order, resend as market
613
+ if order_status!='closed':
614
+ # If no update from websocket, do one last fetch via REST
615
+ order_update = await exchange.fetch_order(order_id, position.ticker) # type: ignore
622
616
  order_status = order_update['status']
623
617
  filled_amount = order_update['filled']
624
618
  remaining_amount = order_update['remaining']
619
+ order_update['multiplier'] = multiplier
620
+ position.append_execution(order_id, order_update)
625
621
 
626
- await exchange.cancel_order(order_id, position.ticker) # type: ignore
627
- position.get_execution(order_id)['status'] = 'canceled'
628
- log(f"Canceled unfilled/partial filled order: {order_id}. Resending remaining_amount: {remaining_amount} as market order.", log_level=LogLevel.INFO)
629
-
630
- rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, remaining_amount) # type: ignore
631
- rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy)
632
- rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
633
- if rounded_slice_amount_in_base_ccy>0:
634
- executed_resent_order = await exchange.create_order( # type: ignore
635
- symbol=position.ticker,
636
- type='market',
637
- amount=remaining_amount,
638
- side=position.side
639
- )
640
-
641
- order_id = executed_resent_order['id']
642
- order_status = executed_resent_order['status']
643
- executed_resent_order['multiplier'] = multiplier
644
- position.append_execution(order_id, executed_resent_order)
645
-
646
- while not order_status or order_status!='closed':
647
- order_update = None
648
- if order_id in executions:
649
- order_update = executions[order_id]
650
-
651
- if order_update:
652
- order_id = order_update['id']
653
- order_status = order_update['status']
654
- filled_amount = order_update['filled']
655
- remaining_amount = order_update['remaining']
656
-
657
- log(f"Waiting for resent market order to close {order_id} ...")
622
+ if order_status!='closed':
623
+ order_status = order_update['status']
624
+ filled_amount = order_update['filled']
625
+ remaining_amount = order_update['remaining']
658
626
 
659
- await asyncio.sleep(int(param['loop_freq_ms']/1000))
627
+ await exchange.cancel_order(order_id, position.ticker) # type: ignore
628
+ position.get_execution(order_id)['status'] = 'canceled'
629
+ log(f"Canceled unfilled/partial filled order: {order_id}. Resending remaining_amount: {remaining_amount} as market order.", log_level=LogLevel.INFO)
630
+
631
+ rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, remaining_amount) # type: ignore
632
+ rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy)
633
+ rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
634
+ if rounded_slice_amount_in_base_ccy>0:
635
+ executed_resent_order = await exchange.create_order( # type: ignore
636
+ symbol=position.ticker,
637
+ type='market',
638
+ amount=remaining_amount,
639
+ side=position.side
640
+ )
641
+
642
+ order_id = executed_resent_order['id']
643
+ order_status = executed_resent_order['status']
644
+ executed_resent_order['multiplier'] = multiplier
645
+ position.append_execution(order_id, executed_resent_order)
646
+
647
+ while not order_status or order_status!='closed':
648
+ order_update = None
649
+ if order_id in executions:
650
+ order_update = executions[order_id]
651
+
652
+ if order_update:
653
+ order_id = order_update['id']
654
+ order_status = order_update['status']
655
+ filled_amount = order_update['filled']
656
+ remaining_amount = order_update['remaining']
657
+
658
+ log(f"Waiting for resent market order to close {order_id} ...")
659
+
660
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
661
+
662
+ log(f"Resent market order{order_id} filled. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
663
+
664
+ slice.dispatched_price = rounded_limit_price
665
+ slice.dispatched_amount = rounded_slice_amount_in_base_ccy
666
+ position.dispatched_slices.append(slice)
667
+
668
+ log(f"Executed slice #{i}", log_level=LogLevel.INFO)
669
+ log(f"{position.ticker}, multiplier: {multiplier}, slice_amount_in_base_ccy: {slice_amount_in_base_ccy}, rounded_slice_amount_in_base_ccy, {rounded_slice_amount_in_base_ccy}", log_level=LogLevel.INFO)
670
+ if position.order_type=='limit':
671
+ log(f"{position.ticker}, limit_price: {limit_price}, rounded_limit_price, {rounded_limit_price}", log_level=LogLevel.INFO)
672
+
673
+ except Exception as slice_err:
674
+ log(
675
+ f"Failed to execute #{i} slice: {slice.to_dict()}. {slice_err} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}",
676
+ log_level=LogLevel.ERROR
677
+ )
678
+ finally:
679
+ i += 1
660
680
 
661
- log(f"Resent market order{order_id} filled. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
681
+ position.patch_executions()
682
+ position.filled_amount = position.get_filled_amount()
683
+ position.average_cost = position.get_average_cost()
684
+ position.fees = position.get_fees()
662
685
 
663
- slice.dispatched_price = rounded_limit_price
664
- slice.dispatched_amount = rounded_slice_amount_in_base_ccy
665
- position.dispatched_slices.append(slice)
686
+ balances = await exchange.fetch_balance() # type: ignore
687
+ if param['default_type']!='spot':
688
+ updated_position = await exchange.fetch_position(symbol=position.ticker) # type: ignore
689
+ # After position closed, 'updated_position' can be an empty dict. hyperliquid for example.
690
+ amount = (updated_position['contracts'] if updated_position else 0) * position.multiplier # in base ccy
691
+ else:
692
+ base_ccy : str = position.ticker.split("/")[0]
693
+ amount = balances[base_ccy]['total']
694
+ position.pos = amount
666
695
 
667
- log(f"Executed slice #{i}", log_level=LogLevel.INFO)
668
- log(f"{position.ticker}, multiplier: {multiplier}, slice_amount_in_base_ccy: {slice_amount_in_base_ccy}, rounded_slice_amount_in_base_ccy, {rounded_slice_amount_in_base_ccy}", log_level=LogLevel.INFO)
669
- if position.order_type=='limit':
670
- log(f"{position.ticker}, limit_price: {limit_price}, rounded_limit_price, {rounded_limit_price}", log_level=LogLevel.INFO)
696
+ position.done = True
671
697
 
672
- except Exception as slice_err:
673
- log(
674
- f"Failed to execute #{i} slice: {slice.to_dict()}. {slice_err} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}",
675
- log_level=LogLevel.ERROR
676
- )
677
- finally:
678
- i += 1
679
-
680
- position.patch_executions()
681
- position.filled_amount = position.get_filled_amount()
682
- position.average_cost = position.get_average_cost()
683
- position.fees = position.get_fees()
684
-
685
- balances = await exchange.fetch_balance() # type: ignore
686
- if param['default_type']!='spot':
687
- updated_position = await exchange.fetch_position(symbol=position.ticker) # type: ignore
688
- # After position closed, 'updated_position' can be an empty dict. hyperliquid for example.
689
- amount = (updated_position['contracts'] if updated_position else 0) * position.multiplier # in base ccy
690
- else:
691
- base_ccy : str = position.ticker.split("/")[0]
692
- amount = balances[base_ccy]['total']
693
- position.pos = amount
698
+ except Exception as position_execution_err:
699
+ position.done = False
700
+ position.execution_err = position_execution_err
694
701
 
695
702
  async def work(
696
703
  param : Dict,
@@ -21,7 +21,7 @@ def timestamp_to_datetime_cols(pd_candles : pd.DataFrame):
21
21
  pd_candles['timestamp_ms'] = pd_candles['timestamp_ms'].apply(
22
22
  lambda x: int(x.timestamp()) if isinstance(x, pd.Timestamp) else (int(x) if len(str(int(x)))==13 else int(x*1000))
23
23
  )
24
- pd_candles['datetime'] = pd_candles['timestamp_ms'].apply(lambda x: datetime.fromtimestamp(int(x)))
24
+ pd_candles['datetime'] = pd_candles['timestamp_ms'].apply(lambda x: datetime.fromtimestamp(int(x/1000)))
25
25
  pd_candles['datetime'] = pd.to_datetime(pd_candles['datetime'])
26
26
  pd_candles['datetime'] = pd_candles['datetime'].dt.tz_localize(None)
27
27
  pd_candles['datetime_utc'] = pd_candles['timestamp_ms'].apply(
@@ -37,17 +37,17 @@ def timestamp_to_datetime_cols(pd_candles : pd.DataFrame):
37
37
  pd_candles['dayofweek'] = pd_candles['datetime'].dt.dayofweek # dayofweek: Monday is 0 and Sunday is 6
38
38
 
39
39
  pd_candles['week_of_month'] = pd_candles['timestamp_ms'].apply(
40
- lambda x: timestamp_to_week_of_month(x)
40
+ lambda x: timestamp_to_week_of_month(int(x/1000))
41
41
  )
42
42
 
43
43
  pd_candles['apac_trading_hr'] = pd_candles['timestamp_ms'].apply(
44
- lambda x: "APAC" in timestamp_to_active_trading_regions(x)
44
+ lambda x: "APAC" in timestamp_to_active_trading_regions(int(x/1000))
45
45
  )
46
46
  pd_candles['emea_trading_hr'] = pd_candles['timestamp_ms'].apply(
47
- lambda x: "EMEA" in timestamp_to_active_trading_regions(x)
47
+ lambda x: "EMEA" in timestamp_to_active_trading_regions(int(x/1000))
48
48
  )
49
49
  pd_candles['amer_trading_hr'] = pd_candles['timestamp_ms'].apply(
50
- lambda x: "AMER" in timestamp_to_active_trading_regions(x)
50
+ lambda x: "AMER" in timestamp_to_active_trading_regions(int(x/1000))
51
51
  )
52
52
 
53
53
  def timestamp_to_active_trading_regions(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab-py
3
- Version: 0.1.38
3
+ Version: 0.1.51
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,15 +7,15 @@ siglab_py/market_data_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm
7
7
  siglab_py/market_data_providers/aggregated_orderbook_provider.py,sha256=FZRobEBNRzcNGlOG3u38OVhmOZYlkNm8dVvR-S7Ii2g,23342
8
8
  siglab_py/market_data_providers/candles_provider.py,sha256=fqHJjlECsBiBlpgyywrc4gTgxiROPNzZM8KxQBB5cOg,14139
9
9
  siglab_py/market_data_providers/candles_ta_provider.py,sha256=uiAhbEZZdTF-YulBHpSLwabos5LHCKU91NTiTmpUc0w,12001
10
- siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py,sha256=xQh2N1Qc9Exs2ODPeH4S1rt4tNWLp-_qDp-Puox6Baw,11239
10
+ siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py,sha256=8-cKcZXFBHLX_-KmPXBKv1QNqJYUdDp6iqa1miVlpF8,11139
11
11
  siglab_py/market_data_providers/deribit_options_expiry_provider.py,sha256=e9Ee8TmC8pXaid8-jouSLKIpuW6_JBBgwRTieI665yQ,8684
12
12
  siglab_py/market_data_providers/futu_candles_ta_to_csv.py,sha256=S4GXaJ7AveEh-Cm9-VhENBdlj_1CfyBTrQO7acTqfUE,10226
13
13
  siglab_py/market_data_providers/orderbooks_provider.py,sha256=olt-3LIkoyzQWfNNQRhJtKibLbkTutt_q_rCCTM7i1g,16216
14
14
  siglab_py/market_data_providers/test_provider.py,sha256=wBLCgcWjs7FGZJXWsNyn30lkOLa_cgpuvqRakMC0wbA,2221
15
15
  siglab_py/ordergateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- siglab_py/ordergateway/client.py,sha256=gtUyR41xXuAcBNoVCSlcg99koiQphe8gUBxlEgvWYt8,14637
16
+ siglab_py/ordergateway/client.py,sha256=sn9_QqoJwQC0Aj89icOReztW_D4LxPt9g93vCSI5-j4,14794
17
17
  siglab_py/ordergateway/encrypt_keys_util.py,sha256=-qi87db8To8Yf1WS1Q_Cp2Ya7ZqgWlRqSHfNXCM7wE4,1339
18
- siglab_py/ordergateway/gateway.py,sha256=VI-OIc3enhQ78cFNzAbLgX0Pzyjr2tl9VDV3IZTAtMQ,37438
18
+ siglab_py/ordergateway/gateway.py,sha256=Owq7QquD3pmvzXft0wkDHs_rMmt7kiXT771ZmG02BA8,38495
19
19
  siglab_py/ordergateway/test_ordergateway.py,sha256=_Gz2U_VqljogGWqGyNDYYls1INqUiig9veyPttfGRpg,3901
20
20
  siglab_py/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  siglab_py/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -26,9 +26,9 @@ siglab_py/tests/unit/market_data_util_tests.py,sha256=A1y83itISmMJdn6wLpfwcr4tGo
26
26
  siglab_py/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  siglab_py/util/analytic_util.py,sha256=xo9gD1ELQt_1v84yu9d4NgxtOIXthePZGipvDrjZAQ8,43834
28
28
  siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
29
- siglab_py/util/market_data_util.py,sha256=RZ-N8UerEC3svMkJ74ab4eKocWvROCHaOykd9oog85w,19441
29
+ siglab_py/util/market_data_util.py,sha256=QJGYIO-ixzjbOm3pY1koiDd_UV5-_QS7JrwuaCn7kug,19486
30
30
  siglab_py/util/retry_util.py,sha256=mxYuRFZRZoaQQjENcwPmxhxixtd1TFvbxIdPx4RwfRc,743
31
- siglab_py-0.1.38.dist-info/METADATA,sha256=B-M6RDjMYbWaYSFPNvQ5fkVovEZXI6B1mlQDhKG_Qvw,980
32
- siglab_py-0.1.38.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
33
- siglab_py-0.1.38.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
34
- siglab_py-0.1.38.dist-info/RECORD,,
31
+ siglab_py-0.1.51.dist-info/METADATA,sha256=-_AbTmpyR4M72ARWFjPXg7V0oTHsPUU3S5N775NfhvA,980
32
+ siglab_py-0.1.51.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
33
+ siglab_py-0.1.51.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
34
+ siglab_py-0.1.51.dist-info/RECORD,,