siglab-py 0.5.37__py3-none-any.whl → 0.5.39__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.

@@ -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#5331
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
 
@@ -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
- 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" --market US --trdmarket US --security_firm FUTUSECURITIES --security_type STOCK --compute_ta Y --pypy_compatible N
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
 
@@ -1,5 +1,5 @@
1
1
  from typing import Union
2
- from util.aws_util import AwsKmsUtil
2
+ from siglab_py.util.aws_util import AwsKmsUtil
3
3
 
4
4
  '''
5
5
  From command line, run 'aws configure' with IAM user's Access key ID and Secret access key. (Assume you have awscli installed)
@@ -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. ATR
87
- c. Boillenger bands (Yes incorrect spelling sorry)
88
- d. FVG
89
- e. Hurst Exponent
90
- f. RSI, MFI
91
- g. MACD
92
- h. Fibonacci
93
- i. Inflections points: where 'close' crosses EMA from above or below.
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
- # RSI - https://www.youtube.com/watch?v=G9oUTi-PI18&t=809s
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(sliding_window_how_many_candles)
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!
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siglab_py
3
- Version: 0.5.37
3
+ Version: 0.5.39
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -2,38 +2,38 @@ siglab_py/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  siglab_py/constants.py,sha256=atSjvM0wv_f1wrzWHE0DcbUn6fqLmg-51BpfMJR1QB4,547
3
3
  siglab_py/exchanges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  siglab_py/exchanges/any_exchange.py,sha256=Y-zue75ZSmu9Ga1fONbjBGLNH5pDHQI01hCSjuLBkAk,889
5
- siglab_py/exchanges/futubull.py,sha256=f-_trzvuH5NaiG-PzyiNqZ12w7J-ARi_-xcr78rFU4E,20510
5
+ siglab_py/exchanges/futubull.py,sha256=i0jNgl9BGJvgEIDeOkTfD52FaK77vX8axCKNYwJHwSI,21144
6
6
  siglab_py/market_data_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  siglab_py/market_data_providers/aggregated_orderbook_provider.py,sha256=FZRobEBNRzcNGlOG3u38OVhmOZYlkNm8dVvR-S7Ii2g,23342
8
8
  siglab_py/market_data_providers/candles_provider.py,sha256=fqHJjlECsBiBlpgyywrc4gTgxiROPNzZM8KxQBB5cOg,14139
9
9
  siglab_py/market_data_providers/candles_ta_provider.py,sha256=uiAhbEZZdTF-YulBHpSLwabos5LHCKU91NTiTmpUc0w,12001
10
10
  siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py,sha256=DHj51QTbkCmEd9RFNVhWWpsSPz1aLd6zTLqkUUbEkK0,11158
11
11
  siglab_py/market_data_providers/deribit_options_expiry_provider.py,sha256=e9Ee8TmC8pXaid8-jouSLKIpuW6_JBBgwRTieI665yQ,8684
12
- siglab_py/market_data_providers/futu_candles_ta_to_csv.py,sha256=PZRpVL80CG6518uUg38Wb9aoWzidunyBAFFFGvrDnaI,10531
12
+ siglab_py/market_data_providers/futu_candles_ta_to_csv.py,sha256=SCWlI_mOuErpGP8Kxh5WKEoff9cqqxO19oLFLd04bTs,10964
13
13
  siglab_py/market_data_providers/orderbooks_provider.py,sha256=olt-3LIkoyzQWfNNQRhJtKibLbkTutt_q_rCCTM7i1g,16216
14
14
  siglab_py/market_data_providers/test_provider.py,sha256=wBLCgcWjs7FGZJXWsNyn30lkOLa_cgpuvqRakMC0wbA,2221
15
15
  siglab_py/market_data_providers/tg_monitor.py,sha256=lRqONP0JAP223gyX37R-wCkhyGFKTvKkgmeDNoO3ay4,21813
16
16
  siglab_py/ordergateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  siglab_py/ordergateway/client.py,sha256=LvtrYirrdFOcKgTkvuqwdEN7r3nurjX320ESnk7tHE0,15095
18
- siglab_py/ordergateway/encrypt_keys_util.py,sha256=-qi87db8To8Yf1WS1Q_Cp2Ya7ZqgWlRqSHfNXCM7wE4,1339
18
+ siglab_py/ordergateway/encrypt_keys_util.py,sha256=U_M-jPrPYOTO_sU0bMVkO5ruNXge5vek8yUGa8jaE-g,1349
19
19
  siglab_py/ordergateway/gateway.py,sha256=Z-BQ-Z9gXoNrKQHzRIy9R1mnCybf9QwWhHpqkSI7bBM,43901
20
20
  siglab_py/ordergateway/test_ordergateway.py,sha256=4PE2flp_soGcD3DrI7zJOzZndjkb6I5XaDrFNNq4Huo,4009
21
21
  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=9tAg7cLDEJ3ueYIQxMYMtPKtZQValKAbRfFgVY_L8dQ,4413
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=lOQoObczb6Z4AZGevKOwuSTdNVKA2QhwAKa36LrMf7w,56524
29
+ siglab_py/util/analytic_util.py,sha256=HrTpzryXaAayn9-GOIU7RlqOIWUyBfwxPHkmG5SBSc8,60297
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
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.dist-info/METADATA,sha256=jaOC_0EKsjoghv2MUr397p7alkxIUGx5WS1OHxM7_IE,829
37
- siglab_py-0.5.37.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
38
- siglab_py-0.5.37.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
39
- siglab_py-0.5.37.dist-info/RECORD,,
36
+ siglab_py-0.5.39.dist-info/METADATA,sha256=j6k_tYOiITDiyz6qLVJZzHqUDZ5wGs-mabQHeKNVgL4,829
37
+ siglab_py-0.5.39.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
38
+ siglab_py-0.5.39.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
39
+ siglab_py-0.5.39.dist-info/RECORD,,