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

@@ -118,6 +118,40 @@ class MarketDataUtilTests(unittest.TestCase):
118
118
  assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
119
119
  assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
120
120
  assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
121
+
122
+ def test_aggregate_candles(self):
123
+ end_date : datetime = datetime.today()
124
+ start_date : datetime = end_date + timedelta(hours=-8)
125
+
126
+ param = {
127
+ 'apiKey' : None,
128
+ 'secret' : None,
129
+ 'password' : None,
130
+ 'subaccount' : None,
131
+ 'rateLimit' : 100, # In ms
132
+ 'options' : {
133
+ 'defaultType': 'swap' }
134
+ }
135
+
136
+ exchange : Exchange = okx(param) # type: ignore
137
+ normalized_symbols = [ 'BTC/USDT:USDT' ]
138
+ pd_candles: Union[pd.DataFrame, None] = fetch_candles(
139
+ start_ts=start_date.timestamp(),
140
+ end_ts=end_date.timestamp(),
141
+ exchange=exchange,
142
+ normalized_symbols=normalized_symbols,
143
+ candle_size='15m' # <---- aggregate 1m into 15m candles
144
+ )[normalized_symbols[0]]
145
+
146
+ assert pd_candles is not None
147
+ pd_candles['timestamp_ms_gap'] = pd_candles['timestamp_ms'].diff()
148
+ timestamp_ms_gap_median = pd_candles['timestamp_ms_gap'].median()
149
+ NUM_MS_IN_1HR = 60*60*1000
150
+ expected_15m_gap_ms = NUM_MS_IN_1HR/4
151
+ assert(timestamp_ms_gap_median==expected_15m_gap_ms)
152
+ total_num_rows = pd_candles.shape[0]
153
+ num_rows_with_15min_gaps = pd_candles[pd_candles.timestamp_ms_gap!=timestamp_ms_gap_median].shape[0]
154
+ assert(num_rows_with_15min_gaps/total_num_rows <= 0.4) # Why not 100% match? minute bars may have gaps (Also depends on what ticker)
121
155
 
122
156
  def test_fetch_candles_futubull(self):
123
157
  # You need Futu OpenD running and you need entitlements
@@ -220,8 +220,13 @@ def timestamp_to_datetime_cols(pd_candles : pd.DataFrame):
220
220
  )
221
221
 
222
222
  pd_candles['timestamp_ms_gap'] = pd_candles['timestamp_ms'] - pd_candles['timestamp_ms'].shift(1)
223
- timestamp_ms_gap = pd_candles.iloc[-1]['timestamp_ms_gap']
224
- assert(pd_candles[~pd_candles.timestamp_ms_gap.isna()][pd_candles.timestamp_ms_gap!=timestamp_ms_gap].shape[0]==0)
223
+
224
+ # Depending on asset, minutes bar may have gaps
225
+ timestamp_ms_gap_median = pd_candles['timestamp_ms_gap'].median()
226
+ NUM_MS_IN_1HR = 60*60*1000
227
+ if timestamp_ms_gap_median>=NUM_MS_IN_1HR:
228
+ num_rows_with_expected_gap = pd_candles[~pd_candles.timestamp_ms_gap.isna()][pd_candles.timestamp_ms_gap==timestamp_ms_gap_median].shape[0]
229
+ assert(num_rows_with_expected_gap/pd_candles.shape[0]>0.9)
225
230
  pd_candles.drop(columns=['timestamp_ms_gap'], inplace=True)
226
231
 
227
232
  def timestamp_to_active_trading_regions(
@@ -511,20 +516,21 @@ def fetch_candles(
511
516
  validation_max_gaps : int = 10,
512
517
  validation_max_end_date_intervals : int = 1
513
518
  ) -> Dict[str, Union[pd.DataFrame, None]]:
519
+ exchange_candles = { '' : None }
514
520
  num_intervals = int(candle_size.replace(candle_size[-1],''))
515
521
 
516
522
  if end_ts>datetime.now().timestamp():
517
523
  end_ts = int(datetime.now().timestamp())
518
524
 
519
525
  if type(exchange) is YahooExchange:
520
- return exchange.fetch_candles(
526
+ exchange_candles = exchange.fetch_candles(
521
527
  start_ts=start_ts,
522
528
  end_ts=end_ts,
523
529
  symbols=normalized_symbols,
524
530
  candle_size=candle_size
525
531
  )
526
532
  elif type(exchange) is NASDAQExchange:
527
- return exchange.fetch_candles(
533
+ exchange_candles = exchange.fetch_candles(
528
534
  start_ts=start_ts,
529
535
  end_ts=end_ts,
530
536
  symbols=normalized_symbols,
@@ -541,11 +547,9 @@ def fetch_candles(
541
547
  pd_candles = exchange_candles[symbol]
542
548
  if not pd_candles is None:
543
549
  fix_column_types(pd_candles) # You don't want to do this from Futubull as you'd need import Futubull from there: Circular references
544
- if num_intervals!=1:
545
- exchange_candles[symbol] = aggregate_candles(candle_size, pd_candles)
546
- return exchange_candles
550
+
547
551
  elif issubclass(exchange.__class__, CcxtExchange):
548
- return _fetch_candles_ccxt(
552
+ exchange_candles = _fetch_candles_ccxt(
549
553
  start_ts=start_ts,
550
554
  end_ts=end_ts,
551
555
  exchange=exchange,
@@ -553,7 +557,12 @@ def fetch_candles(
553
557
  candle_size=candle_size,
554
558
  num_candles_limit=num_candles_limit
555
559
  )
556
- return { '' : None }
560
+ if num_intervals!=1:
561
+ for symbol in exchange_candles:
562
+ if not exchange_candles[symbol] is None:
563
+ exchange_candles[symbol] = aggregate_candles(candle_size, exchange_candles[symbol]) # type: ignore
564
+
565
+ return exchange_candles # type: ignore
557
566
 
558
567
  '''
559
568
  Find listing date https://gist.github.com/mr-easy/5185b1dcdd5f9f908ff196446f092e9b
@@ -589,8 +598,6 @@ def _fetch_candles_ccxt(
589
598
 
590
599
  rsp = {}
591
600
 
592
- num_intervals = int(candle_size.replace(candle_size[-1],''))
593
-
594
601
  exchange.load_markets()
595
602
 
596
603
  num_tickers = len(normalized_symbols)
@@ -653,9 +660,6 @@ def _fetch_candles_ccxt(
653
660
  fix_column_types(pd_all_candles)
654
661
  pd_all_candles['pct_chg_on_close'] = pd_all_candles['close'].pct_change()
655
662
 
656
- if num_intervals!=1:
657
- pd_all_candles = aggregate_candles(candle_size, pd_all_candles)
658
-
659
663
  rsp[ticker] = pd_all_candles
660
664
 
661
665
  i+=1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: siglab_py
3
- Version: 0.5.70
3
+ Version: 0.5.72
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -20,7 +20,7 @@ siglab_py/ordergateway/gateway.py,sha256=Z-BQ-Z9gXoNrKQHzRIy9R1mnCybf9QwWhHpqkSI
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
- siglab_py/tests/integration/market_data_util_tests.py,sha256=p-RWIJZLyj0lAdfi4QTIeAttCm_e8mEVWFKh4OWuogU,7189
23
+ siglab_py/tests/integration/market_data_util_tests.py,sha256=XKO8CX9AF7xRjRvt4lb938v_s89d2IBLAXKfZDdUxdY,8705
24
24
  siglab_py/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  siglab_py/tests/unit/analytic_util_tests.py,sha256=wbasww0ZN9Kb-6e8V_q4o4hpstzVBvQ6Bop_0HZQUtw,5926
26
26
  siglab_py/tests/unit/market_data_util_tests.py,sha256=A1y83itISmMJdn6wLpfwcr4tGola8wTf1D1xbelMvgw,2026
@@ -29,13 +29,13 @@ siglab_py/tests/unit/trading_util_tests.py,sha256=LiflZrduWXyLMbpSFQCaydA7jdJx3v
29
29
  siglab_py/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  siglab_py/util/analytic_util.py,sha256=ywp-VI8UlmoYVej2SaJMrOyheFwyh9KVjsnfw55dpMU,63785
31
31
  siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
32
- siglab_py/util/market_data_util.py,sha256=o0OAqv5ZLEvtvg2WvkUu7ZIs7Qa-ARc1JNaFJgDuC0o,32800
32
+ siglab_py/util/market_data_util.py,sha256=QPI7ZvtYzRllz6REBkQRouMwEG_iHPuDMsbt-4n5FSs,33054
33
33
  siglab_py/util/notification_util.py,sha256=tNZMUkkjz4q1CKqcQq62oEmZgHgNIwz2Iw9J22V22Zw,2668
34
34
  siglab_py/util/retry_util.py,sha256=g-UU6pkPouWZZRZEqP99R2Z0lX5xzckYkzjwqqSDpVQ,922
35
35
  siglab_py/util/simple_math.py,sha256=F7vGj0O2Y9EAGcMFR6SN1tTjBWO_a7YZeiTzk3eHaVI,8518
36
36
  siglab_py/util/slack_notification_util.py,sha256=G27n-adbT3Q6oaHSMvu_Nom794rrda5PprSF-zvmzkM,1912
37
37
  siglab_py/util/trading_util.py,sha256=dlIOzoMGnddLSFODcJ61EBH1Aeruq4IT2MsxIdFkV9I,5252
38
- siglab_py-0.5.70.dist-info/METADATA,sha256=R2s8me8ZPbvgMENhVN-niWA2JWcXWSzbPXFoaign4X4,829
39
- siglab_py-0.5.70.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
40
- siglab_py-0.5.70.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
41
- siglab_py-0.5.70.dist-info/RECORD,,
38
+ siglab_py-0.5.72.dist-info/METADATA,sha256=O_EgcwSXXfqrCkdUURk-tEgNyhfz6x0zY6f1ZQ8VIoY,829
39
+ siglab_py-0.5.72.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
40
+ siglab_py-0.5.72.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
41
+ siglab_py-0.5.72.dist-info/RECORD,,