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

@@ -12,6 +12,8 @@ import numpy as np
12
12
  from redis import StrictRedis
13
13
 
14
14
  '''
15
+ https://medium.com/@norman-lm-fung/aggregated-orderbooks-using-ccxt-and-rest-calls-to-cexes-7bfdc8400d05
16
+
15
17
  From command prompt:
16
18
  python aggregated_orderbook.py --normalized_symbol BTC/USDT:USDT --sliding_window_num_intervals 1200 --update_imabalce_csv_intervals 100 --dump_imbalance_to_disk Y --publish_imbalance_to_redis N
17
19
  or
@@ -14,7 +14,9 @@ from redis import StrictRedis
14
14
  import pandas as pd
15
15
 
16
16
  from util.market_data_util import timestamp_to_datetime_cols, fetch_deribit_btc_option_expiries, fetch_ohlcv_one_candle
17
-
17
+ '''
18
+ https://medium.com/@norman-lm-fung/monitoring-incoming-deribit-open-interest-fd8c8d596ca0
19
+ '''
18
20
  param : Dict = {
19
21
  'market' : 'BTC',
20
22
 
@@ -123,7 +123,51 @@ class DivisiblePosition(Order):
123
123
  return filled_amount
124
124
 
125
125
  def get_average_cost(self) -> float:
126
- average_cost = sum([ self.executions[order_id]['average'] * self.executions[order_id]['amount'] for order_id in self.executions ])
126
+ '''
127
+ Example hyperliquid don't have 'average' in response JSON:
128
+
129
+ {
130
+ 'info': {
131
+ 'order': {
132
+ 'coin': 'SUSHI',
133
+ 'side': 'B',
134
+ 'limitPx': '0.79252',
135
+ 'sz': '0.0',
136
+ 'oid': xxx,
137
+ 'timestamp': xxx,
138
+ 'origSz': '30.0'
139
+ },
140
+ 'status': 'filled',
141
+ 'statusTimestamp': xxx},
142
+ 'id': 'xxx',
143
+ 'clientOrderId': None,
144
+ 'timestamp': xxx,
145
+ 'datetime': '2025-02-26T01:00:00.522Z',
146
+ 'lastTradeTimestamp': None,
147
+ 'lastUpdateTimestamp': xxx,
148
+ 'symbol': 'SUSHI/USDC:USDC',
149
+ 'type': None,
150
+ 'timeInForce': None,
151
+ 'postOnly': None,
152
+ 'reduceOnly': None,
153
+ 'side': 'buy',
154
+ 'price': 0.79252,
155
+ 'triggerPrice': None,
156
+ 'amount': 30.0,
157
+ 'cost': 23.7756,
158
+ 'average': None, <-- No average cost?
159
+ 'filled': 30.0,
160
+ 'remaining': 0.0,
161
+ 'status': 'closed',
162
+ 'fee': None,
163
+ 'trades': [],
164
+ 'fees': [],
165
+ 'stopPrice': None,
166
+ 'takeProfitPrice': None,
167
+ 'stopLossPrice': None
168
+ }
169
+ '''
170
+ average_cost = sum([ (self.executions[order_id]['average'] if self.executions[order_id]['average'] else self.executions[order_id]['price']) * self.executions[order_id]['amount'] for order_id in self.executions ])
127
171
  average_cost = average_cost / sum([ self.executions[order_id]['amount'] for order_id in self.executions ])
128
172
  return average_cost
129
173
 
@@ -12,6 +12,7 @@ import hashlib
12
12
  from collections import deque
13
13
  import logging
14
14
  import json
15
+ import uuid
15
16
  from io import StringIO
16
17
  import re
17
18
  from re import Pattern
@@ -178,11 +179,12 @@ class LogLevel(Enum):
178
179
 
179
180
  param : Dict = {
180
181
  'gateway_id' : '---',
182
+ 'dry_run' : False,
181
183
 
182
184
  "incoming_orders_topic_regex" : r"ordergateway_pending_orders_$GATEWAY_ID$",
183
185
  "executions_publish_topic" : r"ordergateway_executions_$GATEWAY_ID$",
184
186
 
185
- "fetch_order_status_poll_freq_ms" : 500,
187
+ "loop_freq_ms" : 500,
186
188
 
187
189
  'mds' : {
188
190
  'topics' : {
@@ -207,6 +209,39 @@ sh = logging.StreamHandler()
207
209
  sh.setLevel(log_level)
208
210
  sh.setFormatter(formatter)
209
211
  logger.addHandler(sh)
212
+ fh = logging.FileHandler(f"ordergateway.log")
213
+ fh.setLevel(log_level)
214
+ fh.setFormatter(formatter)
215
+ logger.addHandler(fh)
216
+
217
+ DUMMY_EXECUTION : Dict[str, Any] = {
218
+ "clientOrderId": None,
219
+ "timestamp": None,
220
+ "datetime": None,
221
+ "symbol": None,
222
+ "type": None,
223
+ "side": None,
224
+ "price": None,
225
+ "average": None,
226
+ "cost": None,
227
+ "amount": None,
228
+ "filled": None,
229
+ "remaining": None,
230
+ "status": "closed",
231
+ "fee": {
232
+ "cost": None,
233
+ "currency": "USDT"
234
+ },
235
+ "trades": [],
236
+ "reduceOnly": False,
237
+ "fees": [
238
+ {
239
+ "cost": 0,
240
+ "currency": "USDT"
241
+ }
242
+ ],
243
+ "multiplier": None
244
+ }
210
245
 
211
246
  def log(message : str, log_level : LogLevel = LogLevel.INFO):
212
247
  if log_level.value<LogLevel.WARNING.value:
@@ -222,6 +257,7 @@ def parse_args():
222
257
  parser = argparse.ArgumentParser() # type: ignore
223
258
 
224
259
  parser.add_argument("--gateway_id", help="gateway_id: Where are you sending your order?", default=None)
260
+ parser.add_argument("--dry_run", help="Y or N (default, for testing). If Y, orders won't be dispatched to exchange, gateway will fake reply.", default='N')
225
261
  parser.add_argument("--default_type", help="default_type: spot, linear, inverse, futures ...etc", default='linear')
226
262
  parser.add_argument("--rate_limit_ms", help="rate_limit_ms: Check your exchange rules", default=100)
227
263
 
@@ -233,6 +269,15 @@ def parse_args():
233
269
 
234
270
  args = parser.parse_args()
235
271
  param['gateway_id'] = args.gateway_id
272
+
273
+ if args.dry_run:
274
+ if args.dry_run=='Y':
275
+ param['dry_run'] = True
276
+ else:
277
+ param['dry_run'] = False
278
+ else:
279
+ param['dry_run'] = False
280
+
236
281
  param['default_type'] = args.default_type
237
282
  param['rate_limit_ms'] = int(args.rate_limit_ms)
238
283
 
@@ -264,7 +309,7 @@ def init_redis_client() -> StrictRedis:
264
309
 
265
310
  return redis_client
266
311
 
267
- def instantiate_exchange(
312
+ async def instantiate_exchange(
268
313
  gateway_id : str,
269
314
  api_key : str,
270
315
  secret : str,
@@ -306,12 +351,58 @@ def instantiate_exchange(
306
351
  # https://github.com/ccxt/ccxt/blob/master/python/ccxt/deribit.py#L360
307
352
  exchange = ccxtpro.deribit(exchange_params) # type: ignore
308
353
  elif exchange_name=='hyperliquid':
309
- # swap
310
- # https://github.com/ccxt/ccxt/blob/master/python/ccxt/hyperliquid.py#L225
311
- exchange = ccxtpro.hyperliquid(exchange_params) # type: ignore
354
+ '''
355
+ https://app.hyperliquid.xyz/API
356
+
357
+ defaultType from ccxt: swap
358
+ https://github.com/ccxt/ccxt/blob/master/python/ccxt/hyperliquid.py#L225
359
+
360
+ How to integrate? You can skip first 6 min: https://www.youtube.com/watch?v=UuBr331wxr4&t=363s
361
+
362
+ Example,
363
+ API credentials created under "\ More \ API":
364
+ Ledger Arbitrum Wallet Address: 0xAAAAA <-- This is your Ledger Arbitrum wallet address with which you connect to Hyperliquid.
365
+ API Wallet Address 0xBBBBB <-- Generated
366
+ privateKey 0xCCCCC
367
+
368
+ Basic connection via CCXT:
369
+ import asyncio
370
+ import ccxt.pro as ccxtpro
371
+
372
+ async def main():
373
+ rate_limit_ms = 100
374
+ exchange_params = {
375
+ "walletAddress" : "0xAAAAA", # Ledger Arbitrum Wallet Address here! Not the generated address.
376
+ "privateKey" : "0xCCCCC"
377
+ }
378
+ exchange = ccxtpro.hyperliquid(exchange_params)
379
+ balances = await exchange.fetch_balance()
380
+ print(balances)
381
+
382
+ asyncio.run(main())
383
+ '''
384
+ exchange = ccxtpro.hyperliquid(
385
+ {
386
+ "walletAddress" : api_key,
387
+ "privateKey" : secret,
388
+ 'enableRateLimit' : True,
389
+ 'rateLimit' : rate_limit_ms
390
+ }
391
+ ) # type: ignore
312
392
  else:
313
393
  raise ArgumentError(f"Unsupported exchange {exchange_name}, check gateway_id {gateway_id}.")
314
394
 
395
+ await exchange.load_markets() # type: ignore
396
+
397
+ '''
398
+ Is this necessary? The added trouble is for example bybit.authenticate requires arg 'url'. binance doesn't. And fetch_balance already test credentials.
399
+
400
+ try:
401
+ await exchange.authenticate() # type: ignore
402
+ except Exception as swallow_this_error:
403
+ pass
404
+ '''
405
+
315
406
  return exchange
316
407
 
317
408
  async def watch_orders_task(
@@ -329,7 +420,7 @@ async def watch_orders_task(
329
420
  except Exception as loop_err:
330
421
  print(f"watch_orders_task error: {loop_err}")
331
422
 
332
- await asyncio.sleep(int(param['fetch_order_status_poll_freq_ms']/1000))
423
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
333
424
 
334
425
  async def execute_one_position(
335
426
  exchange : AnyExchange,
@@ -337,17 +428,11 @@ async def execute_one_position(
337
428
  param : Dict,
338
429
  executions : Dict[str, Dict[str, Any]]
339
430
  ):
340
- await exchange.load_markets() # type: ignore
341
- try:
342
- await exchange.authenticate() # type: ignore
343
- except Exception as swallow_this_error:
344
- pass # @todo, perhaps a better way for handling this?
345
-
346
431
  market : Dict[str, Any] = exchange.markets[position.ticker] if position.ticker in exchange.markets else None # type: ignore
347
432
  if not market:
348
433
  raise ArgumentError(f"Market not found for {position.ticker} under {exchange.name}") # type: ignore
349
434
 
350
- min_amount = float(market['limits']['amount']['min']) # type: ignore
435
+ min_amount = float(market['limits']['amount']['min']) if market['limits']['amount']['min'] else 0 # type: ignore
351
436
  multiplier = market['contractSize'] if 'contractSize' in market and market['contractSize'] else 1
352
437
  position.multiplier = multiplier
353
438
 
@@ -355,43 +440,77 @@ async def execute_one_position(
355
440
  i = 0
356
441
  for slice in slices:
357
442
  try:
443
+ dt_now : datetime = datetime.now()
444
+
358
445
  slice_amount_in_base_ccy : float = slice.amount
359
446
  rounded_slice_amount_in_base_ccy = slice_amount_in_base_ccy / multiplier # After devided by multiplier, rounded_slice_amount_in_base_ccy in number of contracts actually (Not in base ccy).
360
447
  rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, rounded_slice_amount_in_base_ccy) # type: ignore
361
448
  rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy)
362
449
  rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
363
450
 
364
- limit_price : float = 0
365
- rounded_limit_price : float = 0
451
+ orderbook = await exchange.fetch_order_book(symbol=position.ticker, limit=3) # type: ignore
452
+ if position.side=='buy':
453
+ asks = [ ask[0] for ask in orderbook['asks'] ]
454
+ best_asks = min(asks)
455
+ limit_price : float= best_asks * (1 + position.leg_room_bps/10000)
456
+ else:
457
+ bids = [ bid[0] for bid in orderbook['bids'] ]
458
+ best_bid = max(bids)
459
+ limit_price : float = best_bid * (1 - position.leg_room_bps/10000)
460
+
461
+ rounded_limit_price : float = exchange.price_to_precision(position.ticker, limit_price) # type: ignore
462
+ rounded_limit_price = float(rounded_limit_price)
463
+
366
464
  if position.order_type=='limit':
367
- orderbook = await exchange.fetch_order_book(symbol=position.ticker, limit=3) # type: ignore
368
- if position.side=='buy':
369
- asks = [ ask[0] for ask in orderbook['asks'] ]
370
- best_asks = min(asks)
371
- limit_price = best_asks * (1 + position.leg_room_bps/10000)
465
+ if not param['dry_run']:
466
+ executed_order = await exchange.create_order( # type: ignore
467
+ symbol = position.ticker,
468
+ type = position.order_type,
469
+ amount = rounded_slice_amount_in_base_ccy,
470
+ price = rounded_limit_price,
471
+ side = position.side
472
+ )
372
473
  else:
373
- bids = [ bid[0] for bid in orderbook['bids'] ]
374
- best_bid = max(bids)
375
- limit_price = best_bid * (1 - position.leg_room_bps/10000)
376
-
377
- rounded_limit_price = exchange.price_to_precision(position.ticker, limit_price) # type: ignore
378
- rounded_limit_price = float(rounded_limit_price)
379
-
380
- executed_order = await exchange.create_order( # type: ignore
381
- symbol = position.ticker,
382
- type = position.order_type,
383
- amount = rounded_slice_amount_in_base_ccy,
384
- price = rounded_limit_price,
385
- side = position.side
386
- )
474
+ executed_order = DUMMY_EXECUTION.copy()
475
+ executed_order['clientOrderId'] = str(uuid.uuid4())
476
+ executed_order['timestamp'] = dt_now.timestamp()
477
+ executed_order['datetime'] = dt_now
478
+ executed_order['symbol'] = position.ticker
479
+ executed_order['type'] = position.order_type
480
+ executed_order['side'] = position.side
481
+ executed_order['price'] = rounded_limit_price
482
+ executed_order['average'] = rounded_limit_price
483
+ executed_order['cost'] = 0
484
+ executed_order['amount'] = rounded_slice_amount_in_base_ccy
485
+ executed_order['filled'] = rounded_slice_amount_in_base_ccy
486
+ executed_order['remaining'] = 0
487
+ executed_order['status'] = 'closed'
488
+ executed_order['multiplier'] = position.multiplier
387
489
 
388
490
  else:
389
- executed_order = await exchange.create_order( # type: ignore
390
- symbol = position.ticker,
391
- type = position.order_type,
392
- amount = rounded_slice_amount_in_base_ccy,
393
- side = position.side
394
- )
491
+ if not param['dry_run']:
492
+ executed_order = await exchange.create_order( # type: ignore
493
+ symbol = position.ticker,
494
+ type = position.order_type,
495
+ amount = rounded_slice_amount_in_base_ccy,
496
+ side = position.side
497
+ )
498
+ else:
499
+ executed_order = DUMMY_EXECUTION.copy()
500
+ executed_order['clientOrderId'] = str(uuid.uuid4())
501
+ executed_order['timestamp'] = dt_now.timestamp()
502
+ executed_order['datetime'] = dt_now
503
+ executed_order['symbol'] = position.ticker
504
+ executed_order['type'] = position.order_type
505
+ executed_order['side'] = position.side
506
+ executed_order['price'] = rounded_limit_price
507
+ executed_order['average'] = rounded_limit_price
508
+ executed_order['cost'] = 0
509
+ executed_order['amount'] = rounded_slice_amount_in_base_ccy
510
+ executed_order['filled'] = rounded_slice_amount_in_base_ccy
511
+ executed_order['remaining'] = 0
512
+ executed_order['status'] = 'closed'
513
+ executed_order['multiplier'] = position.multiplier
395
514
 
396
515
  executed_order['slice_id'] = i
397
516
 
@@ -459,7 +578,7 @@ async def execute_one_position(
459
578
  log(f"Limit order fully filled: {order_id}", log_level=LogLevel.INFO)
460
579
  break
461
580
 
462
- await asyncio.sleep(int(param['fetch_order_status_poll_freq_ms']/1000))
581
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
463
582
 
464
583
 
465
584
 
@@ -511,7 +630,7 @@ async def execute_one_position(
511
630
 
512
631
  log(f"Waiting for resent market order to close {order_id} ...")
513
632
 
514
- await asyncio.sleep(int(param['fetch_order_status_poll_freq_ms']/1000))
633
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
515
634
 
516
635
  log(f"Resent market order{order_id} filled. status: {order_status}, filled_amount: {filled_amount}, remaining_amount: {remaining_amount}")
517
636
 
@@ -618,6 +737,8 @@ async def work(
618
737
 
619
738
  except Exception as loop_error:
620
739
  log(f"Error: {loop_error} {str(sys.exc_info()[0])} {str(sys.exc_info()[1])} {traceback.format_exc()}")
740
+ finally:
741
+ await asyncio.sleep(int(param['loop_freq_ms']/1000))
621
742
 
622
743
  async def main():
623
744
  parse_args()
@@ -652,7 +773,7 @@ async def main():
652
773
 
653
774
  redis_client : StrictRedis = init_redis_client()
654
775
 
655
- exchange : Union[AnyExchange, None] = instantiate_exchange(
776
+ exchange : Union[AnyExchange, None] = await instantiate_exchange(
656
777
  gateway_id=param['gateway_id'],
657
778
  api_key=api_key,
658
779
  secret=secret,
@@ -77,12 +77,12 @@ if __name__ == '__main__':
77
77
 
78
78
  positions_2 : List[DivisiblePosition] = [
79
79
  DivisiblePosition(
80
- ticker = 'SUSHI/USDT:USDT',
80
+ ticker = 'SUSHI/USDC:USDC',
81
81
  side = 'buy',
82
- amount = 10,
82
+ amount = 60,
83
83
  leg_room_bps = 5,
84
84
  order_type = 'limit',
85
- slices=5,
85
+ slices=2,
86
86
  wait_fill_threshold_ms=15000
87
87
  ),
88
88
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab-py
3
- Version: 0.1.9
3
+ Version: 0.1.10
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -3,17 +3,17 @@ siglab_py/constants.py,sha256=YGNdEsWtQ99V2oltaynZTjM8VIboSfyIjDXJKSlhv4U,132
3
3
  siglab_py/exchanges/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  siglab_py/exchanges/any_exchange.py,sha256=pnSAatOzUIgwY9iPa5XR5xF_C5VIXAWi_uAhjvBP8bQ,749
5
5
  siglab_py/market_data_providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- siglab_py/market_data_providers/aggregated_orderbook_provider.py,sha256=uN6oD6PqbfnkPKVzKF_GfSEuXdtmv0lFTB-yg9gRYcI,23234
6
+ siglab_py/market_data_providers/aggregated_orderbook_provider.py,sha256=FZRobEBNRzcNGlOG3u38OVhmOZYlkNm8dVvR-S7Ii2g,23342
7
7
  siglab_py/market_data_providers/candles_provider.py,sha256=fqHJjlECsBiBlpgyywrc4gTgxiROPNzZM8KxQBB5cOg,14139
8
8
  siglab_py/market_data_providers/candles_ta_provider.py,sha256=uiAhbEZZdTF-YulBHpSLwabos5LHCKU91NTiTmpUc0w,12001
9
- siglab_py/market_data_providers/deribit_options_expiry_provider.py,sha256=mP-508KftjseOWvttjEOu6fXu7J7DPCYGoyeuY6I_mU,8585
9
+ siglab_py/market_data_providers/deribit_options_expiry_provider.py,sha256=e9Ee8TmC8pXaid8-jouSLKIpuW6_JBBgwRTieI665yQ,8684
10
10
  siglab_py/market_data_providers/orderbooks_provider.py,sha256=cBp-HYCups2Uiwzw0SaUwxrg4unJvnm2TDqIK8f4hUg,15674
11
11
  siglab_py/market_data_providers/test_provider.py,sha256=wBLCgcWjs7FGZJXWsNyn30lkOLa_cgpuvqRakMC0wbA,2221
12
12
  siglab_py/ordergateway/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- siglab_py/ordergateway/client.py,sha256=TCazOjsRjjrImSfT4-cmbY7bvmxwJXixMpaJM5opIcY,6528
13
+ siglab_py/ordergateway/client.py,sha256=1Y0g2AT_Sh7Y-2YdEpnrGHwu2Ml1iqKFmj8_LOoF8YU,8301
14
14
  siglab_py/ordergateway/encrypt_keys_util.py,sha256=-qi87db8To8Yf1WS1Q_Cp2Ya7ZqgWlRqSHfNXCM7wE4,1339
15
- siglab_py/ordergateway/gateway.py,sha256=31u3Y6nz5mFV9u_wVa3kjGWMyCnwtpaNNnGgy9Gf2qI,30609
16
- siglab_py/ordergateway/test_ordergateway.py,sha256=DH4djEZeWZr69lTx_xG9_N8S_E2wAG4S3430VisU5w4,3805
15
+ siglab_py/ordergateway/gateway.py,sha256=5ZN_7gOOeJdADlbqpX_tDhZ4v9aw-GgLyYr9o544zJc,35317
16
+ siglab_py/ordergateway/test_ordergateway.py,sha256=sCj25DSBmWAJPt-CTPqy12K_tOIAHygtEj0vHa1nbnY,3805
17
17
  siglab_py/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  siglab_py/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  siglab_py/tests/integration/market_data_util_tests.py,sha256=HFESIXEdQmrbDV3DvLiDLq0iE-4itysxSFur_Th4jjE,5207
@@ -24,7 +24,7 @@ siglab_py/util/analytic_util.py,sha256=QLabbEMqM4rKKH2PE_LqxIyo-BUdCRhkLybLATBIm
24
24
  siglab_py/util/aws_util.py,sha256=KGmjHrr1rpnnxr33nXHNzTul4tvyyxl9p6gpwNv0Ygc,2557
25
25
  siglab_py/util/market_data_util.py,sha256=3qTq71xGvQXj0ORKJV50IN5FP_mCBF_gvdmlPyhdyco,16439
26
26
  siglab_py/util/retry_util.py,sha256=mxYuRFZRZoaQQjENcwPmxhxixtd1TFvbxIdPx4RwfRc,743
27
- siglab_py-0.1.9.dist-info/METADATA,sha256=VhHvluQnwwmVjf3BuiPeI7d19ruSkwyRo0snXBl5M50,1096
28
- siglab_py-0.1.9.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
29
- siglab_py-0.1.9.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
30
- siglab_py-0.1.9.dist-info/RECORD,,
27
+ siglab_py-0.1.10.dist-info/METADATA,sha256=Gc_AY2ISZcBXIYCdMFuVfe3v1_zwAB64fyK-iFfuO4g,1097
28
+ siglab_py-0.1.10.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
29
+ siglab_py-0.1.10.dist-info/top_level.txt,sha256=AbD4VR9OqmMOGlMJLkAVPGQMtUPIQv0t1BF5xmcLJSk,10
30
+ siglab_py-0.1.10.dist-info/RECORD,,