siglab-py 0.1.50__py3-none-any.whl → 0.1.52__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.
- siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +1 -2
- siglab_py/ordergateway/client.py +6 -0
- siglab_py/ordergateway/gateway.py +241 -235
- {siglab_py-0.1.50.dist-info → siglab_py-0.1.52.dist-info}/METADATA +1 -1
- {siglab_py-0.1.50.dist-info → siglab_py-0.1.52.dist-info}/RECORD +7 -7
- {siglab_py-0.1.50.dist-info → siglab_py-0.1.52.dist-info}/WHEEL +0 -0
- {siglab_py-0.1.50.dist-info → siglab_py-0.1.52.dist-info}/top_level.txt +0 -0
|
@@ -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-
|
|
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)
|
siglab_py/ordergateway/client.py
CHANGED
|
@@ -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
|
|
|
@@ -324,7 +327,10 @@ class DivisiblePosition(Order):
|
|
|
324
327
|
rv['executions'] = self.executions
|
|
325
328
|
rv['filled_amount'] = self.filled_amount
|
|
326
329
|
rv['average_cost'] = self.average_cost
|
|
330
|
+
rv['fees'] = self.fees
|
|
327
331
|
rv['pos'] = self.pos
|
|
332
|
+
rv['done'] = self.done
|
|
333
|
+
rv['execution_err'] = self.execution_err
|
|
328
334
|
return rv
|
|
329
335
|
|
|
330
336
|
def execute_positions(
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from ctypes import ArgumentError
|
|
2
1
|
import sys
|
|
3
2
|
import traceback
|
|
4
3
|
import os
|
|
@@ -392,7 +391,7 @@ async def instantiate_exchange(
|
|
|
392
391
|
}
|
|
393
392
|
) # type: ignore
|
|
394
393
|
else:
|
|
395
|
-
raise
|
|
394
|
+
raise ValueError(f"Unsupported exchange {exchange_name}, check gateway_id {gateway_id}.")
|
|
396
395
|
|
|
397
396
|
await exchange.load_markets() # type: ignore
|
|
398
397
|
|
|
@@ -446,251 +445,258 @@ async def execute_one_position(
|
|
|
446
445
|
param : Dict,
|
|
447
446
|
executions : Dict[str, Dict[str, Any]]
|
|
448
447
|
):
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
-
)
|
|
448
|
+
try:
|
|
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)
|
|
525
478
|
else:
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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]
|
|
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)
|
|
596
482
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
|
607
515
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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
|
+
)
|
|
525
|
+
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]
|
|
596
|
+
|
|
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
|
|
607
|
+
|
|
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
|
|
622
615
|
order_status = order_update['status']
|
|
623
616
|
filled_amount = order_update['filled']
|
|
624
617
|
remaining_amount = order_update['remaining']
|
|
618
|
+
order_update['multiplier'] = multiplier
|
|
619
|
+
position.append_execution(order_id, order_update)
|
|
625
620
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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} ...")
|
|
621
|
+
if order_status!='closed':
|
|
622
|
+
order_status = order_update['status']
|
|
623
|
+
filled_amount = order_update['filled']
|
|
624
|
+
remaining_amount = order_update['remaining']
|
|
658
625
|
|
|
659
|
-
|
|
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} ...")
|
|
658
|
+
|
|
659
|
+
await asyncio.sleep(int(param['loop_freq_ms']/1000))
|
|
660
|
+
|
|
661
|
+
log(f"Resent market order{order_id} filled. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
|
|
662
|
+
|
|
663
|
+
slice.dispatched_price = rounded_limit_price
|
|
664
|
+
slice.dispatched_amount = rounded_slice_amount_in_base_ccy
|
|
665
|
+
position.dispatched_slices.append(slice)
|
|
666
|
+
|
|
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)
|
|
671
|
+
|
|
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
|
|
660
679
|
|
|
661
|
-
|
|
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()
|
|
662
684
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
position.
|
|
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
|
|
666
694
|
|
|
667
|
-
|
|
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)
|
|
695
|
+
position.done = True
|
|
671
696
|
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
|
697
|
+
except Exception as position_execution_err:
|
|
698
|
+
position.done = False
|
|
699
|
+
position.execution_err = f"Execution failed: {position_execution_err} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}"
|
|
694
700
|
|
|
695
701
|
async def work(
|
|
696
702
|
param : Dict,
|
|
@@ -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=
|
|
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=
|
|
16
|
+
siglab_py/ordergateway/client.py,sha256=EwoVKxEcngIs8-b4MThPBdZfFIWJg1OFAKG9bwC5BYw,14826
|
|
17
17
|
siglab_py/ordergateway/encrypt_keys_util.py,sha256=-qi87db8To8Yf1WS1Q_Cp2Ya7ZqgWlRqSHfNXCM7wE4,1339
|
|
18
|
-
siglab_py/ordergateway/gateway.py,sha256=
|
|
18
|
+
siglab_py/ordergateway/gateway.py,sha256=VkTTNDVWQxmhgdDNd4d0Xg0TYA-yVKNenemqLz5ZXmA,38556
|
|
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
|
|
@@ -28,7 +28,7 @@ siglab_py/util/analytic_util.py,sha256=xo9gD1ELQt_1v84yu9d4NgxtOIXthePZGipvDrjZA
|
|
|
28
28
|
siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
|
|
29
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.
|
|
32
|
-
siglab_py-0.1.
|
|
33
|
-
siglab_py-0.1.
|
|
34
|
-
siglab_py-0.1.
|
|
31
|
+
siglab_py-0.1.52.dist-info/METADATA,sha256=93KeqsFYmMe8_ydvspXrxw-fhv8DUJ-OFjzvFUmTxjQ,980
|
|
32
|
+
siglab_py-0.1.52.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
33
|
+
siglab_py-0.1.52.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
|
|
34
|
+
siglab_py-0.1.52.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|