siglab-py 0.5.26__tar.gz → 0.5.28__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.
- {siglab_py-0.5.26 → siglab_py-0.5.28}/PKG-INFO +1 -1
- {siglab_py-0.5.26 → siglab_py-0.5.28}/pyproject.toml +1 -1
- {siglab_py-0.5.26 → siglab_py-0.5.28}/setup.cfg +1 -1
- siglab_py-0.5.28/siglab_py/constants.py +23 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/analytic_util_tests.py +2 -2
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/analytic_util.py +56 -31
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/PKG-INFO +1 -1
- siglab_py-0.5.26/siglab_py/constants.py +0 -12
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/any_exchange.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/futubull.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/candles_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/test_provider.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/tg_monitor.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/client.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/gateway.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/test_ordergateway.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/integration/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/market_data_util_tests.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/trading_util_tests.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/__init__.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/aws_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/market_data_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/notification_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/retry_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/slack_notification_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/trading_util.py +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/SOURCES.txt +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/dependency_links.txt +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/requires.txt +0 -0
- {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "siglab_py"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.28"
|
|
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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from typing import Union, List, Dict, Any
|
|
3
|
+
|
|
4
|
+
JSON_SERIALIZABLE_TYPES = Union[str, bool, int, float, None, List[Any], Dict[Any, Any]]
|
|
5
|
+
|
|
6
|
+
class LogLevel(enum.Enum):
|
|
7
|
+
CRITICAL = 50
|
|
8
|
+
ERROR = 40
|
|
9
|
+
WARNING = 30
|
|
10
|
+
INFO = 20
|
|
11
|
+
DEBUG = 10
|
|
12
|
+
NOTSET = 0
|
|
13
|
+
|
|
14
|
+
class TrendDirection(enum.Enum):
|
|
15
|
+
UNDEFINED = 0
|
|
16
|
+
HIGHER_HIGHS = 1
|
|
17
|
+
LOWER_HIGHS = 2
|
|
18
|
+
SIDEWAYS = 3
|
|
19
|
+
HIGHER_LOWS = 4
|
|
20
|
+
LOWER_LOWS = 5
|
|
21
|
+
|
|
22
|
+
def to_string(self) -> str:
|
|
23
|
+
return self.name.lower() if self != TrendDirection.UNDEFINED else ''
|
|
@@ -57,14 +57,14 @@ class AnalyticUtilTests(unittest.TestCase):
|
|
|
57
57
|
'ema_volume_short_periods', 'ema_volume_long_periods',
|
|
58
58
|
'max_short_periods', 'max_long_periods', 'idmax_short_periods', 'idmax_long_periods', 'min_short_periods', 'min_long_periods', 'idmin_short_periods', 'idmin_long_periods',
|
|
59
59
|
'price_swing_short_periods', 'price_swing_long_periods',
|
|
60
|
-
'
|
|
60
|
+
'trend_from_highs_long_periods', 'trend_from_lows_long_periods', 'trend_from_highs_short_periods', 'trend_from_lows_short_periods',
|
|
61
61
|
'h_l', 'h_pc', 'l_pc', 'tr', 'atr', 'atr_avg_short_periods', 'atr_avg_long_periods',
|
|
62
62
|
'hurst_exp',
|
|
63
63
|
'boillenger_upper', 'boillenger_lower', 'boillenger_channel_height', 'boillenger_upper_agg', 'boillenger_lower_agg', 'boillenger_channel_height_agg',
|
|
64
64
|
'aggressive_up', 'aggressive_up_index', 'aggressive_up_candle_height', 'aggressive_up_candle_high', 'aggressive_up_candle_low', 'aggressive_down', 'aggressive_down_index', 'aggressive_down_candle_height', 'aggressive_down_candle_high', 'aggressive_down_candle_low',
|
|
65
65
|
'fvg_low', 'fvg_high', 'fvg_gap', 'fvg_mitigated',
|
|
66
66
|
'close_delta', 'close_delta_percent', 'up', 'down',
|
|
67
|
-
'rsi', 'ema_rsi', 'rsi_max', 'rsi_idmax', 'rsi_min', 'rsi_idmin', 'rsi_trend', '
|
|
67
|
+
'rsi', 'ema_rsi', 'rsi_max', 'rsi_idmax', 'rsi_min', 'rsi_idmin', 'rsi_trend', 'rsi_trend_from_highs', 'rsi_trend_from_lows', 'rsi_divergence',
|
|
68
68
|
'typical_price',
|
|
69
69
|
'money_flow', 'money_flow_positive', 'money_flow_negative', 'positive_flow_sum', 'negative_flow_sum', 'money_flow_ratio', 'mfi',
|
|
70
70
|
'macd', 'signal', 'macd_minus_signal',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import tzlocal
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from typing import List, Dict, Union, NoReturn, Any, Tuple
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
import math
|
|
6
7
|
import pandas as pd
|
|
@@ -11,6 +12,7 @@ from ccxt.base.exchange import Exchange as CcxtExchange
|
|
|
11
12
|
from ccxt import deribit
|
|
12
13
|
|
|
13
14
|
from siglab_py.util.market_data_util import fix_column_types
|
|
15
|
+
from siglab_py.constants import TrendDirection
|
|
14
16
|
|
|
15
17
|
# Fibonacci
|
|
16
18
|
MAGIC_FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.00, 1.618, 2.618, 3.618, 4.236]
|
|
@@ -51,34 +53,33 @@ def calculate_slope(
|
|
|
51
53
|
pd_data[f"normalized_{slope_col_name}_idmin"] = normalized_slope_rolling.apply(lambda x : x.idxmin())
|
|
52
54
|
pd_data[f"normalized_{slope_col_name}_idmax"] = normalized_slope_rolling.apply(lambda x : x.idxmax())
|
|
53
55
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
unique_maxima = series.dropna()[series.dropna().diff().ne(0)]
|
|
56
|
+
def trend_from_highs(series: np.ndarray) -> float:
|
|
57
|
+
valid_series = series[~np.isnan(series)]
|
|
58
|
+
unique_maxima = valid_series[np.concatenate(([True], np.diff(valid_series) != 0))]
|
|
58
59
|
if len(unique_maxima) < 2:
|
|
59
|
-
return
|
|
60
|
-
first, last = unique_maxima
|
|
60
|
+
return TrendDirection.UNDEFINED.value
|
|
61
|
+
first, last = unique_maxima[0], unique_maxima[-1]
|
|
61
62
|
if first > last:
|
|
62
|
-
return
|
|
63
|
+
return TrendDirection.LOWER_HIGHS.value
|
|
63
64
|
elif first < last:
|
|
64
|
-
return
|
|
65
|
+
return TrendDirection.HIGHER_HIGHS.value
|
|
65
66
|
else:
|
|
66
|
-
return
|
|
67
|
+
return TrendDirection.SIDEWAYS.value
|
|
67
68
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
unique_minima = series.dropna()[series.dropna().diff().ne(0)]
|
|
69
|
+
def trend_from_lows(series: np.ndarray) -> float:
|
|
70
|
+
valid_series = series[~np.isnan(series)]
|
|
71
|
+
unique_minima = valid_series[np.concatenate(([True], np.diff(valid_series) != 0))]
|
|
72
72
|
if len(unique_minima) < 2:
|
|
73
|
-
return
|
|
74
|
-
first, last = unique_minima
|
|
73
|
+
return TrendDirection.UNDEFINED.value
|
|
74
|
+
first, last = unique_minima[0], unique_minima[-1]
|
|
75
75
|
if first > last:
|
|
76
|
-
return
|
|
76
|
+
return TrendDirection.LOWER_LOWS.value
|
|
77
77
|
elif first < last:
|
|
78
|
-
return
|
|
78
|
+
return TrendDirection.HIGHER_LOWS.value
|
|
79
79
|
else:
|
|
80
|
-
return
|
|
81
|
-
|
|
80
|
+
return TrendDirection.SIDEWAYS.value
|
|
81
|
+
|
|
82
|
+
|
|
82
83
|
'''
|
|
83
84
|
compute_candles_stats will calculate typical/basic technical indicators using in many trading strategies:
|
|
84
85
|
a. Basic SMA/EMAs (And slopes)
|
|
@@ -208,10 +209,26 @@ def compute_candles_stats(
|
|
|
208
209
|
pd_candles['min_long_periods'] - pd_candles['max_long_periods'] # Down swing (negative)
|
|
209
210
|
)
|
|
210
211
|
|
|
211
|
-
pd_candles['
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
212
|
+
pd_candles['trend_from_highs_long_periods'] = np.where(
|
|
213
|
+
pd.isna(pd_candles['max_long_periods']),
|
|
214
|
+
None, # type: ignore
|
|
215
|
+
pd_candles['max_long_periods'].rolling(window=sliding_window_how_many_candles).apply(trend_from_highs, raw=True)
|
|
216
|
+
)
|
|
217
|
+
pd_candles['trend_from_lows_long_periods'] = np.where(
|
|
218
|
+
pd.isna(pd_candles['min_long_periods']),
|
|
219
|
+
None, # type: ignore
|
|
220
|
+
pd_candles['min_long_periods'].rolling(window=sliding_window_how_many_candles).apply(trend_from_lows, raw=True)
|
|
221
|
+
)
|
|
222
|
+
pd_candles['trend_from_highs_short_periods'] = np.where(
|
|
223
|
+
pd.isna(pd_candles['max_short_periods']),
|
|
224
|
+
None, # type: ignore
|
|
225
|
+
pd_candles['max_short_periods'].rolling(window=int(sliding_window_how_many_candles/slow_fast_interval_ratio)).apply(trend_from_highs, raw=True)
|
|
226
|
+
)
|
|
227
|
+
pd_candles['trend_from_lows_short_periods'] = np.where(
|
|
228
|
+
pd.isna(pd_candles['min_short_periods']),
|
|
229
|
+
None, # type: ignore
|
|
230
|
+
pd_candles['min_short_periods'].rolling(window=int(sliding_window_how_many_candles/slow_fast_interval_ratio)).apply(trend_from_lows, raw=True)
|
|
231
|
+
)
|
|
215
232
|
|
|
216
233
|
# ATR https://medium.com/codex/detecting-ranging-and-trending-markets-with-choppiness-index-in-python-1942e6450b58
|
|
217
234
|
pd_candles.loc[:,'h_l'] = pd_candles['high'] - pd_candles['low']
|
|
@@ -429,18 +446,26 @@ def compute_candles_stats(
|
|
|
429
446
|
|
|
430
447
|
pd_candles['rsi_trend'] = pd_candles.apply(lambda row: rsi_trend(row), axis=1)
|
|
431
448
|
|
|
432
|
-
pd_candles['
|
|
433
|
-
|
|
449
|
+
pd_candles['rsi_trend_from_highs'] = np.where(
|
|
450
|
+
pd.isna(pd_candles['rsi_max']),
|
|
451
|
+
None, # type: ignore
|
|
452
|
+
pd_candles['rsi_max'].rolling(window=rsi_trend_sliding_window_how_many_candles).apply(trend_from_highs, raw=True)
|
|
453
|
+
)
|
|
454
|
+
pd_candles['rsi_trend_from_lows'] = np.where(
|
|
455
|
+
pd.isna(pd_candles['rsi_min']),
|
|
456
|
+
None, # type: ignore
|
|
457
|
+
pd_candles['rsi_min'].rolling(window=rsi_trend_sliding_window_how_many_candles).apply(trend_from_lows, raw=True)
|
|
458
|
+
)
|
|
434
459
|
|
|
435
460
|
def _rsi_divergence(row):
|
|
436
|
-
|
|
437
|
-
|
|
461
|
+
trend_from_highs_long_periods = TrendDirection(row['trend_from_highs_long_periods']) if row['trend_from_highs_long_periods'] is not None and not pd.isna(row['trend_from_highs_long_periods']) else None # type: ignore
|
|
462
|
+
rsi_trend_from_highs = TrendDirection(row['rsi_trend_from_highs']) if row['rsi_trend_from_highs'] is not None and not pd.isna(row['rsi_trend_from_highs']) else None # type: ignore
|
|
463
|
+
|
|
464
|
+
if trend_from_highs_long_periods and rsi_trend_from_highs and trend_from_highs_long_periods == TrendDirection.LOWER_HIGHS and rsi_trend_from_highs == TrendDirection.HIGHER_HIGHS:
|
|
438
465
|
return 'bullish_divergence'
|
|
439
|
-
|
|
440
|
-
elif row['higher_highs_long_periods']=='higher_highs' and row['rsi_higher_highs']=='lower_highs':
|
|
466
|
+
elif trend_from_highs_long_periods and rsi_trend_from_highs and trend_from_highs_long_periods == TrendDirection.HIGHER_HIGHS and rsi_trend_from_highs == TrendDirection.LOWER_HIGHS:
|
|
441
467
|
return 'bearish_divergence'
|
|
442
|
-
|
|
443
|
-
return 'no_divergence'
|
|
468
|
+
return 'no_divergence'
|
|
444
469
|
pd_candles['rsi_divergence'] = pd_candles.apply(_rsi_divergence, axis=1)
|
|
445
470
|
|
|
446
471
|
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import enum
|
|
2
|
-
from typing import Union, List, Dict, Any
|
|
3
|
-
|
|
4
|
-
JSON_SERIALIZABLE_TYPES = Union[str, bool, int, float, None, List[Any], Dict[Any, Any]]
|
|
5
|
-
|
|
6
|
-
class LogLevel(enum.Enum):
|
|
7
|
-
CRITICAL = 50
|
|
8
|
-
ERROR = 40
|
|
9
|
-
WARNING = 30
|
|
10
|
-
INFO = 20
|
|
11
|
-
DEBUG = 10
|
|
12
|
-
NOTSET = 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/candles_ta_provider.py
RENAMED
|
File without changes
|
{siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py
RENAMED
|
File without changes
|
|
File without changes
|
{siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py
RENAMED
|
File without changes
|
{siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/orderbooks_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|