siglab-py 0.5.37__tar.gz → 0.5.39__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.37 → siglab_py-0.5.39}/PKG-INFO +1 -1
- {siglab_py-0.5.37 → siglab_py-0.5.39}/pyproject.toml +1 -1
- {siglab_py-0.5.37 → siglab_py-0.5.39}/setup.cfg +1 -1
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/exchanges/futubull.py +11 -2
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py +6 -4
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/ordergateway/encrypt_keys_util.py +1 -1
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/unit/analytic_util_tests.py +27 -2
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/analytic_util.py +77 -11
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py.egg-info/PKG-INFO +1 -1
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/constants.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/exchanges/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/exchanges/any_exchange.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/candles_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/test_provider.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/tg_monitor.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/ordergateway/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/ordergateway/client.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/ordergateway/gateway.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/ordergateway/test_ordergateway.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/integration/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/unit/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/unit/market_data_util_tests.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/tests/unit/trading_util_tests.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/__init__.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/aws_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/market_data_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/notification_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/retry_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/slack_notification_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/util/trading_util.py +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py.egg-info/SOURCES.txt +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py.egg-info/dependency_links.txt +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py.egg-info/requires.txt +0 -0
- {siglab_py-0.5.37 → siglab_py-0.5.39}/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.39"
|
|
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"}
|
|
@@ -4,7 +4,7 @@ https://www.futuhk.com/en/support/categories/909?global_content=%7B%22promote_id
|
|
|
4
4
|
|
|
5
5
|
Fees: https://www.futuhk.com/en/commissionnew#crypto
|
|
6
6
|
|
|
7
|
-
Subscribe L2 data: https://openapi.futunn.com/futu-api-doc/en/intro/authority.html
|
|
7
|
+
Subscribe L2 data: https://openapi.futunn.com/futu-api-doc/en/intro/authority.html
|
|
8
8
|
|
|
9
9
|
Investor Protection: https://www.futuhk.com/en
|
|
10
10
|
|
|
@@ -15,8 +15,17 @@ Margin Trading:
|
|
|
15
15
|
Download Futu OpenD
|
|
16
16
|
https://www.futuhk.com/en/support/topic1_464?global_content=%7B%22promote_id%22%3A13765%2C%22sub_promote_id%22%3A10%7D
|
|
17
17
|
|
|
18
|
-
If you run the installer version "Futu_OpenD-GUI_9.0.5008_Windows.exe", it'd be installed under:
|
|
18
|
+
If you run the installer version "Futu_OpenD-GUI_9.0.5008_Windows.exe", it'd be installed under C-Drive:
|
|
19
19
|
C:\\Users\\xxx\\AppData\\Roaming\\Futu_OpenD\\Futu_OpenD.exe
|
|
20
|
+
Unfortunately, log folder also under C-drive as a result, and they are big.
|
|
21
|
+
|
|
22
|
+
For command line version: https://openapi.futunn.com/futu-api-doc/opend/opend-cmd.html
|
|
23
|
+
Binary under downloaded package (You can put it under for example D-drive):
|
|
24
|
+
...\Futu_OpenD_9.4.5408_Windows\Futu_OpenD_9.4.5408_Windows
|
|
25
|
+
|
|
26
|
+
Put a batch file "start_futu_opend.bat", if login_pwd include special characters, enclose pwd with double quotes:
|
|
27
|
+
FutuOpenD -login_account=1234567 -login_pwd="... Your Secret here ..."
|
|
28
|
+
Config file is "FutuOpenD.xml", you can adjust logging verbosity here.
|
|
20
29
|
|
|
21
30
|
Architecture: https://openapi.futunn.com/futu-api-doc/en/intro/intro.html
|
|
22
31
|
|
{siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py
RENAMED
|
@@ -17,15 +17,17 @@ from siglab_py.util.analytic_util import compute_candles_stats
|
|
|
17
17
|
'''
|
|
18
18
|
Usage:
|
|
19
19
|
set PYTHONPATH=%PYTHONPATH%;D:\dev\siglab\siglab_py
|
|
20
|
-
python futu_candles_ta_to_csv.py --symbol HK.00700 --end_date "2025-03-11 0:0:0" --start_date "2021-03-11 0:0:0" --market HK --trdmarket HK --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible N
|
|
20
|
+
python futu_candles_ta_to_csv.py --symbol HK.00700 --end_date "2025-03-11 0:0:0" --start_date "2021-03-11 0:0:0" --candle_size 1h --market HK --trdmarket HK --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible N
|
|
21
|
+
python futu_candles_ta_to_csv.py --symbol HK.02840 --end_date "2025-07-30 0:0:0" --start_date "2018-01-01 0:0:0" --candle_size 1h --market HK --trdmarket HK --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible N
|
|
22
|
+
python futu_candles_ta_to_csv.py --symbol AAPL --end_date "2025-03-11 0:0:0" --start_date "2024-03-11 0:0:0" --candle_size 1h --market US --trdmarket US --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible N
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Gold contracts? Note, symbol is case sensitive with Futu.
|
|
24
|
+
Gold contracts? Note, symbol is case sensitive with Futu. So, "GCmain" is correct, "GCMain" is in-correct.
|
|
25
25
|
python futu_candles_ta_to_csv.py --symbol US.GCmain --end_date "2025-03-11 0:0:0" --start_date "2021-03-11 0:0:0" --market US --trdmarket FUTURES --security_firm FUTUSECURITIES --security_type FUTURE --compute_ta Y --pypy_compatible N
|
|
26
26
|
|
|
27
27
|
(Remember: python -mpip install siglab_py)
|
|
28
28
|
|
|
29
|
+
Gold future contract specification: https://www.futunn.com/en/futures/GCMAIN-US/contract-specs
|
|
30
|
+
|
|
29
31
|
This script is pypy compatible. Set "pypy_compatible" to True, in which case "compute_candles_stats" will skip calculation for TAs which requires: scipy, statsmodels, scikit-learn, sklearn.preprocessing
|
|
30
32
|
pypy futu_candles_ta_to_csv.py --symbol HK.00700 --end_date "2025-03-11 0:0:0" --start_date "2024-03-11 0:0:0" --market HK --trdmarket HK --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible Y
|
|
31
33
|
|
|
@@ -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
|
|
|
@@ -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
|
+
|
|
@@ -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
|
|
@@ -433,8 +434,11 @@ def compute_candles_stats(
|
|
|
433
434
|
|
|
434
435
|
pd_candles['fvg_mitigated'] = pd_candles.apply(lambda row: compute_fvg_mitigated(row, pd_candles), axis=1)
|
|
435
436
|
|
|
436
|
-
|
|
437
|
-
|
|
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
|
+
'''
|
|
438
442
|
pd_candles.loc[:,'close_delta'] = pd_candles['close'].diff()
|
|
439
443
|
pd_candles.loc[:,'close_delta_percent'] = pd_candles['close'].pct_change()
|
|
440
444
|
lo_up = pd_candles['close_delta'].clip(lower=0)
|
|
@@ -576,7 +580,7 @@ def compute_candles_stats(
|
|
|
576
580
|
pd_data=pd_candles,
|
|
577
581
|
src_col_name='ema_rsi',
|
|
578
582
|
slope_col_name='ema_rsi_slope',
|
|
579
|
-
sliding_window_how_many_candles=int(
|
|
583
|
+
sliding_window_how_many_candles=int(rsi_trend_sliding_window_how_many_candles)
|
|
580
584
|
)
|
|
581
585
|
|
|
582
586
|
pd_candles['regular_divergence'] = (
|
|
@@ -608,6 +612,68 @@ def compute_candles_stats(
|
|
|
608
612
|
'close_vs_ema_inflection'
|
|
609
613
|
] = np.sign(pd_candles['close'] - pd_candles['ema_long_periods'])
|
|
610
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
|
+
|
|
611
677
|
'''
|
|
612
678
|
The implementation from Geeksforgeeks https://www.geeksforgeeks.org/find-indices-of-all-local-maxima-and-local-minima-in-an-array/ is wrong.
|
|
613
679
|
If you have consecutive-duplicates, things will gall apart!
|
|
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.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/candles_ta_provider.py
RENAMED
|
File without changes
|
{siglab_py-0.5.37 → siglab_py-0.5.39}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py
RENAMED
|
File without changes
|
|
File without changes
|
{siglab_py-0.5.37 → siglab_py-0.5.39}/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
|