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.

Files changed (44) hide show
  1. {siglab_py-0.5.26 → siglab_py-0.5.28}/PKG-INFO +1 -1
  2. {siglab_py-0.5.26 → siglab_py-0.5.28}/pyproject.toml +1 -1
  3. {siglab_py-0.5.26 → siglab_py-0.5.28}/setup.cfg +1 -1
  4. siglab_py-0.5.28/siglab_py/constants.py +23 -0
  5. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/analytic_util_tests.py +2 -2
  6. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/analytic_util.py +56 -31
  7. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/PKG-INFO +1 -1
  8. siglab_py-0.5.26/siglab_py/constants.py +0 -12
  9. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/__init__.py +0 -0
  10. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/__init__.py +0 -0
  11. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/any_exchange.py +0 -0
  12. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/exchanges/futubull.py +0 -0
  13. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/__init__.py +0 -0
  14. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
  15. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/candles_provider.py +0 -0
  16. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
  17. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +0 -0
  18. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
  19. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py +0 -0
  20. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
  21. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/test_provider.py +0 -0
  22. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/market_data_providers/tg_monitor.py +0 -0
  23. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/__init__.py +0 -0
  24. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/client.py +0 -0
  25. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
  26. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/gateway.py +0 -0
  27. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/ordergateway/test_ordergateway.py +0 -0
  28. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/__init__.py +0 -0
  29. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/integration/__init__.py +0 -0
  30. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
  31. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/__init__.py +0 -0
  32. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/market_data_util_tests.py +0 -0
  33. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/tests/unit/trading_util_tests.py +0 -0
  34. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/__init__.py +0 -0
  35. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/aws_util.py +0 -0
  36. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/market_data_util.py +0 -0
  37. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/notification_util.py +0 -0
  38. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/retry_util.py +0 -0
  39. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/slack_notification_util.py +0 -0
  40. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py/util/trading_util.py +0 -0
  41. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/SOURCES.txt +0 -0
  42. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/dependency_links.txt +0 -0
  43. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/requires.txt +0 -0
  44. {siglab_py-0.5.26 → siglab_py-0.5.28}/siglab_py.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siglab_py
3
- Version: 0.5.26
3
+ Version: 0.5.28
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "siglab_py"
7
- version = "0.5.26"
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"}
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = siglab_py
3
- version = 0.5.26
3
+ version = 0.5.28
4
4
  description = Market data fetches, TA calculations and generic order gateway.
5
5
  author = r0bbarh00d
6
6
  author_email = r0bbarh00d@gmail.com
@@ -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
- 'higher_highs_long_periods', 'lower_lows_long_periods', 'higher_highs_short_periods', 'lower_lows_short_periods',
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', 'rsi_higher_highs', 'rsi_lower_lows', 'rsi_divergence',
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 higherhighs(series: pd.Series) -> Union[str, None]:
55
- if pd.isna(series.iloc[0]):
56
- return None
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 None
60
- first, last = unique_maxima.iloc[0], unique_maxima.iloc[-1]
60
+ return TrendDirection.UNDEFINED.value
61
+ first, last = unique_maxima[0], unique_maxima[-1]
61
62
  if first > last:
62
- return 'lower_highs'
63
+ return TrendDirection.LOWER_HIGHS.value
63
64
  elif first < last:
64
- return 'higher_highs'
65
+ return TrendDirection.HIGHER_HIGHS.value
65
66
  else:
66
- return 'sideways'
67
+ return TrendDirection.SIDEWAYS.value
67
68
 
68
- def lowerlows(series: pd.Series) -> Union[str, None]:
69
- if pd.isna(series.iloc[0]):
70
- return None
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 None
74
- first, last = unique_minima.iloc[0], unique_minima.iloc[-1]
73
+ return TrendDirection.UNDEFINED.value
74
+ first, last = unique_minima[0], unique_minima[-1]
75
75
  if first > last:
76
- return 'lower_lows'
76
+ return TrendDirection.LOWER_LOWS.value
77
77
  elif first < last:
78
- return 'higher_lows'
78
+ return TrendDirection.HIGHER_LOWS.value
79
79
  else:
80
- return 'sideways'
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['higher_highs_long_periods'] = higherhighs(pd_candles['max_long_periods'])
212
- pd_candles['lower_lows_long_periods'] = lowerlows(pd_candles['min_long_periods'])
213
- pd_candles['higher_highs_short_periods'] = higherhighs(pd_candles['max_short_periods'])
214
- pd_candles['lower_lows_short_periods'] = lowerlows(pd_candles['min_short_periods'])
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['rsi_higher_highs'] = higherhighs(pd_candles['rsi_max'])
433
- pd_candles['rsi_lower_lows'] = lowerlows(pd_candles['rsi_min'])
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
- # Bullish Divergence: Price lower low + RSI higher low
437
- if row['lower_lows_long_periods']=='lower_lows' and row['rsi_lower_lows']=='higher_lows':
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
- # Bearish Divergence: Price higher high + RSI lower high
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
- else:
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siglab_py
3
- Version: 0.5.26
3
+ Version: 0.5.28
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -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