siglab-py 0.1.30__py3-none-any.whl → 0.6.33__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.
- siglab_py/algo/__init__.py +0 -0
- siglab_py/algo/macdrsi_crosses_15m_tc_strategy.py +107 -0
- siglab_py/algo/strategy_base.py +122 -0
- siglab_py/algo/strategy_executor.py +1308 -0
- siglab_py/algo/tp_algo.py +529 -0
- siglab_py/backtests/__init__.py +0 -0
- siglab_py/backtests/backtest_core.py +2405 -0
- siglab_py/backtests/coinflip_15m_crypto.py +432 -0
- siglab_py/backtests/fibonacci_d_mv_crypto.py +541 -0
- siglab_py/backtests/macdrsi_crosses_15m_tc_crypto.py +473 -0
- siglab_py/constants.py +26 -1
- siglab_py/exchanges/binance.py +38 -0
- siglab_py/exchanges/deribit.py +83 -0
- siglab_py/exchanges/futubull.py +12 -2
- siglab_py/market_data_providers/candles_provider.py +11 -10
- siglab_py/market_data_providers/candles_ta_provider.py +5 -5
- siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +4 -4
- siglab_py/market_data_providers/futu_candles_ta_to_csv.py +7 -2
- siglab_py/market_data_providers/google_monitor.py +320 -0
- siglab_py/market_data_providers/orderbooks_provider.py +15 -12
- siglab_py/market_data_providers/tg_monitor.py +428 -0
- siglab_py/market_data_providers/{test_provider.py → trigger_provider.py} +9 -8
- siglab_py/ordergateway/client.py +172 -41
- siglab_py/ordergateway/encrypt_keys_util.py +1 -1
- siglab_py/ordergateway/gateway.py +456 -347
- siglab_py/ordergateway/test_ordergateway.py +8 -7
- siglab_py/tests/integration/market_data_util_tests.py +75 -2
- siglab_py/tests/unit/analytic_util_tests.py +47 -12
- siglab_py/tests/unit/market_data_util_tests.py +45 -1
- siglab_py/tests/unit/simple_math_tests.py +252 -0
- siglab_py/tests/unit/trading_util_tests.py +65 -0
- siglab_py/util/analytic_util.py +476 -67
- siglab_py/util/datetime_util.py +39 -0
- siglab_py/util/market_data_util.py +528 -98
- siglab_py/util/module_util.py +40 -0
- siglab_py/util/notification_util.py +78 -0
- siglab_py/util/retry_util.py +16 -3
- siglab_py/util/simple_math.py +262 -0
- siglab_py/util/slack_notification_util.py +59 -0
- siglab_py/util/trading_util.py +118 -0
- {siglab_py-0.1.30.dist-info → siglab_py-0.6.33.dist-info}/METADATA +5 -9
- siglab_py-0.6.33.dist-info/RECORD +56 -0
- {siglab_py-0.1.30.dist-info → siglab_py-0.6.33.dist-info}/WHEEL +1 -1
- siglab_py-0.1.30.dist-info/RECORD +0 -34
- {siglab_py-0.1.30.dist-info → siglab_py-0.6.33.dist-info}/top_level.txt +0 -0
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any, Union
|
|
4
|
+
|
|
5
|
+
from siglab_py.algo.strategy_base import StrategyBase
|
|
6
|
+
|
|
7
|
+
class MACDRSICrosses15mTCStrategy(StrategyBase):
|
|
8
|
+
def __init__(self, *args: object) -> None:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def order_notional_adj(
|
|
13
|
+
algo_param : Dict,
|
|
14
|
+
) -> Dict[str, float]:
|
|
15
|
+
return StrategyBase.order_notional_adj(algo_param) # type: ignore
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def allow_entry_initial(
|
|
19
|
+
lo_row_tm1,
|
|
20
|
+
hi_row_tm1,
|
|
21
|
+
last_candles
|
|
22
|
+
) -> Dict[str, bool]:
|
|
23
|
+
allow_long : bool = (
|
|
24
|
+
lo_row_tm1['macd_cross'] == 'bullish'
|
|
25
|
+
# use 'macd_cross_last' instead in combinations with 'macd_bullish_cross_last_id' if you want to make more entries
|
|
26
|
+
'''
|
|
27
|
+
and (
|
|
28
|
+
lo_row_tm1.name >= lo_row_tm1['macd_bullish_cross_last_id']
|
|
29
|
+
and
|
|
30
|
+
(lo_row_tm1.name - lo_row_tm1['macd_bullish_cross_last_id']) < 5
|
|
31
|
+
)
|
|
32
|
+
'''
|
|
33
|
+
and lo_row_tm1['rsi_trend']=="up"
|
|
34
|
+
and lo_row_tm1['close']>hi_row_tm1['ema_close']
|
|
35
|
+
)
|
|
36
|
+
allow_short : bool = (
|
|
37
|
+
lo_row_tm1['macd_cross'] == 'bearish'
|
|
38
|
+
'''
|
|
39
|
+
and (
|
|
40
|
+
lo_row_tm1.name >= lo_row_tm1['macd_bearish_cross_last_id']
|
|
41
|
+
and
|
|
42
|
+
(lo_row_tm1.name - lo_row_tm1['macd_bearish_cross_last_id']) < 5
|
|
43
|
+
)
|
|
44
|
+
'''
|
|
45
|
+
and lo_row_tm1['rsi_trend']=="down"
|
|
46
|
+
and lo_row_tm1['close']<hi_row_tm1['ema_close']
|
|
47
|
+
)
|
|
48
|
+
return {
|
|
49
|
+
'long' : allow_long,
|
|
50
|
+
'short' : allow_short
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def allow_entry_final(
|
|
55
|
+
lo_row,
|
|
56
|
+
algo_param : Dict
|
|
57
|
+
) -> Dict[str, Union[bool, float, None]]:
|
|
58
|
+
open : float = lo_row['open']
|
|
59
|
+
|
|
60
|
+
entry_price_long, entry_price_short = open, open
|
|
61
|
+
allow_long, allow_short = True, True
|
|
62
|
+
reference_price = None
|
|
63
|
+
|
|
64
|
+
pnl_potential_bps = algo_param['tp_max_percent']*100
|
|
65
|
+
|
|
66
|
+
target_price_long = entry_price_long * (1 + pnl_potential_bps/10000)
|
|
67
|
+
target_price_short = entry_price_short * (1 - pnl_potential_bps/10000)
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
'long' : allow_long,
|
|
71
|
+
'short' : allow_short,
|
|
72
|
+
|
|
73
|
+
# In additional to allow or not, allow_entry_final also calculate a few things which you may need to mark the entry trades.
|
|
74
|
+
'entry_price_long' : entry_price_long,
|
|
75
|
+
'entry_price_short' : entry_price_short,
|
|
76
|
+
'target_price_long' : target_price_long,
|
|
77
|
+
'target_price_short' : target_price_short,
|
|
78
|
+
'reference_price' : reference_price
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def trailing_stop_threshold_eval(
|
|
83
|
+
algo_param : Dict,
|
|
84
|
+
*args: Any, **kwargs: Any
|
|
85
|
+
) -> Dict[str, float]:
|
|
86
|
+
result = StrategyBase.trailing_stop_threshold_eval(algo_param)
|
|
87
|
+
tp_min_percent = result['tp_min_percent']
|
|
88
|
+
tp_max_percent = result['tp_max_percent']
|
|
89
|
+
return {
|
|
90
|
+
'tp_min_percent' : tp_min_percent,
|
|
91
|
+
'tp_max_percent' : tp_max_percent
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def tp_eval (
|
|
96
|
+
lo_row,
|
|
97
|
+
this_ticker_open_trades : List[Dict],
|
|
98
|
+
algo_param : Dict
|
|
99
|
+
) -> bool:
|
|
100
|
+
return StrategyBase.tp_eval(lo_row, this_ticker_open_trades, algo_param)
|
|
101
|
+
|
|
102
|
+
@staticmethod
|
|
103
|
+
def get_strategy_indicators() -> List[str]:
|
|
104
|
+
return [
|
|
105
|
+
'lo_row_tm1:macd_cross', 'lo_row_tm1:macd_bullish_cross_last_id', 'lo_row_tm1:macd_bearish_cross_last_id',
|
|
106
|
+
'lo_row_tm1:rsi_trend', 'lo_row_tm1:rsi', 'lo_row_tm1:rsi_max', 'lo_row_tm1:rsi_min', 'lo_row_tm1:rsi_idmax', 'lo_row_tm1:rsi_idmin'
|
|
107
|
+
]
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
from typing import List, Dict, Any, Union
|
|
4
|
+
|
|
5
|
+
from siglab_py.backtests.backtest_core import generic_tp_eval
|
|
6
|
+
|
|
7
|
+
class StrategyBase(ABC):
|
|
8
|
+
def __init__(self, *args: object) -> None:
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def reversal(
|
|
13
|
+
direction : str, # up or down
|
|
14
|
+
last_candles
|
|
15
|
+
) -> bool:
|
|
16
|
+
if direction == "down" and all([ candle[1]<candle[4] for candle in last_candles ]): # All green?
|
|
17
|
+
return True
|
|
18
|
+
elif direction == "up" and all([ candle[1]>candle[4] for candle in last_candles ]): # All red?
|
|
19
|
+
return True
|
|
20
|
+
else:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def order_notional_adj(
|
|
25
|
+
algo_param : Dict,
|
|
26
|
+
*args: Any, **kwargs: Any
|
|
27
|
+
) -> Dict[str, float]:
|
|
28
|
+
target_order_notional = algo_param['amount_base_ccy']
|
|
29
|
+
return {
|
|
30
|
+
'target_order_notional' : target_order_notional
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def allow_entry_initial(
|
|
35
|
+
*args: Any, **kwargs: Any
|
|
36
|
+
) -> Dict[str, bool]:
|
|
37
|
+
return {
|
|
38
|
+
'long' : False,
|
|
39
|
+
'short' : False
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@staticmethod
|
|
43
|
+
def allow_entry_final(
|
|
44
|
+
lo_row,
|
|
45
|
+
algo_param : Dict,
|
|
46
|
+
*args: Any, **kwargs: Any
|
|
47
|
+
) -> Dict[str, Union[bool, float, None]]:
|
|
48
|
+
open : float = lo_row['open']
|
|
49
|
+
|
|
50
|
+
entry_price_long, entry_price_short = open, open
|
|
51
|
+
allow_long, allow_short = False, False
|
|
52
|
+
reference_price = None
|
|
53
|
+
|
|
54
|
+
pnl_potential_bps = algo_param['tp_max_percent']*100
|
|
55
|
+
|
|
56
|
+
target_price_long = entry_price_long * (1 + pnl_potential_bps/10000)
|
|
57
|
+
target_price_short = entry_price_short * (1 - pnl_potential_bps/10000)
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
'long' : allow_long,
|
|
61
|
+
'short' : allow_short,
|
|
62
|
+
|
|
63
|
+
# In additional to allow or not, allow_entry_final also calculate a few things which you may need to mark the entry trades.
|
|
64
|
+
'entry_price_long' : entry_price_long,
|
|
65
|
+
'entry_price_short' : entry_price_short,
|
|
66
|
+
'target_price_long' : target_price_long,
|
|
67
|
+
'target_price_short' : target_price_short,
|
|
68
|
+
'reference_price' : reference_price
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def sl_adj(
|
|
73
|
+
algo_param : Dict,
|
|
74
|
+
*args: Any, **kwargs: Any
|
|
75
|
+
) -> Dict[str, float]:
|
|
76
|
+
running_sl_percent_hard = algo_param['sl_hard_percent']
|
|
77
|
+
return {
|
|
78
|
+
'running_sl_percent_hard' : running_sl_percent_hard
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def trailing_stop_threshold_eval(
|
|
83
|
+
algo_param : Dict,
|
|
84
|
+
*args: Any, **kwargs: Any
|
|
85
|
+
) -> Dict[str, float]:
|
|
86
|
+
tp_min_percent = algo_param['tp_min_percent']
|
|
87
|
+
tp_max_percent = algo_param['tp_max_percent']
|
|
88
|
+
return {
|
|
89
|
+
'tp_min_percent' : tp_min_percent,
|
|
90
|
+
'tp_max_percent' : tp_max_percent
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def tp_eval (
|
|
95
|
+
lo_row,
|
|
96
|
+
this_ticker_open_trades : List[Dict],
|
|
97
|
+
algo_param : Dict
|
|
98
|
+
) -> bool:
|
|
99
|
+
'''
|
|
100
|
+
Be very careful, backtest_core 'generic_pnl_eval' may use a) some indicator (tp_indicator_name), or b) target_price to evaluate 'unrealized_pnl_tp'.
|
|
101
|
+
'tp_eval' only return True or False but it needs be congruent with backtest_core 'generic_pnl_eval', otherwise incorrect rosy pnl may be reported.
|
|
102
|
+
'''
|
|
103
|
+
return generic_tp_eval(lo_row, this_ticker_open_trades)
|
|
104
|
+
|
|
105
|
+
# List of TA/indicators you wish to include in POSITION_CACHE_COLUMNS from strategy_executor (Display concern only)
|
|
106
|
+
@staticmethod
|
|
107
|
+
def get_strategy_indicators() -> List[str]:
|
|
108
|
+
return []
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def get_strategy_algo_params() -> List[Dict[str, Any]]:
|
|
112
|
+
'''
|
|
113
|
+
[
|
|
114
|
+
{
|
|
115
|
+
'key' : 'rsi_lower',
|
|
116
|
+
'val' : 30
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
'''
|
|
120
|
+
return [
|
|
121
|
+
|
|
122
|
+
]
|