siglab-py 0.5.36__py3-none-any.whl → 0.5.38__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/tests/unit/analytic_util_tests.py +28 -3
- siglab_py/util/analytic_util.py +78 -10
- siglab_py/util/notification_util.py +1 -1
- {siglab_py-0.5.36.dist-info → siglab_py-0.5.38.dist-info}/METADATA +1 -1
- {siglab_py-0.5.36.dist-info → siglab_py-0.5.38.dist-info}/RECORD +7 -7
- {siglab_py-0.5.36.dist-info → siglab_py-0.5.38.dist-info}/WHEEL +0 -0
- {siglab_py-0.5.36.dist-info → siglab_py-0.5.38.dist-info}/top_level.txt +0 -0
|
@@ -2,7 +2,7 @@ import unittest
|
|
|
2
2
|
from typing import List
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from util.analytic_util import compute_candles_stats
|
|
5
|
+
from util.analytic_util import compute_candles_stats, lookup_fib_target
|
|
6
6
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
|
|
@@ -38,7 +38,7 @@ class AnalyticUtilTests(unittest.TestCase):
|
|
|
38
38
|
pd_candles=pd_candles,
|
|
39
39
|
boillenger_std_multiples=2,
|
|
40
40
|
sliding_window_how_many_candles=20,
|
|
41
|
-
pypy_compat=True
|
|
41
|
+
pypy_compat=True # Slopes calculation? Set pypy_compat to False
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
expected_columns : List[str] = [
|
|
@@ -78,4 +78,29 @@ class AnalyticUtilTests(unittest.TestCase):
|
|
|
78
78
|
missing_columns = [ expected for expected in expected_columns if expected not in pd_candles.columns.to_list() ]
|
|
79
79
|
unexpected_columns = [ actual for actual in pd_candles.columns.to_list() if actual not in expected_columns ]
|
|
80
80
|
|
|
81
|
-
assert(pd_candles.columns.to_list()==expected_columns)
|
|
81
|
+
assert(pd_candles.columns.to_list()==expected_columns)
|
|
82
|
+
|
|
83
|
+
def test_lookup_fib_target(self):
|
|
84
|
+
data_dir = Path(__file__).parent.parent.parent.parent / "data"
|
|
85
|
+
csv_path = data_dir / "sample_btc_candles.csv"
|
|
86
|
+
pd_candles : pd.DataFrame = pd.read_csv(csv_path)
|
|
87
|
+
target_fib_level : float = 0.618
|
|
88
|
+
compute_candles_stats(
|
|
89
|
+
pd_candles=pd_candles,
|
|
90
|
+
boillenger_std_multiples=2,
|
|
91
|
+
sliding_window_how_many_candles=20,
|
|
92
|
+
target_fib_level=target_fib_level,
|
|
93
|
+
pypy_compat=True # Slopes calculation? Set pypy_compat to False
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
last_row = pd_candles.iloc[-1]
|
|
97
|
+
result = lookup_fib_target(
|
|
98
|
+
row=last_row,
|
|
99
|
+
pd_candles=pd_candles,
|
|
100
|
+
target_fib_level=target_fib_level
|
|
101
|
+
)
|
|
102
|
+
if result:
|
|
103
|
+
assert(result['short_periods']['min']<result['short_periods']['fib_target']<result['short_periods']['max'])
|
|
104
|
+
assert(result['long_periods']['min']<result['long_periods']['fib_target']<result['long_periods']['max'])
|
|
105
|
+
|
|
106
|
+
|
siglab_py/util/analytic_util.py
CHANGED
|
@@ -83,14 +83,15 @@ def trend_from_lows(series: np.ndarray) -> float:
|
|
|
83
83
|
'''
|
|
84
84
|
compute_candles_stats will calculate typical/basic technical indicators using in many trading strategies:
|
|
85
85
|
a. Basic SMA/EMAs (And slopes)
|
|
86
|
-
b.
|
|
87
|
-
c.
|
|
88
|
-
d.
|
|
89
|
-
e.
|
|
90
|
-
f.
|
|
91
|
-
g.
|
|
92
|
-
h.
|
|
93
|
-
i.
|
|
86
|
+
b. EMA crosses
|
|
87
|
+
c. ATR
|
|
88
|
+
d. Boillenger bands (Yes incorrect spelling sorry)
|
|
89
|
+
e. FVG
|
|
90
|
+
f. Hurst Exponent
|
|
91
|
+
g. RSI, MFI
|
|
92
|
+
h. MACD
|
|
93
|
+
i. Fibonacci
|
|
94
|
+
j. Inflections points: where 'close' crosses EMA from above or below.
|
|
94
95
|
|
|
95
96
|
Parameters:
|
|
96
97
|
a. boillenger_std_multiples: For boillenger upper and lower calc
|
|
@@ -167,6 +168,8 @@ def compute_candles_stats(
|
|
|
167
168
|
pd_candles['annualized_volatility'] = (
|
|
168
169
|
pd_candles['interval_historical_volatility'] * annualization_factor
|
|
169
170
|
)
|
|
171
|
+
|
|
172
|
+
Why log return? Trading Dude https://python.plainenglish.io/stop-using-percentage-returns-logarithmic-returns-explained-with-code-64a4634b883a
|
|
170
173
|
'''
|
|
171
174
|
pd_candles['log_return'] = np.log(pd_candles['close'] / pd_candles['close'].shift(1))
|
|
172
175
|
pd_candles['interval_hist_vol'] = pd_candles['log_return'].rolling(window=sliding_window_how_many_candles).std()
|
|
@@ -431,8 +434,11 @@ def compute_candles_stats(
|
|
|
431
434
|
|
|
432
435
|
pd_candles['fvg_mitigated'] = pd_candles.apply(lambda row: compute_fvg_mitigated(row, pd_candles), axis=1)
|
|
433
436
|
|
|
434
|
-
|
|
435
|
-
|
|
437
|
+
'''
|
|
438
|
+
RSI
|
|
439
|
+
Divergences from Bybit Learn https://www.youtube.com/watch?v=G9oUTi-PI18&t=809s
|
|
440
|
+
RSI Reversals from BK Traders https://www.youtube.com/watch?v=MvkbrHjiQlI
|
|
441
|
+
'''
|
|
436
442
|
pd_candles.loc[:,'close_delta'] = pd_candles['close'].diff()
|
|
437
443
|
pd_candles.loc[:,'close_delta_percent'] = pd_candles['close'].pct_change()
|
|
438
444
|
lo_up = pd_candles['close_delta'].clip(lower=0)
|
|
@@ -606,6 +612,68 @@ def compute_candles_stats(
|
|
|
606
612
|
'close_vs_ema_inflection'
|
|
607
613
|
] = np.sign(pd_candles['close'] - pd_candles['ema_long_periods'])
|
|
608
614
|
|
|
615
|
+
def lookup_fib_target(
|
|
616
|
+
row,
|
|
617
|
+
pd_candles,
|
|
618
|
+
target_fib_level : float = 0.618
|
|
619
|
+
) -> Union[Dict, None]:
|
|
620
|
+
if row is None:
|
|
621
|
+
return None
|
|
622
|
+
|
|
623
|
+
fib_target_short_periods = None
|
|
624
|
+
fib_target_long_periods = None
|
|
625
|
+
|
|
626
|
+
max_short_periods = row['max_short_periods']
|
|
627
|
+
idmax_short_periods = int(row['idmax_short_periods']) if not math.isnan(row['idmax_short_periods']) else None
|
|
628
|
+
max_long_periods = row['max_long_periods']
|
|
629
|
+
idmax_long_periods = int(row['idmax_long_periods']) if not math.isnan(row['idmax_long_periods']) else None
|
|
630
|
+
|
|
631
|
+
min_short_periods = row['min_short_periods']
|
|
632
|
+
idmin_short_periods = int(row['idmin_short_periods']) if not math.isnan(row['idmin_short_periods']) else None
|
|
633
|
+
min_long_periods = row['min_long_periods']
|
|
634
|
+
idmin_long_periods = int(row['idmin_long_periods']) if not math.isnan(row['idmin_long_periods']) else None
|
|
635
|
+
|
|
636
|
+
if idmax_short_periods and idmin_short_periods and idmax_short_periods>0 and idmin_short_periods>0:
|
|
637
|
+
if idmax_short_periods>idmin_short_periods and idmax_short_periods < len(pd_candles):
|
|
638
|
+
# Falling from prev peak
|
|
639
|
+
last_peak = pd_candles.iloc[idmax_short_periods]
|
|
640
|
+
fib_target_short_periods = last_peak[f'fib_{target_fib_level}_short_periods'] if not math.isnan(last_peak[f'fib_{target_fib_level}_short_periods']) else None
|
|
641
|
+
|
|
642
|
+
else:
|
|
643
|
+
# Bouncing from prev bottom
|
|
644
|
+
if idmin_short_periods < len(pd_candles):
|
|
645
|
+
last_bottom = pd_candles.iloc[idmin_short_periods]
|
|
646
|
+
fib_target_short_periods = last_bottom[f'fib_{target_fib_level}_short_periods'] if not math.isnan(last_bottom[f'fib_{target_fib_level}_short_periods']) else None
|
|
647
|
+
|
|
648
|
+
if idmax_long_periods and idmin_long_periods and idmax_long_periods>0 and idmin_long_periods>0:
|
|
649
|
+
if idmax_long_periods>idmin_long_periods and idmax_long_periods < len(pd_candles):
|
|
650
|
+
# Falling from prev peak
|
|
651
|
+
last_peak = pd_candles.iloc[idmax_long_periods]
|
|
652
|
+
fib_target_long_periods = last_peak[f'fib_{target_fib_level}_long_periods'] if not math.isnan(last_peak[f'fib_{target_fib_level}_long_periods']) else None
|
|
653
|
+
|
|
654
|
+
else:
|
|
655
|
+
# Bouncing from prev bottom
|
|
656
|
+
if idmin_long_periods < len(pd_candles):
|
|
657
|
+
last_bottom = pd_candles.iloc[idmin_long_periods]
|
|
658
|
+
fib_target_long_periods = last_bottom[f'fib_{target_fib_level}_long_periods'] if not math.isnan(last_bottom[f'fib_{target_fib_level}_long_periods']) else None
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
'short_periods' : {
|
|
662
|
+
'idmin' : idmin_short_periods,
|
|
663
|
+
'idmax' : idmax_short_periods,
|
|
664
|
+
'min' : min_short_periods,
|
|
665
|
+
'max' : max_short_periods,
|
|
666
|
+
'fib_target' : fib_target_short_periods,
|
|
667
|
+
},
|
|
668
|
+
'long_periods' : {
|
|
669
|
+
'idmin' : idmin_long_periods,
|
|
670
|
+
'idmax' : idmax_long_periods,
|
|
671
|
+
'min' : min_long_periods,
|
|
672
|
+
'max' : max_long_periods,
|
|
673
|
+
'fib_target' : fib_target_long_periods
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
609
677
|
'''
|
|
610
678
|
The implementation from Geeksforgeeks https://www.geeksforgeeks.org/find-indices-of-all-local-maxima-and-local-minima-in-an-array/ is wrong.
|
|
611
679
|
If you have consecutive-duplicates, things will gall apart!
|
|
@@ -5,7 +5,7 @@ import pandas as pd
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
from tabulate import tabulate
|
|
7
7
|
|
|
8
|
-
from util.slack_notification_util import slack_dispatch_notification
|
|
8
|
+
from siglab_py.util.slack_notification_util import slack_dispatch_notification
|
|
9
9
|
|
|
10
10
|
from siglab_py.constants import LogLevel
|
|
11
11
|
|
|
@@ -22,18 +22,18 @@ siglab_py/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
22
22
|
siglab_py/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
23
|
siglab_py/tests/integration/market_data_util_tests.py,sha256=p-RWIJZLyj0lAdfi4QTIeAttCm_e8mEVWFKh4OWuogU,7189
|
|
24
24
|
siglab_py/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
-
siglab_py/tests/unit/analytic_util_tests.py,sha256=
|
|
25
|
+
siglab_py/tests/unit/analytic_util_tests.py,sha256=Tffqy6ngMDj71NkwWvswRln8uhLUJ-RckhbTMN-jVX4,5474
|
|
26
26
|
siglab_py/tests/unit/market_data_util_tests.py,sha256=A1y83itISmMJdn6wLpfwcr4tGola8wTf1D1xbelMvgw,2026
|
|
27
27
|
siglab_py/tests/unit/trading_util_tests.py,sha256=9DZmTZlW55lPtNfTCukgDdiyBiMYv9R4mEFWJIJiTNg,3870
|
|
28
28
|
siglab_py/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
-
siglab_py/util/analytic_util.py,sha256=
|
|
29
|
+
siglab_py/util/analytic_util.py,sha256=vZ8G8Kpb2dYoeV5ruIaoVz4lUcVFAILsQVF-zuFb9yk,60287
|
|
30
30
|
siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
|
|
31
31
|
siglab_py/util/market_data_util.py,sha256=mUXg4uaiX3b6_klgJWIEgnUQU4IUd6CwTOqKLiQWRlU,31307
|
|
32
|
-
siglab_py/util/notification_util.py,sha256=
|
|
32
|
+
siglab_py/util/notification_util.py,sha256=tNZMUkkjz4q1CKqcQq62oEmZgHgNIwz2Iw9J22V22Zw,2668
|
|
33
33
|
siglab_py/util/retry_util.py,sha256=g-UU6pkPouWZZRZEqP99R2Z0lX5xzckYkzjwqqSDpVQ,922
|
|
34
34
|
siglab_py/util/slack_notification_util.py,sha256=G27n-adbT3Q6oaHSMvu_Nom794rrda5PprSF-zvmzkM,1912
|
|
35
35
|
siglab_py/util/trading_util.py,sha256=-TGNgJdy4HMDPgq31KQn_lRawFxuXnFU5NnLRb1XM5o,5757
|
|
36
|
-
siglab_py-0.5.
|
|
37
|
-
siglab_py-0.5.
|
|
38
|
-
siglab_py-0.5.
|
|
39
|
-
siglab_py-0.5.
|
|
36
|
+
siglab_py-0.5.38.dist-info/METADATA,sha256=GfE11A07HWATi24pVPfOLFZxxMPd1aKae-97cmeuOqo,829
|
|
37
|
+
siglab_py-0.5.38.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
|
38
|
+
siglab_py-0.5.38.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
|
|
39
|
+
siglab_py-0.5.38.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|