Skip to content

Commit 3f88a74

Browse files
author
方佳
committed
Merge branch 'main_merge_0508' into 'main'
feat: Support for Webull Hong Kong Institutional Clients. See merge request webull/openapi-python-sdk!11
2 parents fcce60f + af8744f commit 3f88a74

File tree

32 files changed

+667
-181
lines changed

32 files changed

+667
-181
lines changed

webull-python-sdk-core/webullsdkcore/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "0.1.11"
1+
__version__ = "0.1.12"
22

33
import logging
44

webull-python-sdk-core/webullsdkcore/client.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@
4545
import platform
4646
import json
4747
from webullsdkcore import compat
48+
from webullsdkcore.common.customer_type import CustomerType
4849
from webullsdkcore.exception import error_code
4950
from webullsdkcore.exception.exceptions import ClientException, ServerException
51+
from webullsdkcore.headers import WB_USER_ID
5052
from webullsdkcore.retry.retry_policy_context import RetryPolicyContext
5153
from webullsdkcore.vendored.requests import codes
5254
from webullsdkcore.vendored.requests.adapters import HTTPAdapter
@@ -79,19 +81,22 @@ def __init__(
7981
app_secret=None,
8082
region_id=DEFAULT_REGION_ID,
8183
user_agent=None,
82-
port=DEFAULT_PORT,
84+
port=DEFAULT_PORT,
85+
customer_type=CustomerType.INDIVIDUAL,
8386
connect_timeout=None,
8487
timeout=None,
8588
credential=None,
8689
verify=None,
8790
auto_retry=False,
88-
max_retry_num=None
91+
max_retry_num=None,
92+
user_id=None
8993
):
9094
self._app_key = app_key
9195
self._app_secret = app_secret
9296
self._region_id = region_id
9397
self._user_agent = user_agent
9498
self._port = port
99+
self._customer_type = customer_type
95100
self._connect_timeout = connect_timeout
96101
self._read_timeout = timeout
97102
self._extra_user_agent = {}
@@ -105,6 +110,7 @@ def __init__(
105110
self._endpoint_resolver = DefaultEndpointResolver(self)
106111
self._max_retry_num = max_retry_num
107112
self._auto_retry = auto_retry
113+
self._user_id = user_id
108114
if self._auto_retry:
109115
self._retry_policy = retry_policy.get_default_retry_policy(self._max_retry_num)
110116
else:
@@ -125,6 +131,12 @@ def get_user_agent(self):
125131
def get_verify(self):
126132
return self._verify
127133

134+
def set_customer_type(self):
135+
return self._customer_type
136+
137+
def set_user_id(self):
138+
return self._user_id
139+
128140
def set_user_agent(self, agent):
129141
"""
130142
User agent set to client will overwrite the request setting.
@@ -237,6 +249,10 @@ def _implementation_of_do_action(self, request, signer=None):
237249
if not isinstance(request, BaseRequest):
238250
raise ClientException(error_code.SDK_INVALID_REQUEST)
239251
request.add_header('Accept-Encoding', 'gzip')
252+
253+
if self._user_id:
254+
request.add_header(WB_USER_ID, self._user_id)
255+
240256
if request.endpoint:
241257
endpoint = request.endpoint
242258
else:
@@ -320,7 +336,8 @@ def _get_request_connect_timeout(self, request):
320336

321337
def _resolve_endpoint(self, request):
322338
resolve_request = ResolveEndpointRequest(
323-
self._region_id
339+
self._region_id,
340+
self._customer_type
324341
)
325342
return self._endpoint_resolver.resolve(resolve_request)
326343

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from enum import Enum
2+
from typing import Optional
3+
4+
5+
class CustomerType(Enum):
6+
INSTITUTION = "institution"
7+
INDIVIDUAL = "individual"
8+
9+
@classmethod
10+
def of(cls, name: str) -> Optional['CustomerType']:
11+
for item in cls:
12+
if item.name == name:
13+
return item
14+
return None

webull-python-sdk-core/webullsdkcore/data/endpoints.json

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,35 @@
33
"regions": ["us", "hk", "jp"],
44
"region_mapping": {
55
"us": {
6-
"api": "api.webull.com",
7-
"quotes-api": "usquotes-api.webullfintech.com",
8-
"events-api": "events-api.webull.com"
6+
"individual": {
7+
"api": "api.webull.com",
8+
"quotes-api": "usquotes-api.webullfintech.com",
9+
"events-api": "events-api.webull.com"
10+
},
11+
"institution": {
12+
"api": "",
13+
"quotes-api": "",
14+
"events-api": ""
15+
}
916
},
1017
"hk": {
11-
"api": "api.webull.hk",
12-
"quotes-api": "quotes-api.webull.hk",
13-
"events-api": "events-api.webull.hk"
18+
"individual": {
19+
"api": "api.webull.hk",
20+
"quotes-api": "quotes-api.webull.hk",
21+
"events-api": "events-api.webull.hk"
22+
},
23+
"institution": {
24+
"api": "inst-api.webull.hk",
25+
"quotes-api": "quotes-api.webull.hk",
26+
"events-api": "events-inst-api.webull.hk"
27+
}
1428
},
1529
"jp": {
16-
"api": "api.webull.co.jp",
17-
"quotes-api": "",
18-
"events-api": "events-api.webull.co.jp"
30+
"individual": {
31+
"api": "api.webull.co.jp",
32+
"quotes-api": "",
33+
"events-api": "events-api.webull.co.jp"
34+
}
1935
}
2036
}
2137
}

webull-python-sdk-core/webullsdkcore/endpoint/default_endpoint_resolver.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@
4545
from webullsdkcore.endpoint.user_customized_endpoint_resolver import UserCustomizedEndpointResolver
4646

4747
class DefaultEndpointResolver(EndpointResolver):
48-
def __init__(self, client, user_config=None):
48+
def __init__(self, customer_type, user_config=None):
49+
self._customer_type = customer_type
4950
self._user_customized_endpoint_resolver = UserCustomizedEndpointResolver()
5051
endpoint_resolvers = [
5152
self._user_customized_endpoint_resolver,
52-
LocalConfigRegionalEndpointResolver(user_config)
53+
LocalConfigRegionalEndpointResolver(user_config, customer_type=self._customer_type)
5354
]
5455
self._resolver = ChainedEndpointResolver(endpoint_resolvers)
5556

webull-python-sdk-core/webullsdkcore/endpoint/local_config_regional_endpoint_resolver.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@
4343
import os
4444
import webullsdkcore
4545
from webullsdkcore.endpoint import EndpointResolver
46+
from webullsdkcore.common.customer_type import CustomerType
4647

4748
ENDPOINT_JSON = os.path.join(os.path.dirname(webullsdkcore.__file__), "data", "endpoints.json")
4849

4950

5051
class LocalConfigRegionalEndpointResolver(EndpointResolver):
51-
def __init__(self, config_json_str=None):
52+
def __init__(self, config_json_str=None, customer_type=None):
5253
EndpointResolver.__init__(self)
54+
self._customer_type = customer_type
5355
if config_json_str:
5456
obj = json.loads(config_json_str)
5557
else:
@@ -68,7 +70,12 @@ def resolve(self, request):
6870
if request.region_id:
6971
region_code_mapping = self._region_mapping.get(request.region_id)
7072
else:
71-
region_code_mapping = self._region_code_mapping.get(self._default_region)
73+
region_code_mapping = self._region_mapping.get(self._default_region)
74+
7275
if region_code_mapping:
73-
return region_code_mapping.get(request.api_type)
76+
# Select the appropriate API endpoint based on client type
77+
customer_type = self._customer_type.set_customer_type() if self._customer_type else CustomerType.INDIVIDUAL
78+
customer_type_key = "institution" if customer_type.value == CustomerType.INSTITUTION.value else "individual"
79+
customer_endpoints = region_code_mapping.get(customer_type_key, {})
80+
return customer_endpoints.get(request.api_type)
7481
return None

webull-python-sdk-core/webullsdkcore/endpoint/resolver_endpoint_request.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
from webullsdkcore.common.api_type import DEFAULT as HTTP_API_TYPE
4343
class ResolveEndpointRequest(object):
44-
def __init__(self, region_id=None, api_type=HTTP_API_TYPE):
44+
def __init__(self, region_id=None, customer_type=None, api_type=HTTP_API_TYPE):
4545
self.region_id = region_id
46-
self.api_type = api_type
46+
self.api_type = api_type
47+
self.customer_type = customer_type

webull-python-sdk-core/webullsdkcore/headers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
NONCE = "x-signature-nonce"
4848
TIMESTAMP = "x-timestamp"
4949
VERSION = "x-version"
50+
WB_USER_ID = "wb-user-id"
5051

5152
NATIVE_HOST = "Host"
5253
NATIVE_CONTENT_TYPE = "Content-Type"

webull-python-sdk-demos/setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
LONG_DESCRIPTION = fp.read()
1616

1717
requires = [
18-
"webull-python-sdk-mdata==0.1.11",
19-
"webull-python-sdk-trade==0.1.11"
18+
"webull-python-sdk-mdata==0.1.12",
19+
"webull-python-sdk-trade==0.1.12"
2020
]
2121

2222
setup_args = {

webull-python-sdk-demos/tests/events-core/test_subscribe.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,25 @@
1414

1515
import unittest
1616

17+
from webullsdkcore.common.customer_type import CustomerType
1718
from webullsdktrade.events.types import ORDER_STATUS_CHANGED, EVENT_TYPE_ORDER
1819
from webullsdktradeeventscore.events_client import EventsClient
1920

2021
your_app_key = "<your_app_key>"
2122
your_app_secret = "<your_app_secret>"
2223
account_id = "<your_account_id>"
2324
region_id = "hk"
25+
customer_type = CustomerType.INSTITUTION
2426

2527
endpoint = "<event_api_endpoint>"
2628

2729

2830
class TestGrpcSubscribe(unittest.TestCase):
2931
def test_subscribe(self):
3032
# Create EventsClient instance
31-
events_client = EventsClient(your_app_key, your_app_secret, region_id)
33+
events_client = EventsClient(your_app_key, your_app_secret, region_id, customer_type=customer_type)
3234
# For non production environment, you need to set the domain name of the subscription service through eventsclient. For example, the domain name of the UAT environment is set here
33-
events_client = EventsClient(your_app_key, your_app_secret, region_id,
34-
host=endpoint)
35+
# events_client = EventsClient(your_app_key, your_app_secret, region_id, host=endpoint, customer_type=customer_type)
3536

3637
# Set the callback function when the event data is received.
3738
# The data of order status change is printed here

0 commit comments

Comments
 (0)