siglab-py 0.1.19__tar.gz → 0.1.20__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 (36) hide show
  1. {siglab_py-0.1.19 → siglab_py-0.1.20}/PKG-INFO +1 -1
  2. {siglab_py-0.1.19 → siglab_py-0.1.20}/pyproject.toml +1 -1
  3. {siglab_py-0.1.19 → siglab_py-0.1.20}/setup.cfg +1 -1
  4. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/exchanges/futubull.py +21 -1
  5. siglab_py-0.1.20/siglab_py/market_data_providers/futu_candles.py +95 -0
  6. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/ordergateway/gateway.py +5 -2
  7. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py.egg-info/PKG-INFO +1 -1
  8. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py.egg-info/SOURCES.txt +1 -0
  9. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/__init__.py +0 -0
  10. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/constants.py +0 -0
  11. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/exchanges/__init__.py +0 -0
  12. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/exchanges/any_exchange.py +0 -0
  13. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/__init__.py +0 -0
  14. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
  15. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/candles_provider.py +0 -0
  16. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
  17. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
  18. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
  19. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/market_data_providers/test_provider.py +0 -0
  20. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/ordergateway/__init__.py +0 -0
  21. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/ordergateway/client.py +0 -0
  22. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
  23. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/ordergateway/test_ordergateway.py +0 -0
  24. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/tests/__init__.py +0 -0
  25. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/tests/integration/__init__.py +0 -0
  26. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
  27. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/tests/unit/__init__.py +0 -0
  28. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/tests/unit/analytic_util_tests.py +0 -0
  29. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/util/__init__.py +0 -0
  30. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/util/analytic_util.py +0 -0
  31. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/util/aws_util.py +0 -0
  32. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/util/market_data_util.py +0 -0
  33. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py/util/retry_util.py +0 -0
  34. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py.egg-info/dependency_links.txt +0 -0
  35. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py.egg-info/requires.txt +0 -0
  36. {siglab_py-0.1.19 → siglab_py-0.1.20}/siglab_py.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab_py
3
- Version: 0.1.19
3
+ Version: 0.1.20
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.1.19"
7
+ version = "0.1.20"
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.1.19
3
+ version = 0.1.20
4
4
  description = Market data fetches, TA calculations and generic order gateway.
5
5
  author = r0bbarh00d
6
6
  author_email = r0bbarh00d@gmail.com
@@ -8,7 +8,9 @@ Subscribe L2 data: https://openapi.futunn.com/futu-api-doc/en/intro/authority.ht
8
8
 
9
9
  Investor Protection: https://www.futuhk.com/en
10
10
 
11
- Margin Trading: https://www.futunn.com/en/learn/detail-what-is-margin-trading-62010-220679271
11
+ Margin Trading:
12
+ https://www.futunn.com/en/learn/detail-what-is-margin-trading-62010-220679271
13
+ Margin rate 6.8% annual https://www.futuhk.com/en/support/topic2_417
12
14
 
13
15
  Download Futu OpenD
14
16
  https://www.futuhk.com/en/support/topic1_464?global_content=%7B%22promote_id%22%3A13765%2C%22sub_promote_id%22%3A10%7D
@@ -40,6 +42,7 @@ API
40
42
 
41
43
  '''
42
44
  from datetime import datetime, timedelta
45
+ import pytz
43
46
  from typing import List, Dict, Union, Any, NoReturn
44
47
  import pandas as pd
45
48
 
@@ -259,6 +262,23 @@ class Futubull(AnyExchange):
259
262
  '''
260
263
  time_key = row['time_key']
261
264
  dt = datetime.strptime(time_key, "%Y-%m-%d %H:%M:%S")
265
+
266
+ if self.market in [ Market.HK, Market.SH, Market.SZ ]:
267
+ tz = pytz.timezone('Asia/Shanghai')
268
+ dt = tz.localize(dt)
269
+ elif self.market == Market.US:
270
+ tz = pytz.timezone('US/Eastern')
271
+ dt = tz.localize(dt)
272
+ elif self.market == Market.CA:
273
+ tz = pytz.timezone('US/Eastern')
274
+ dt = tz.localize(dt)
275
+ elif self.market == Market.AU:
276
+ tz = pytz.timezone('Australia/Sydney')
277
+ dt = tz.localize(dt)
278
+ else:
279
+ # @todo: HK SH SZ US AU CA FX
280
+ raise ValueError(f"Unsupported market {self.market}")
281
+
262
282
  timestamp_ms = int(dt.timestamp() * 1000)
263
283
  open = row['open']
264
284
  high = row['high']
@@ -0,0 +1,95 @@
1
+ import argparse
2
+ from datetime import datetime
3
+ from typing import Dict, Union
4
+ import asyncio
5
+
6
+ from futu import *
7
+
8
+ from siglab_py.exchanges.futubull import Futubull
9
+ from siglab_py.util.market_data_util import fetch_candles
10
+ from siglab_py.util.analytic_util import compute_candles_stats
11
+
12
+ end_date : datetime = datetime.today()
13
+ start_date : datetime = end_date - timedelta(days=365)
14
+
15
+ param : Dict = {
16
+ 'symbol' : None,
17
+ 'trdmarket' : TrdMarket.HK,
18
+ 'security_firm' : SecurityFirm.FUTUSECURITIES,
19
+ 'market' : Market.HK,
20
+ 'security_type' : SecurityType.STOCK,
21
+ 'daemon' : {
22
+ 'host' : '127.0.0.1',
23
+ 'port' : 11111
24
+ }
25
+ }
26
+
27
+ '''
28
+ If debugging from VSCode, launch.json:
29
+
30
+ {
31
+ "version": "0.2.0",
32
+ "configurations": [
33
+ {
34
+ "name": "Python Debugger: Current File",
35
+ "type": "debugpy",
36
+ "request": "launch",
37
+ "program": "${file}",
38
+ "console": "integratedTerminal",
39
+ "args" : [
40
+ "--symbol", "HK.00700",
41
+ "--market", "HK",
42
+ "--trdmarket", "HK",
43
+ "--security_firm", "FUTUSECURITIES",
44
+ "--security_type", "STOCK"
45
+ ],
46
+ "env": {
47
+ "PYTHONPATH": "${workspaceFolder}"
48
+ }
49
+ }
50
+ ]
51
+ }
52
+ '''
53
+ def parse_args():
54
+ parser = argparse.ArgumentParser() # type: ignore
55
+ parser.add_argument("--symbol", help="symbol, example HK.00700", default=None)
56
+
57
+ '''
58
+ Enums here:
59
+ https://openapi.futunn.com/futu-api-doc/en/quote/quote.html#66
60
+ https://openapi.futunn.com/futu-api-doc/en/trade/trade.html#9434
61
+ '''
62
+ parser.add_argument("--market", help="market: HK SH SZ US AU CA FX", default=Market.HK)
63
+ parser.add_argument("--trdmarket", help="trdmarket: HK, HKCC, HKFUND, FUTURES, CN, CA, AU, JP, MY, SG, US, USFUND", default=TrdMarket.HK)
64
+ parser.add_argument("--security_firm", help="security_firm: FUTUSECURITIES (HK), FUTUINC (US), FUTUSG (SG), FUTUAU (AU)", default=SecurityFirm.FUTUSECURITIES)
65
+ parser.add_argument("--security_type", help="STOCK, BOND, ETF, FUTURE, WARRANT, IDX ... ", default=SecurityType.STOCK)
66
+
67
+ args = parser.parse_args()
68
+ param['symbol'] = args.symbol
69
+ param['market'] = args.market
70
+ param['trdmarket'] = args.trdmarket
71
+ param['security_firm'] = args.security_firm
72
+
73
+ async def main():
74
+ parse_args()
75
+
76
+ exchange = Futubull(param)
77
+
78
+ pd_candles: Union[pd.DataFrame, None] = fetch_candles(
79
+ start_ts=int(start_date.timestamp()),
80
+ end_ts=int(end_date.timestamp()),
81
+ exchange=exchange,
82
+ normalized_symbols=[ param['symbol'] ],
83
+ candle_size='1d'
84
+ )[param['symbol']]
85
+
86
+ assert pd_candles is not None
87
+
88
+ if pd_candles is not None:
89
+ assert len(pd_candles) > 0, "No candles returned."
90
+ expected_columns = {'exchange', 'symbol', 'timestamp_ms', 'open', 'high', 'low', 'close', 'volume', 'datetime_utc', 'datetime', 'year', 'month', 'day', 'hour', 'minute'}
91
+ assert set(pd_candles.columns) >= expected_columns, "Missing expected columns."
92
+ assert pd_candles['timestamp_ms'].notna().all(), "timestamp_ms column contains NaN values."
93
+ assert pd_candles['timestamp_ms'].is_monotonic_increasing, "Timestamps are not in ascending order."
94
+
95
+ asyncio.run(main())
@@ -463,9 +463,13 @@ async def execute_one_position(
463
463
  slice_amount_in_base_ccy : float = slice.amount
464
464
  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).
465
465
  rounded_slice_amount_in_base_ccy = exchange.amount_to_precision(position.ticker, rounded_slice_amount_in_base_ccy) # type: ignore
466
- rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy)
466
+ rounded_slice_amount_in_base_ccy = float(rounded_slice_amount_in_base_ccy) if rounded_slice_amount_in_base_ccy else 0
467
467
  rounded_slice_amount_in_base_ccy = rounded_slice_amount_in_base_ccy if rounded_slice_amount_in_base_ccy>min_amount else min_amount
468
468
 
469
+ if rounded_slice_amount_in_base_ccy==0:
470
+ log(f"{position.ticker} Slice amount rounded to zero?! slice amount before rounding: {slice.amount}")
471
+ continue
472
+
469
473
  orderbook = await exchange.fetch_order_book(symbol=position.ticker, limit=3) # type: ignore
470
474
  if position.side=='buy':
471
475
  asks = [ ask[0] for ask in orderbook['asks'] ]
@@ -604,7 +608,6 @@ async def execute_one_position(
604
608
  await asyncio.sleep(int(param['loop_freq_ms']/1000))
605
609
 
606
610
 
607
-
608
611
  # Cancel hung limit order, resend as market
609
612
  if order_status!='closed':
610
613
  # If no update from websocket, do one last fetch via REST
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: siglab-py
3
- Version: 0.1.19
3
+ Version: 0.1.20
4
4
  Summary: Market data fetches, TA calculations and generic order gateway.
5
5
  Author: r0bbarh00d
6
6
  Author-email: r0bbarh00d <r0bbarh00d@gmail.com>
@@ -15,6 +15,7 @@ siglab_py/market_data_providers/aggregated_orderbook_provider.py
15
15
  siglab_py/market_data_providers/candles_provider.py
16
16
  siglab_py/market_data_providers/candles_ta_provider.py
17
17
  siglab_py/market_data_providers/deribit_options_expiry_provider.py
18
+ siglab_py/market_data_providers/futu_candles.py
18
19
  siglab_py/market_data_providers/orderbooks_provider.py
19
20
  siglab_py/market_data_providers/test_provider.py
20
21
  siglab_py/ordergateway/__init__.py