siglab-py 0.2.1__tar.gz → 0.2.2__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.
- {siglab_py-0.2.1 → siglab_py-0.2.2}/PKG-INFO +1 -1
- {siglab_py-0.2.1 → siglab_py-0.2.2}/pyproject.toml +1 -1
- {siglab_py-0.2.1 → siglab_py-0.2.2}/setup.cfg +1 -1
- siglab_py-0.2.2/siglab_py/constants.py +12 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/ordergateway/gateway.py +41 -7
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/ordergateway/test_ordergateway.py +2 -1
- siglab_py-0.2.2/siglab_py/util/notification_util.py +69 -0
- siglab_py-0.2.2/siglab_py/util/slack_notification_util.py +61 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py.egg-info/PKG-INFO +1 -1
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py.egg-info/SOURCES.txt +3 -1
- siglab_py-0.2.1/siglab_py/constants.py +0 -3
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/exchanges/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/exchanges/any_exchange.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/exchanges/futubull.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/aggregated_orderbook_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/candles_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/candles_ta_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/deribit_options_expiry_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/orderbooks_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/test_provider.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/ordergateway/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/ordergateway/client.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/ordergateway/encrypt_keys_util.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/integration/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/integration/market_data_util_tests.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/unit/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/unit/analytic_util_tests.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/tests/unit/market_data_util_tests.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/util/__init__.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/util/analytic_util.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/util/aws_util.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/util/market_data_util.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/util/retry_util.py +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py.egg-info/dependency_links.txt +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py.egg-info/requires.txt +0 -0
- {siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "siglab_py"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.02"
|
|
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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
|
@@ -25,14 +25,19 @@ import ccxt.pro as ccxtpro
|
|
|
25
25
|
|
|
26
26
|
from siglab_py.exchanges.any_exchange import AnyExchange
|
|
27
27
|
from siglab_py.ordergateway.client import Order, DivisiblePosition
|
|
28
|
+
from siglab_py.util.notification_util import dispatch_notification
|
|
29
|
+
from siglab_py.constants import LogLevel
|
|
28
30
|
|
|
29
31
|
'''
|
|
30
32
|
Usage:
|
|
31
|
-
python gateway.py --gateway_id
|
|
33
|
+
python gateway.py --gateway_id hyperliquid_01 --default_type linear --rate_limit_ms 100
|
|
32
34
|
|
|
33
35
|
--default_type defaults to linear
|
|
34
36
|
--rate_limit_ms defaults to 100
|
|
35
37
|
|
|
38
|
+
--slack_info_url, --slack_critical_url and --slack_alert_url are if you want gateway to dispatch Slack notification on events.
|
|
39
|
+
How to get Slack webhook urls? https://medium.com/@natalia_assad/how-send-a-table-to-slack-using-python-d1a20b08abe0
|
|
40
|
+
|
|
36
41
|
This script is pypy compatible:
|
|
37
42
|
pypy gateway.py --gateway_id bybit_01 --default_type linear --rate_limit_ms 100
|
|
38
43
|
|
|
@@ -82,7 +87,11 @@ To debug from vscode, launch.json:
|
|
|
82
87
|
"--aws_kms_key_id", "",
|
|
83
88
|
"--apikey", "xxx",
|
|
84
89
|
"--secret", "xxx",
|
|
85
|
-
"--passphrase", "xxx"
|
|
90
|
+
"--passphrase", "xxx",
|
|
91
|
+
|
|
92
|
+
"--slack_info_url", "https://hooks.slack.com/services/xxx",
|
|
93
|
+
"--slack_critial_url", "https://hooks.slack.com/services/xxx",
|
|
94
|
+
"--slack_alert_url", "https://hooks.slack.com/services/xxx",
|
|
86
95
|
],
|
|
87
96
|
"env": {
|
|
88
97
|
"PYTHONPATH": "${workspaceFolder}"
|
|
@@ -188,6 +197,17 @@ param : Dict = {
|
|
|
188
197
|
"loop_freq_ms" : 500, # reduce this if you need trade faster
|
|
189
198
|
"loops_random_delay_multiplier" : 1, # Add randomness to time between slices are sent off. Set to 1 if no random delay needed.
|
|
190
199
|
|
|
200
|
+
'notification' : {
|
|
201
|
+
'footer' : None,
|
|
202
|
+
|
|
203
|
+
# slack webhook url's for notifications
|
|
204
|
+
'slack' : {
|
|
205
|
+
'info' : { 'webhook_url' : None },
|
|
206
|
+
'critical' : { 'webhook_url' : None },
|
|
207
|
+
'alert' : { 'webhook_url' : None },
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
|
|
191
211
|
'mds' : {
|
|
192
212
|
'topics' : {
|
|
193
213
|
|
|
@@ -268,6 +288,10 @@ def parse_args():
|
|
|
268
288
|
parser.add_argument("--secret", help="Exchange secret", default=None)
|
|
269
289
|
parser.add_argument("--passphrase", help="Exchange passphrase", default=None)
|
|
270
290
|
|
|
291
|
+
parser.add_argument("--slack_info_url", help="Slack webhook url for INFO", default=None)
|
|
292
|
+
parser.add_argument("--slack_critial_url", help="Slack webhook url for CRITICAL", default=None)
|
|
293
|
+
parser.add_argument("--slack_alert_url", help="Slack webhook url for ALERT", default=None)
|
|
294
|
+
|
|
271
295
|
args = parser.parse_args()
|
|
272
296
|
param['gateway_id'] = args.gateway_id
|
|
273
297
|
|
|
@@ -297,6 +321,11 @@ def parse_args():
|
|
|
297
321
|
param['secret'] = args.secret
|
|
298
322
|
param['passphrase'] = args.passphrase
|
|
299
323
|
|
|
324
|
+
param['notification']['slack']['info'] = args.slack_info_url
|
|
325
|
+
param['notification']['slack']['critical'] = args.slack_critial_url
|
|
326
|
+
param['notification']['slack']['alert'] = args.slack_alert_url
|
|
327
|
+
param['notification']['footer'] = f"From gateway {param['gateway_id']}"
|
|
328
|
+
|
|
300
329
|
def init_redis_client() -> StrictRedis:
|
|
301
330
|
redis_client : StrictRedis = StrictRedis(
|
|
302
331
|
host = param['mds']['redis']['host'],
|
|
@@ -389,7 +418,8 @@ async def instantiate_exchange(
|
|
|
389
418
|
"walletAddress" : api_key,
|
|
390
419
|
"privateKey" : secret,
|
|
391
420
|
'enableRateLimit' : True,
|
|
392
|
-
'rateLimit' : rate_limit_ms
|
|
421
|
+
'rateLimit' : rate_limit_ms,
|
|
422
|
+
"verbose": True,
|
|
393
423
|
}
|
|
394
424
|
) # type: ignore
|
|
395
425
|
else:
|
|
@@ -445,7 +475,8 @@ async def execute_one_position(
|
|
|
445
475
|
exchange : AnyExchange,
|
|
446
476
|
position : DivisiblePosition,
|
|
447
477
|
param : Dict,
|
|
448
|
-
executions : Dict[str, Dict[str, Any]]
|
|
478
|
+
executions : Dict[str, Dict[str, Any]],
|
|
479
|
+
notification_params : Dict[str, Any]
|
|
449
480
|
):
|
|
450
481
|
try:
|
|
451
482
|
market : Dict[str, Any] = exchange.markets[position.ticker] if position.ticker in exchange.markets else None # type: ignore
|
|
@@ -710,7 +741,8 @@ async def execute_one_position(
|
|
|
710
741
|
async def work(
|
|
711
742
|
param : Dict,
|
|
712
743
|
exchange : AnyExchange,
|
|
713
|
-
redis_client : StrictRedis
|
|
744
|
+
redis_client : StrictRedis,
|
|
745
|
+
notification_params : Dict[str, Any]
|
|
714
746
|
):
|
|
715
747
|
incoming_orders_topic_regex : str = param['incoming_orders_topic_regex']
|
|
716
748
|
incoming_orders_topic_regex = incoming_orders_topic_regex.replace("$GATEWAY_ID$", param['gateway_id'])
|
|
@@ -758,7 +790,7 @@ async def work(
|
|
|
758
790
|
]
|
|
759
791
|
|
|
760
792
|
start = time.time()
|
|
761
|
-
pending_executions = [ execute_one_position(exchange, position, param, executions) for position in positions ]
|
|
793
|
+
pending_executions = [ execute_one_position(exchange, position, param, executions, notification_params) for position in positions ]
|
|
762
794
|
await asyncio.gather(*pending_executions)
|
|
763
795
|
order_dispatch_elapsed_ms = int((time.time() - start) *1000)
|
|
764
796
|
|
|
@@ -819,6 +851,8 @@ async def main():
|
|
|
819
851
|
secret : str = param['secret']
|
|
820
852
|
passphrase : str = param['passphrase']
|
|
821
853
|
|
|
854
|
+
notification_params : Dict[str, Any] = param['notification']
|
|
855
|
+
|
|
822
856
|
if encrypt_decrypt_with_aws_kms:
|
|
823
857
|
aws_kms_key_id = str(os.getenv('AWS_KMS_KEY_ID'))
|
|
824
858
|
|
|
@@ -843,6 +877,6 @@ async def main():
|
|
|
843
877
|
balances = await exchange.fetch_balance() # type: ignore
|
|
844
878
|
log(f"{param['gateway_id']}: account balances {balances}")
|
|
845
879
|
|
|
846
|
-
await work(param=param, exchange=exchange, redis_client=redis_client)
|
|
880
|
+
await work(param=param, exchange=exchange, redis_client=redis_client, notification_params=notification_params)
|
|
847
881
|
|
|
848
882
|
asyncio.run(main())
|
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List, Union
|
|
|
6
6
|
import logging
|
|
7
7
|
import json
|
|
8
8
|
from redis import StrictRedis
|
|
9
|
+
from sympy import false
|
|
9
10
|
|
|
10
11
|
from ordergateway.client import DivisiblePosition, execute_positions
|
|
11
12
|
from constants import JSON_SERIALIZABLE_TYPES
|
|
@@ -95,7 +96,7 @@ if __name__ == '__main__':
|
|
|
95
96
|
side = 'sell',
|
|
96
97
|
amount = 0.00100,
|
|
97
98
|
leg_room_bps = 5,
|
|
98
|
-
reduce_only=
|
|
99
|
+
reduce_only=False,
|
|
99
100
|
order_type = 'limit',
|
|
100
101
|
slices=1,
|
|
101
102
|
wait_fill_threshold_ms=60000,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, Union
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import numpy as np
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
|
|
7
|
+
from slack_notification_util import slack_dispatch_notification
|
|
8
|
+
|
|
9
|
+
from siglab_py.constants import LogLevel
|
|
10
|
+
|
|
11
|
+
def dispatch_notification(
|
|
12
|
+
title : str,
|
|
13
|
+
message : Union[str, Dict, pd.DataFrame],
|
|
14
|
+
footer : str,
|
|
15
|
+
params : Dict[str, Any],
|
|
16
|
+
log_level : LogLevel = LogLevel.INFO
|
|
17
|
+
):
|
|
18
|
+
if isinstance(message, Dict):
|
|
19
|
+
_message = json.dumps(message, indent=2, separators=(' ', ':'))
|
|
20
|
+
elif isinstance(message, pd.DataFrame):
|
|
21
|
+
_message = tabulate(message, headers='keys', tablefmt='orgtbl') # type: ignore
|
|
22
|
+
else:
|
|
23
|
+
_message = message
|
|
24
|
+
|
|
25
|
+
slack_dispatch_notification(title, _message, footer, params, log_level)
|
|
26
|
+
|
|
27
|
+
if __name__ == '__main__':
|
|
28
|
+
params : Dict[str, Any] = {
|
|
29
|
+
"slack" : {
|
|
30
|
+
"info" : {
|
|
31
|
+
"webhook_url" : "https://hooks.slack.com/services/xxx"
|
|
32
|
+
},
|
|
33
|
+
"critical" : {
|
|
34
|
+
"webhook_url" : "https://hooks.slack.com/services/xxx"
|
|
35
|
+
},
|
|
36
|
+
"alert" : {
|
|
37
|
+
"webhook_url" : "https://hooks.slack.com/services/xxx"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
log_level : LogLevel = LogLevel.CRITICAL
|
|
43
|
+
|
|
44
|
+
title : str = "Test message"
|
|
45
|
+
footer : str = "... some footer .."
|
|
46
|
+
|
|
47
|
+
message1 : str = "Testing 1"
|
|
48
|
+
dispatch_notification(title=title, message=message1, footer=footer, params=params, log_level=log_level)
|
|
49
|
+
|
|
50
|
+
message2 : Dict[str, Any] = {
|
|
51
|
+
'aaa' : 123,
|
|
52
|
+
'bbb' : 456,
|
|
53
|
+
'ccc' : {
|
|
54
|
+
'ddd' : 789
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
dispatch_notification(title=title, message=message2, footer=footer, params=params, log_level=log_level)
|
|
58
|
+
|
|
59
|
+
start_date = pd.to_datetime('2024-01-01 00:00:00')
|
|
60
|
+
datetimes = pd.date_range(start=start_date, periods=20, freq='H')
|
|
61
|
+
np.random.seed(42)
|
|
62
|
+
close_prices = np.random.uniform(80000, 90000, size=20).round(2)
|
|
63
|
+
data : pd.DataFrame = pd.DataFrame({
|
|
64
|
+
'datetime': datetimes,
|
|
65
|
+
'close': close_prices
|
|
66
|
+
})
|
|
67
|
+
data['timestamp_ms'] = data['datetime'].astype('int64')
|
|
68
|
+
message3 = data
|
|
69
|
+
dispatch_notification(title=title, message=message3, footer=footer, params=params, log_level=log_level)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'''
|
|
2
|
+
https://medium.com/@natalia_assad/how-send-a-table-to-slack-using-python-d1a20b08abe0
|
|
3
|
+
'''
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
import json
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from siglab_py.constants import LogLevel
|
|
10
|
+
|
|
11
|
+
def slack_dispatch_notification(
|
|
12
|
+
title : str,
|
|
13
|
+
message : str,
|
|
14
|
+
footer : str,
|
|
15
|
+
params : Dict[str, Any],
|
|
16
|
+
log_level : LogLevel = LogLevel.INFO
|
|
17
|
+
):
|
|
18
|
+
slack_params = params['slack']
|
|
19
|
+
|
|
20
|
+
if log_level==LogLevel.INFO or log_level==LogLevel.DEBUG:
|
|
21
|
+
webhook_url = slack_params['info']['webhook_url']
|
|
22
|
+
elif log_level==LogLevel.ERROR or log_level==LogLevel.CRITICAL:
|
|
23
|
+
webhook_url = slack_params['critical']['webhook_url']
|
|
24
|
+
elif log_level==LogLevel.ERROR or log_level==LogLevel.CRITICAL:
|
|
25
|
+
webhook_url = slack_params['alert']['webhook_url']
|
|
26
|
+
else:
|
|
27
|
+
webhook_url = slack_params['info']['webhook_url']
|
|
28
|
+
|
|
29
|
+
slack_data = {
|
|
30
|
+
"username": "NotificationBot",
|
|
31
|
+
"type": "section",
|
|
32
|
+
"blocks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "header",
|
|
35
|
+
"text": {
|
|
36
|
+
"type": "plain_text",
|
|
37
|
+
"text": f"{title}"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "section",
|
|
42
|
+
"text": {
|
|
43
|
+
"type": "mrkdwn",
|
|
44
|
+
"text": message
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "section",
|
|
49
|
+
"text": {
|
|
50
|
+
"type": "plain_text",
|
|
51
|
+
"text": footer
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
byte_length = str(sys.getsizeof(slack_data, 2000))
|
|
58
|
+
headers = {'Content-Type': "application/json", 'Content-Length': byte_length}
|
|
59
|
+
response = requests.post(webhook_url, data=json.dumps(slack_data), headers=headers)
|
|
60
|
+
if response.status_code != 200:
|
|
61
|
+
raise Exception(response.status_code, response.text)
|
|
@@ -34,4 +34,6 @@ siglab_py/util/__init__.py
|
|
|
34
34
|
siglab_py/util/analytic_util.py
|
|
35
35
|
siglab_py/util/aws_util.py
|
|
36
36
|
siglab_py/util/market_data_util.py
|
|
37
|
-
siglab_py/util/
|
|
37
|
+
siglab_py/util/notification_util.py
|
|
38
|
+
siglab_py/util/retry_util.py
|
|
39
|
+
siglab_py/util/slack_notification_util.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/aggregated_orderbook_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/ccxt_candles_ta_to_csv.py
RENAMED
|
File without changes
|
|
File without changes
|
{siglab_py-0.2.1 → siglab_py-0.2.2}/siglab_py/market_data_providers/futu_candles_ta_to_csv.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|