siglab-py 0.3.0__py3-none-any.whl → 0.3.2__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/ordergateway/gateway.py +11 -10
- siglab_py/tests/unit/analytic_util_tests.py +1 -1
- siglab_py/tests/unit/trading_util_tests.py +6 -6
- siglab_py/util/analytic_util.py +16 -6
- siglab_py/util/trading_util.py +7 -7
- {siglab_py-0.3.0.dist-info → siglab_py-0.3.2.dist-info}/METADATA +1 -1
- {siglab_py-0.3.0.dist-info → siglab_py-0.3.2.dist-info}/RECORD +9 -9
- {siglab_py-0.3.0.dist-info → siglab_py-0.3.2.dist-info}/WHEEL +0 -0
- {siglab_py-0.3.0.dist-info → siglab_py-0.3.2.dist-info}/top_level.txt +0 -0
|
@@ -502,14 +502,15 @@ async def execute_one_position(
|
|
|
502
502
|
slices : List[Order] = position.to_slices()
|
|
503
503
|
|
|
504
504
|
# Residual handling in last slice
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
505
|
+
if len(slices)>1:
|
|
506
|
+
last_slice = slices[-1]
|
|
507
|
+
last_slice_rounded_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, last_slice.amount/multiplier) # After divided by multiplier, rounded_slice_amount_in_base_ccy in number of contracts actually (Not in base ccy).
|
|
508
|
+
last_slice_rounded_amount_in_base_ccy = float(last_slice_rounded_amount_in_base_ccy) if last_slice_rounded_amount_in_base_ccy else 0
|
|
509
|
+
if last_slice_rounded_amount_in_base_ccy<=min_amount:
|
|
510
|
+
slices.pop()
|
|
511
|
+
slices[-1].amount += last_slice.amount
|
|
511
512
|
|
|
512
|
-
|
|
513
|
+
log(f"{position.ticker} Last slice residual smaller than min_amount. Amount is added to prev slice instead. last_slice_amount: {last_slice.amount/multiplier}, last_slice_rounded_amount: {last_slice_rounded_amount_in_base_ccy}")
|
|
513
514
|
|
|
514
515
|
i = 0
|
|
515
516
|
for slice in slices:
|
|
@@ -796,10 +797,10 @@ async def execute_one_position(
|
|
|
796
797
|
dispatch_notification(title=f"{param['current_filename']} {param['gateway_id']} execute_one_position done. {position.ticker} {position.side} {position.amount}", message=notification_summary, footer=param['notification']['footer'], params=notification_params, log_level=LogLevel.CRITICAL, logger=logger)
|
|
797
798
|
|
|
798
799
|
except Exception as position_execution_err:
|
|
799
|
-
err_msg = f"Execution failed: {position_execution_err} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}"
|
|
800
|
-
log(
|
|
800
|
+
err_msg = f"{position.ticker} Execution failed: {position_execution_err} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}"
|
|
801
|
+
log(err_msg)
|
|
801
802
|
|
|
802
|
-
dispatch_notification(title=f"{param['current_filename']} {param['gateway_id']}
|
|
803
|
+
dispatch_notification(title=f"{param['current_filename']} {param['gateway_id']} {position.ticker} execute_one_position failed!!!", message=err_msg, footer=param['notification']['footer'], params=notification_params, log_level=LogLevel.ERROR, logger=logger) # type: ignore
|
|
803
804
|
|
|
804
805
|
position.done = False
|
|
805
806
|
position.execution_err = err_msg
|
|
@@ -51,7 +51,7 @@ class AnalyticUtilTests(unittest.TestCase):
|
|
|
51
51
|
'sma_short_periods', 'sma_long_periods', 'ema_short_periods', 'ema_long_periods', 'ema_close',
|
|
52
52
|
'std', 'std_percent',
|
|
53
53
|
'candle_height_percent', 'candle_height_percent_rounded',
|
|
54
|
-
'log_return', '
|
|
54
|
+
'log_return', 'interval_hist_vol', 'annualized_hist_vol',
|
|
55
55
|
'chop_against_ema',
|
|
56
56
|
'ema_volume_short_periods', 'ema_volume_long_periods',
|
|
57
57
|
'max_short_periods', 'max_long_periods', 'idmax_short_periods', 'idmax_long_periods', 'min_short_periods', 'min_long_periods', 'idmin_short_periods', 'idmin_long_periods',
|
|
@@ -12,7 +12,7 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
12
12
|
tp_min_percent : float = 1.5
|
|
13
13
|
tp_max_percent : float = 2.5
|
|
14
14
|
sl_percent_trailing : float = 50 # Trailing stop loss in percent
|
|
15
|
-
|
|
15
|
+
default_effective_tp_trailing_percent : float = 50
|
|
16
16
|
|
|
17
17
|
pnl_percent_notional : float = 0.5 # Trade's current pnl in percent.
|
|
18
18
|
|
|
@@ -21,7 +21,7 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
21
21
|
tp_max_percent = tp_max_percent,
|
|
22
22
|
sl_percent_trailing = sl_percent_trailing,
|
|
23
23
|
pnl_percent_notional = pnl_percent_notional,
|
|
24
|
-
|
|
24
|
+
default_effective_tp_trailing_percent = default_effective_tp_trailing_percent
|
|
25
25
|
)
|
|
26
26
|
assert(effective_tp_trailing_percent==50) # Generous trailing SL when trading starting out and pnl small.
|
|
27
27
|
|
|
@@ -29,7 +29,7 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
29
29
|
tp_min_percent : float = 1.5
|
|
30
30
|
tp_max_percent : float = 2.5
|
|
31
31
|
sl_percent_trailing : float = 50 # Trailing stop loss in percent
|
|
32
|
-
|
|
32
|
+
default_effective_tp_trailing_percent : float = 50
|
|
33
33
|
|
|
34
34
|
pnl_percent_notional : float = 2 # Trade's current pnl in percent.
|
|
35
35
|
|
|
@@ -38,7 +38,7 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
38
38
|
tp_max_percent = tp_max_percent,
|
|
39
39
|
sl_percent_trailing = sl_percent_trailing,
|
|
40
40
|
pnl_percent_notional = pnl_percent_notional,
|
|
41
|
-
|
|
41
|
+
default_effective_tp_trailing_percent = default_effective_tp_trailing_percent
|
|
42
42
|
)
|
|
43
43
|
assert(effective_tp_trailing_percent==25) # Intermediate trailing SL
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
46
46
|
tp_min_percent : float = 1.5
|
|
47
47
|
tp_max_percent : float = 2.5
|
|
48
48
|
sl_percent_trailing : float = 50 # Trailing stop loss in percent
|
|
49
|
-
|
|
49
|
+
default_effective_tp_trailing_percent : float = 50
|
|
50
50
|
|
|
51
51
|
pnl_percent_notional : float = 2.5 # Trade's current pnl in percent.
|
|
52
52
|
|
|
@@ -55,6 +55,6 @@ class TradingUtilTests(unittest.TestCase):
|
|
|
55
55
|
tp_max_percent = tp_max_percent,
|
|
56
56
|
sl_percent_trailing = sl_percent_trailing,
|
|
57
57
|
pnl_percent_notional = pnl_percent_notional,
|
|
58
|
-
|
|
58
|
+
default_effective_tp_trailing_percent = default_effective_tp_trailing_percent
|
|
59
59
|
)
|
|
60
60
|
assert(effective_tp_trailing_percent==0) # Most tight trailing SL
|
siglab_py/util/analytic_util.py
CHANGED
|
@@ -98,10 +98,13 @@ def compute_candles_stats(
|
|
|
98
98
|
pd_candles['ema_short_periods'] = close_short_periods_ewm.mean()
|
|
99
99
|
pd_candles['ema_long_periods'] = close_long_periods_ewm.mean()
|
|
100
100
|
pd_candles['ema_close'] = pd_candles['ema_long_periods'] # Alias, shorter name
|
|
101
|
-
pd_candles['std'] =
|
|
101
|
+
pd_candles['std'] = close_long_periods_rolling.std()
|
|
102
102
|
|
|
103
103
|
pd_candles['std_percent'] = pd_candles['std'] / pd_candles['ema_close'] * 100
|
|
104
104
|
|
|
105
|
+
pd_candles['normalized_close_short'] = (pd_candles['close'] - pd_candles['sma_short_periods']) / close_short_periods_rolling.std()
|
|
106
|
+
pd_candles['normalized_close_long'] = (pd_candles['close'] - pd_candles['sma_long_periods']) / pd_candles['std']
|
|
107
|
+
|
|
105
108
|
pd_candles['candle_height_percent'] = pd_candles['candle_height'] / pd_candles['ema_close'] * 100
|
|
106
109
|
pd_candles['candle_height_percent_rounded'] = pd_candles['candle_height_percent'].round().astype('Int64')
|
|
107
110
|
|
|
@@ -118,7 +121,14 @@ def compute_candles_stats(
|
|
|
118
121
|
)
|
|
119
122
|
'''
|
|
120
123
|
pd_candles['log_return'] = np.log(pd_candles['close'] / pd_candles['close'].shift(1))
|
|
121
|
-
pd_candles['
|
|
124
|
+
pd_candles['interval_hist_vol'] = pd_candles['log_return'].rolling(window=sliding_window_how_many_candles).std()
|
|
125
|
+
|
|
126
|
+
time_gap_sec = int(pd_candles['timestamp_ms'].iloc[1] - pd_candles['timestamp_ms'].iloc[0])/1000
|
|
127
|
+
seconds_in_year = 365 * 24 * 60 * 60
|
|
128
|
+
candles_per_year = seconds_in_year / time_gap_sec
|
|
129
|
+
annualization_factor = np.sqrt(candles_per_year)
|
|
130
|
+
pd_candles['annualized_hist_vol'] = pd_candles['interval_hist_vol'] * annualization_factor
|
|
131
|
+
|
|
122
132
|
|
|
123
133
|
pd_candles['chop_against_ema'] = (
|
|
124
134
|
(~pd_candles['is_green'] & (pd_candles['close'] > pd_candles['ema_close'])) | # Case 1: Green candle and close > EMA
|
|
@@ -361,12 +371,12 @@ def compute_candles_stats(
|
|
|
361
371
|
import statsmodels.api as sm # in-compatible with pypy
|
|
362
372
|
|
|
363
373
|
# Slopes
|
|
364
|
-
X = sm.add_constant(range(len(pd_candles['
|
|
365
|
-
rolling_slope = pd_candles['
|
|
374
|
+
X = sm.add_constant(range(len(pd_candles['normalized_close_short'])))
|
|
375
|
+
rolling_slope = pd_candles['normalized_close_short'].rolling(window=int(sliding_window_how_many_candles/slow_fast_interval_ratio)).apply(lambda x: sm.OLS(x, X[:len(x)]).fit().params[1], raw=False)
|
|
366
376
|
pd_candles['close_short_slope'] = rolling_slope
|
|
367
377
|
|
|
368
|
-
X = sm.add_constant(range(len(pd_candles['
|
|
369
|
-
rolling_slope = pd_candles['
|
|
378
|
+
X = sm.add_constant(range(len(pd_candles['normalized_close_long'])))
|
|
379
|
+
rolling_slope = pd_candles['normalized_close_long'].rolling(window=sliding_window_how_many_candles).apply(lambda x: sm.OLS(x, X[:len(x)]).fit().params[1], raw=False)
|
|
370
380
|
pd_candles['close_long_slope'] = rolling_slope
|
|
371
381
|
|
|
372
382
|
X = sm.add_constant(range(len(pd_candles['ema_short_periods'])))
|
siglab_py/util/trading_util.py
CHANGED
|
@@ -11,11 +11,11 @@ Examples,
|
|
|
11
11
|
min TP = 1.5% <-- min TP
|
|
12
12
|
max TP = 2.5% <-- max TP
|
|
13
13
|
|
|
14
|
-
slope = (0-50)/(2.5-1.5) = -50
|
|
14
|
+
slope = (0-50)/(2.5-1.5) = -50/+1 = -50
|
|
15
15
|
effective_tp_trailing_percent = slope * (pnl_percent_notional - 1.5%) + sl_percent_trailing
|
|
16
16
|
|
|
17
|
-
Case 1. pnl_percent_notional =
|
|
18
|
-
effective_tp_trailing_percent = slope * (pnl_percent_notional -
|
|
17
|
+
Case 1. pnl_percent_notional = 1.5% (Trade starting off, only +50bps pnl. i.e. min TP)
|
|
18
|
+
effective_tp_trailing_percent = slope * (pnl_percent_notional - 1.5%) + sl_percent_trailing
|
|
19
19
|
= -50 * (1.5-1.5) + 50%
|
|
20
20
|
= 0 + 50
|
|
21
21
|
= 50% (Most loose)
|
|
@@ -55,12 +55,12 @@ def calc_eff_trailing_sl(
|
|
|
55
55
|
tp_max_percent : float,
|
|
56
56
|
sl_percent_trailing : float,
|
|
57
57
|
pnl_percent_notional : float,
|
|
58
|
-
|
|
58
|
+
default_effective_tp_trailing_percent : float = 50
|
|
59
59
|
) -> float:
|
|
60
60
|
slope = (0 - sl_percent_trailing) / (tp_max_percent - tp_min_percent)
|
|
61
|
-
|
|
61
|
+
effective_tp_trailing_percent = (
|
|
62
62
|
slope * (pnl_percent_notional - tp_min_percent) + sl_percent_trailing
|
|
63
63
|
if pnl_percent_notional>tp_min_percent
|
|
64
|
-
else
|
|
64
|
+
else default_effective_tp_trailing_percent
|
|
65
65
|
)
|
|
66
|
-
return
|
|
66
|
+
return effective_tp_trailing_percent
|
|
@@ -15,24 +15,24 @@ siglab_py/market_data_providers/test_provider.py,sha256=wBLCgcWjs7FGZJXWsNyn30lk
|
|
|
15
15
|
siglab_py/ordergateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
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=KQ5mFT458YQcAuxcQv_NK0iaz1LkG8ykV2-lZ61OerI,45891
|
|
19
19
|
siglab_py/ordergateway/test_ordergateway.py,sha256=4PE2flp_soGcD3DrI7zJOzZndjkb6I5XaDrFNNq4Huo,4009
|
|
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
|
|
22
22
|
siglab_py/tests/integration/market_data_util_tests.py,sha256=p-RWIJZLyj0lAdfi4QTIeAttCm_e8mEVWFKh4OWuogU,7189
|
|
23
23
|
siglab_py/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
siglab_py/tests/unit/analytic_util_tests.py,sha256=
|
|
24
|
+
siglab_py/tests/unit/analytic_util_tests.py,sha256=68FTWDrfXTAyFLte6wiRfwcFVJItU5F47_DebgF6hAc,3788
|
|
25
25
|
siglab_py/tests/unit/market_data_util_tests.py,sha256=A1y83itISmMJdn6wLpfwcr4tGola8wTf1D1xbelMvgw,2026
|
|
26
|
-
siglab_py/tests/unit/trading_util_tests.py,sha256=
|
|
26
|
+
siglab_py/tests/unit/trading_util_tests.py,sha256=tyefqOTQOoXSlemSDonqmdHp61-1nuXb0_6oeLlaNSM,2689
|
|
27
27
|
siglab_py/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
siglab_py/util/analytic_util.py,sha256=
|
|
28
|
+
siglab_py/util/analytic_util.py,sha256=gEEKB0_qDbpoK7QbDAow5jm2Uo-Ij3wad9iGs1xI-KM,45225
|
|
29
29
|
siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
|
|
30
30
|
siglab_py/util/market_data_util.py,sha256=9Uze8DE5z90H4Qm15R55ZllAi5trUkwCAW-BWYbfaW8,19420
|
|
31
31
|
siglab_py/util/notification_util.py,sha256=vySgHjpHgwFDLW0tHSi_AGh9JBbPc25IUgvWxmjAeT8,2658
|
|
32
32
|
siglab_py/util/retry_util.py,sha256=mxYuRFZRZoaQQjENcwPmxhxixtd1TFvbxIdPx4RwfRc,743
|
|
33
33
|
siglab_py/util/slack_notification_util.py,sha256=G27n-adbT3Q6oaHSMvu_Nom794rrda5PprSF-zvmzkM,1912
|
|
34
|
-
siglab_py/util/trading_util.py,sha256=
|
|
35
|
-
siglab_py-0.3.
|
|
36
|
-
siglab_py-0.3.
|
|
37
|
-
siglab_py-0.3.
|
|
38
|
-
siglab_py-0.3.
|
|
34
|
+
siglab_py/util/trading_util.py,sha256=FmqsamuPhMjZUkz4lCyuE8MHFapXn6yNl8Isy7peQEs,3047
|
|
35
|
+
siglab_py-0.3.2.dist-info/METADATA,sha256=dbBew40mI924gqRR91kUk-hcFFZynmBDyt_PKINmorI,979
|
|
36
|
+
siglab_py-0.3.2.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
|
37
|
+
siglab_py-0.3.2.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
|
|
38
|
+
siglab_py-0.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|