webull-openapi-python-sdk 1.0.6__py3-none-any.whl → 1.0.8__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.
- samples/__init__.py +1 -1
- samples/account/__init__.py +1 -0
- samples/account/account_client.py +42 -0
- samples/assets/__init__.py +1 -0
- samples/assets/assets_client.py +47 -0
- samples/data/data_client.py +7 -0
- samples/data/data_streaming_client.py +5 -0
- samples/data/data_streaming_client_async.py +5 -0
- samples/order/__init__.py +1 -0
- samples/order/order_option_client.py +92 -0
- samples/order/order_stock_client.py +95 -0
- samples/trade/trade_client.py +6 -0
- samples/trade/trade_client_v2.py +6 -1
- samples/trade/trade_client_v3.py +38 -1
- webull/__init__.py +1 -1
- webull/core/__init__.py +1 -1
- webull/core/client.py +7 -0
- webull/core/http/initializer/client_initializer.py +1 -1
- webull/core/http/initializer/token/token_manager.py +5 -9
- webull/core/http/initializer/token/token_storage.py +140 -0
- webull/data/__init__.py +1 -1
- webull/data/internal/quotes_client.py +6 -0
- webull/data/quotes/subscribe/message_pb2.py +12 -12
- webull/data/quotes/subscribe/snapshot_result.py +78 -14
- webull/trade/__init__.py +1 -1
- webull/trade/request/v3/batch_place_order_request.py +52 -0
- webull/trade/trade/v3/order_opration_v3.py +13 -0
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/METADATA +1 -1
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/RECORD +33 -24
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/WHEEL +0 -0
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/licenses/LICENSE +0 -0
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/licenses/NOTICE +0 -0
- {webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/top_level.txt +0 -0
samples/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '1.0.
|
|
1
|
+
__version__ = '1.0.8'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding=utf-8
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import uuid
|
|
17
|
+
from time import sleep
|
|
18
|
+
|
|
19
|
+
from webull.core.client import ApiClient
|
|
20
|
+
from webull.trade.trade_client import TradeClient
|
|
21
|
+
|
|
22
|
+
optional_api_endpoint = "<api_endpoint>"
|
|
23
|
+
your_app_key = "<your_app_key>"
|
|
24
|
+
your_app_secret = "<your_app_secret>"
|
|
25
|
+
region_id = "<region_id>" # hk or us
|
|
26
|
+
account_id = "<your_account_id>"
|
|
27
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
28
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
29
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
30
|
+
# token_dir = "<your_token_dir>"
|
|
31
|
+
# api_client.set_token_dir(token_dir)
|
|
32
|
+
|
|
33
|
+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
34
|
+
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
trade_client = TradeClient(api_client)
|
|
39
|
+
|
|
40
|
+
res = trade_client.account_v2.get_account_list()
|
|
41
|
+
if res.status_code == 200:
|
|
42
|
+
print('get account list:', res.json())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding=utf-8
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import uuid
|
|
17
|
+
from time import sleep
|
|
18
|
+
|
|
19
|
+
from webull.core.client import ApiClient
|
|
20
|
+
from webull.trade.trade_client import TradeClient
|
|
21
|
+
|
|
22
|
+
optional_api_endpoint = "<api_endpoint>"
|
|
23
|
+
your_app_key = "<your_app_key>"
|
|
24
|
+
your_app_secret = "<your_app_secret>"
|
|
25
|
+
region_id = "<region_id>" # hk or us
|
|
26
|
+
account_id = "<your_account_id>"
|
|
27
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
28
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
29
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
30
|
+
# token_dir = "<your_token_dir>"
|
|
31
|
+
# api_client.set_token_dir(token_dir)
|
|
32
|
+
|
|
33
|
+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
34
|
+
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if __name__ == '__main__':
|
|
38
|
+
trade_client = TradeClient(api_client)
|
|
39
|
+
|
|
40
|
+
res = trade_client.account_v2.get_account_balance(account_id)
|
|
41
|
+
if res.status_code == 200:
|
|
42
|
+
print('get account balance info:', res.json())
|
|
43
|
+
|
|
44
|
+
res = trade_client.account_v2.get_account_position(account_id)
|
|
45
|
+
if res.status_code == 200:
|
|
46
|
+
print('get account positions info:', res.json())
|
|
47
|
+
|
samples/data/data_client.py
CHANGED
|
@@ -24,9 +24,16 @@ optional_api_endpoint = "<api_endpoint>"
|
|
|
24
24
|
your_app_key = "<your_app_key>"
|
|
25
25
|
your_app_secret = "<your_app_secret>"
|
|
26
26
|
region_id = "<region_id>"
|
|
27
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
28
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
29
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
30
|
+
# token_dir = "<your_token_dir>"
|
|
31
|
+
# api_client.set_token_dir(token_dir)
|
|
32
|
+
|
|
27
33
|
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
28
34
|
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
29
35
|
|
|
36
|
+
|
|
30
37
|
if __name__ == '__main__':
|
|
31
38
|
data_client = DataClient(api_client)
|
|
32
39
|
|
|
@@ -27,6 +27,11 @@ your_app_secret = "</your_app_secret>"
|
|
|
27
27
|
optional_api_endpoint = "</optional_quotes_endpoint>"
|
|
28
28
|
optional_quotes_endpoint = "</optional_quotes_endpoint>"
|
|
29
29
|
region_id = '<region_id>'
|
|
30
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
31
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
32
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
33
|
+
# token_dir = "<your_token_dir>"
|
|
34
|
+
# data_streaming_client.set_token_dir(token_dir)
|
|
30
35
|
|
|
31
36
|
session_id = uuid.uuid4().hex
|
|
32
37
|
data_streaming_client = DataStreamingClient(your_app_key, your_app_secret, region_id, session_id,
|
|
@@ -27,6 +27,11 @@ your_app_secret = "</your_app_secret>"
|
|
|
27
27
|
optional_api_endpoint = "</optional_quotes_endpoint>"
|
|
28
28
|
optional_quotes_endpoint = "</optional_quotes_endpoint>"
|
|
29
29
|
region_id = '<region_id>'
|
|
30
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
31
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
32
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
33
|
+
# token_dir = "<your_token_dir>"
|
|
34
|
+
# data_streaming_client.set_token_dir(token_dir)
|
|
30
35
|
|
|
31
36
|
session_id = uuid.uuid4().hex
|
|
32
37
|
data_streaming_client = DataStreamingClient(your_app_key, your_app_secret, region_id, session_id,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# coding=utf-8
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import uuid
|
|
16
|
+
from webull.core.client import ApiClient
|
|
17
|
+
from webull.trade.trade_client import TradeClient
|
|
18
|
+
|
|
19
|
+
optional_api_endpoint = "<api_endpoint>"
|
|
20
|
+
your_app_key = "<your_app_key>"
|
|
21
|
+
your_app_secret = "<your_app_secret>"
|
|
22
|
+
region_id = "<region_id>" # hk or us
|
|
23
|
+
account_id = "<your_account_id>"
|
|
24
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
25
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
26
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
27
|
+
# token_dir = "<your_token_dir>"
|
|
28
|
+
# api_client.set_token_dir(token_dir)
|
|
29
|
+
|
|
30
|
+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
31
|
+
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
32
|
+
|
|
33
|
+
if __name__ == '__main__':
|
|
34
|
+
trade_client = TradeClient(api_client)
|
|
35
|
+
|
|
36
|
+
# Options
|
|
37
|
+
# For option order inquiries, please use the V2 query interface: api.order_v2.get_order_detail(account_id, client_order_id).
|
|
38
|
+
client_order_id = uuid.uuid4().hex
|
|
39
|
+
option_new_orders = [
|
|
40
|
+
{
|
|
41
|
+
"client_order_id": client_order_id,
|
|
42
|
+
"combo_type": "NORMAL",
|
|
43
|
+
"order_type": "LIMIT",
|
|
44
|
+
"quantity": "1",
|
|
45
|
+
"limit_price": "21.25",
|
|
46
|
+
"option_strategy": "SINGLE",
|
|
47
|
+
"side": "BUY",
|
|
48
|
+
"time_in_force": "GTC",
|
|
49
|
+
"entrust_type": "QTY",
|
|
50
|
+
"legs": [
|
|
51
|
+
{
|
|
52
|
+
"side": "BUY",
|
|
53
|
+
"quantity": "1",
|
|
54
|
+
"symbol": "TSLA",
|
|
55
|
+
"strike_price": "400",
|
|
56
|
+
"option_expire_date": "2025-12-26",
|
|
57
|
+
"instrument_type": "OPTION",
|
|
58
|
+
"option_type": "CALL",
|
|
59
|
+
"market": "US"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
# preview
|
|
66
|
+
res = trade_client.order_v2.preview_option(account_id, option_new_orders)
|
|
67
|
+
if res.status_code == 200:
|
|
68
|
+
print("preview option res:", res.json())
|
|
69
|
+
|
|
70
|
+
# place
|
|
71
|
+
res = trade_client.order_v2.place_option(account_id, option_new_orders)
|
|
72
|
+
if res.status_code == 200:
|
|
73
|
+
print("place option res:" , res.json())
|
|
74
|
+
|
|
75
|
+
option_modify_orders = [
|
|
76
|
+
{
|
|
77
|
+
"client_order_id": client_order_id,
|
|
78
|
+
"quantity": "2",
|
|
79
|
+
"limit_price": "21.25"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
res = trade_client.order_v2.replace_option(account_id, option_modify_orders)
|
|
83
|
+
if res.status_code == 200:
|
|
84
|
+
print("Replace option order res:" , res.json())
|
|
85
|
+
|
|
86
|
+
res = trade_client.order_v2.cancel_option(account_id, client_order_id)
|
|
87
|
+
if res.status_code == 200:
|
|
88
|
+
print("Cancel option order res:" , res.json())
|
|
89
|
+
|
|
90
|
+
res = trade_client.order_v2.get_order_detail(account_id, client_order_id)
|
|
91
|
+
if res.status_code == 200:
|
|
92
|
+
print("Option order detail order res:" , res.json())
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import uuid
|
|
16
|
+
from webull.core.client import ApiClient
|
|
17
|
+
from webull.trade.trade_client import TradeClient
|
|
18
|
+
|
|
19
|
+
optional_api_endpoint = "<api_endpoint>"
|
|
20
|
+
your_app_key = "<your_app_key>"
|
|
21
|
+
your_app_secret = "<your_app_secret>"
|
|
22
|
+
region_id = "<region_id>" # hk or us
|
|
23
|
+
account_id = "<your_account_id>"
|
|
24
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
25
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
26
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
27
|
+
# token_dir = "<your_token_dir>"
|
|
28
|
+
# api_client.set_token_dir(token_dir)
|
|
29
|
+
|
|
30
|
+
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
31
|
+
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == '__main__':
|
|
35
|
+
trade_client = TradeClient(api_client)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# simple order
|
|
39
|
+
client_order_id = uuid.uuid4().hex
|
|
40
|
+
print('client order id:', client_order_id)
|
|
41
|
+
new_simple_orders = [
|
|
42
|
+
{
|
|
43
|
+
"client_order_id": client_order_id,
|
|
44
|
+
"symbol": "BULL",
|
|
45
|
+
"instrument_type": "EQUITY",
|
|
46
|
+
"market": "US",
|
|
47
|
+
"order_type": "LIMIT",
|
|
48
|
+
"limit_price": "26",
|
|
49
|
+
"quantity": "1",
|
|
50
|
+
"support_trading_session": "CORE",
|
|
51
|
+
"side": "BUY",
|
|
52
|
+
"time_in_force": "DAY",
|
|
53
|
+
"entrust_type": "QTY"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
new_hk_stock_simple_orders = [
|
|
57
|
+
{
|
|
58
|
+
"client_order_id": client_order_id,
|
|
59
|
+
"symbol": "00700",
|
|
60
|
+
"instrument_type": "EQUITY",
|
|
61
|
+
"market": "HK",
|
|
62
|
+
"order_type": "ENHANCED_LIMIT",
|
|
63
|
+
"limit_price": "612",
|
|
64
|
+
"quantity": "100",
|
|
65
|
+
"side": "BUY",
|
|
66
|
+
"time_in_force": "DAY",
|
|
67
|
+
"entrust_type": "QTY"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
res = trade_client.order_v2.preview_order(account_id, new_simple_orders)
|
|
71
|
+
if res.status_code == 200:
|
|
72
|
+
print('preview order res:', res.json())
|
|
73
|
+
|
|
74
|
+
res = trade_client.order_v2.place_order(account_id, new_simple_orders)
|
|
75
|
+
if res.status_code == 200:
|
|
76
|
+
print('place order res:', res.json())
|
|
77
|
+
|
|
78
|
+
modify_simple_orders = [
|
|
79
|
+
{
|
|
80
|
+
"client_order_id": client_order_id,
|
|
81
|
+
"quantity": "2",
|
|
82
|
+
"limit_price": "25"
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
res = trade_client.order_v2.replace_order(account_id, modify_simple_orders)
|
|
86
|
+
if res.status_code == 200:
|
|
87
|
+
print('replace order res:', res.json())
|
|
88
|
+
|
|
89
|
+
res = trade_client.order_v2.cancel_order(account_id, client_order_id)
|
|
90
|
+
if res.status_code == 200:
|
|
91
|
+
print('cancel order res:', res.json())
|
|
92
|
+
|
|
93
|
+
res = trade_client.order_v2.get_order_detail(account_id, client_order_id)
|
|
94
|
+
if res.status_code == 200:
|
|
95
|
+
print('order detail:', res.json())
|
samples/trade/trade_client.py
CHANGED
|
@@ -25,6 +25,12 @@ your_app_key = "<your_app_key>"
|
|
|
25
25
|
your_app_secret = "<your_app_secret>"
|
|
26
26
|
region_id = "<region_id>"
|
|
27
27
|
account_id = "<your_account_id>"
|
|
28
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
29
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
30
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
31
|
+
# token_dir = "<your_token_dir>"
|
|
32
|
+
# api_client.set_token_dir(token_dir)
|
|
33
|
+
|
|
28
34
|
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
29
35
|
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
30
36
|
|
samples/trade/trade_client_v2.py
CHANGED
|
@@ -24,10 +24,15 @@ your_app_key = "<your_app_key>"
|
|
|
24
24
|
your_app_secret = "<your_app_secret>"
|
|
25
25
|
region_id = "<region_id>"
|
|
26
26
|
account_id = "<your_account_id>"
|
|
27
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
28
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
29
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
30
|
+
# token_dir = "<your_token_dir>"
|
|
31
|
+
# api_client.set_token_dir(token_dir)
|
|
32
|
+
|
|
27
33
|
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
28
34
|
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
29
35
|
|
|
30
|
-
|
|
31
36
|
if __name__ == '__main__':
|
|
32
37
|
trade_client = TradeClient(api_client)
|
|
33
38
|
|
samples/trade/trade_client_v3.py
CHANGED
|
@@ -24,6 +24,12 @@ your_app_key = "<your_app_key>"
|
|
|
24
24
|
your_app_secret = "<your_app_secret>"
|
|
25
25
|
region_id = "<region_id>"
|
|
26
26
|
account_id = "<your_account_id>"
|
|
27
|
+
# The token_dir parameter can be used to specify the directory for storing the 2FA token. Both absolute and relative paths are supported and this option has the highest priority.
|
|
28
|
+
# Alternatively, the storage directory can be configured via an environment variable with the key WEBULL_OPENAPI_TOKEN_DIR, which also supports both absolute and relative paths.
|
|
29
|
+
# If neither is specified, the default configuration will be used, and the token will be stored at conf/token.txt under the current working directory.
|
|
30
|
+
# token_dir = "<your_token_dir>"
|
|
31
|
+
# api_client.set_token_dir(token_dir)
|
|
32
|
+
|
|
27
33
|
api_client = ApiClient(your_app_key, your_app_secret, region_id)
|
|
28
34
|
api_client.add_endpoint(region_id, optional_api_endpoint)
|
|
29
35
|
|
|
@@ -202,7 +208,38 @@ if __name__ == '__main__':
|
|
|
202
208
|
if res.status_code == 200:
|
|
203
209
|
print('get master order detail res:', res.json())
|
|
204
210
|
|
|
205
|
-
|
|
211
|
+
# batch place order
|
|
212
|
+
batch_place_orders = [
|
|
213
|
+
{
|
|
214
|
+
"combo_type": "NORMAL",
|
|
215
|
+
"client_order_id": uuid.uuid4().hex,
|
|
216
|
+
"instrument_type": "EQUITY",
|
|
217
|
+
"market": "US",
|
|
218
|
+
"symbol": "AAPL",
|
|
219
|
+
"order_type": "MARKET",
|
|
220
|
+
"entrust_type": "QTY",
|
|
221
|
+
"support_trading_session": "CORE",
|
|
222
|
+
"time_in_force": "DAY",
|
|
223
|
+
"side": "BUY",
|
|
224
|
+
"quantity": "1"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"combo_type": "NORMAL",
|
|
228
|
+
"client_order_id": uuid.uuid4().hex,
|
|
229
|
+
"instrument_type": "EQUITY",
|
|
230
|
+
"market": "US",
|
|
231
|
+
"symbol": "TESL",
|
|
232
|
+
"order_type": "MARKET",
|
|
233
|
+
"entrust_type": "QTY",
|
|
234
|
+
"support_trading_session": "CORE",
|
|
235
|
+
"time_in_force": "DAY",
|
|
236
|
+
"side": "BUY",
|
|
237
|
+
"quantity": "1"
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
res = trade_client.order_v3.batch_place_order(account_id, batch_place_orders)
|
|
241
|
+
if res.status_code == 200:
|
|
242
|
+
print('batch place normal equity order res:', res.json())
|
|
206
243
|
|
|
207
244
|
|
|
208
245
|
# ============================================================
|
webull/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '1.0.
|
|
1
|
+
__version__ = '1.0.8'
|
webull/core/__init__.py
CHANGED
webull/core/client.py
CHANGED
|
@@ -122,6 +122,7 @@ class ApiClient:
|
|
|
122
122
|
self._token_check_duration_seconds = token_check_duration_seconds
|
|
123
123
|
validation.assert_integer_positive(token_check_interval_seconds, "token_check_interval_seconds")
|
|
124
124
|
self._token_check_interval_seconds = token_check_interval_seconds
|
|
125
|
+
self._token_dir = None
|
|
125
126
|
|
|
126
127
|
def get_region_id(self):
|
|
127
128
|
return self._region_id
|
|
@@ -164,6 +165,12 @@ class ApiClient:
|
|
|
164
165
|
def get_token_check_interval_seconds(self):
|
|
165
166
|
return self._token_check_interval_seconds
|
|
166
167
|
|
|
168
|
+
def set_token_dir(self, token_dir):
|
|
169
|
+
self._token_dir = token_dir
|
|
170
|
+
|
|
171
|
+
def get_token_dir(self):
|
|
172
|
+
return self._token_dir
|
|
173
|
+
|
|
167
174
|
@staticmethod
|
|
168
175
|
def user_agent_header():
|
|
169
176
|
base = '%s (%s %s;%s)' \
|
|
@@ -34,11 +34,10 @@
|
|
|
34
34
|
# coding=utf-8
|
|
35
35
|
|
|
36
36
|
import logging
|
|
37
|
-
import os
|
|
38
37
|
import time
|
|
39
|
-
from pathlib import Path
|
|
40
38
|
|
|
41
39
|
from webull.core import compat
|
|
40
|
+
from webull.core.http.initializer.token.token_storage import TokenStorage
|
|
42
41
|
from webull.core.utils import desensitize
|
|
43
42
|
from webull.core.exception.exceptions import ClientException
|
|
44
43
|
from webull.core.http.initializer.token.bean.access_token import AccessToken
|
|
@@ -47,13 +46,10 @@ from webull.core.http.initializer.token.token_operation import TokenOperation
|
|
|
47
46
|
logger = logging.getLogger(__name__)
|
|
48
47
|
|
|
49
48
|
class TokenManager:
|
|
50
|
-
TOKEN_FILE_NAME = "conf/token.txt"
|
|
51
|
-
CONF_ENV_TOKEN_DIR = os.getenv("WEBULL_OPENAPI_TOKEN_DIR")
|
|
52
|
-
DEFAULT_ENV_TOKEN_DIR = os.getcwd()
|
|
53
49
|
|
|
54
|
-
def __init__(self):
|
|
55
|
-
|
|
56
|
-
self.token_file_path =
|
|
50
|
+
def __init__(self, custom_token_dir=None):
|
|
51
|
+
token_storage = TokenStorage(custom_token_dir=custom_token_dir)
|
|
52
|
+
self.token_file_path = token_storage.get_token_file_path()
|
|
57
53
|
|
|
58
54
|
def init_token(self, api_client):
|
|
59
55
|
local_access_token = self.load_token_from_local()
|
|
@@ -100,7 +96,7 @@ class TokenManager:
|
|
|
100
96
|
def save_token_to_local(self, server_access_token):
|
|
101
97
|
try:
|
|
102
98
|
logger.info("save_token_to_local writing token to local file. token:%s expires:%s status:%s",
|
|
103
|
-
server_access_token.get("token"), server_access_token.get("expires"), server_access_token.get("status"))
|
|
99
|
+
desensitize.desensitize_token(server_access_token.get("token")), server_access_token.get("expires"), server_access_token.get("status"))
|
|
104
100
|
self.token_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
105
101
|
with open(self.token_file_path, "w", encoding="utf-8") as f:
|
|
106
102
|
f.write(server_access_token.get("token") + "\n")
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
16
|
+
# or more contributor license agreements. See the NOTICE file
|
|
17
|
+
# distributed with this work for additional information
|
|
18
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
19
|
+
# to you under the Apache License, Version 2.0 (the
|
|
20
|
+
# "License"); you may not use this file except in compliance
|
|
21
|
+
# with the License. You may obtain a copy of the License at
|
|
22
|
+
#
|
|
23
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
24
|
+
#
|
|
25
|
+
#
|
|
26
|
+
#
|
|
27
|
+
# Unless required by applicable law or agreed to in writing,
|
|
28
|
+
# software distributed under the License is distributed on an
|
|
29
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
30
|
+
# KIND, either express or implied. See the License for the
|
|
31
|
+
# specific language governing permissions and limitations
|
|
32
|
+
# under the License.
|
|
33
|
+
|
|
34
|
+
# coding=utf-8
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
import os
|
|
38
|
+
import platform
|
|
39
|
+
import re
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
from webull.core.exception.exceptions import ClientException
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
class TokenStorage:
|
|
47
|
+
|
|
48
|
+
DEFAULT_TOKEN_PATH = "conf"
|
|
49
|
+
DEFAULT_TOKEN_FILE = "token.txt"
|
|
50
|
+
DEFAULT_ENV_TOKEN_DIR = "WEBULL_OPENAPI_TOKEN_DIR"
|
|
51
|
+
|
|
52
|
+
_INVALID_CHARS = {
|
|
53
|
+
"windows": re.compile(r'[<>"|?*]'),
|
|
54
|
+
"posix": re.compile(r'[\0]')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def __init__(self, custom_token_dir=None):
|
|
58
|
+
# Resolve the final storage directory (priority: custom direction > environment variable > default).
|
|
59
|
+
self.storage_token_dir = self._resolve_dir(custom_token_dir)
|
|
60
|
+
# Full path validation
|
|
61
|
+
self._validate_path()
|
|
62
|
+
# Ensure the directory exists
|
|
63
|
+
self._ensure_dir_exists()
|
|
64
|
+
# Full path
|
|
65
|
+
self.token_file = self.storage_token_dir / self.DEFAULT_TOKEN_FILE
|
|
66
|
+
logger.info("storage_token initialized path:%s.",self.token_file)
|
|
67
|
+
# Check file exists
|
|
68
|
+
self._check_file_exists()
|
|
69
|
+
|
|
70
|
+
def _resolve_dir(self, custom_token_dir=None) -> Path:
|
|
71
|
+
if custom_token_dir and custom_token_dir.strip():
|
|
72
|
+
# custom direction
|
|
73
|
+
raw_dir = custom_token_dir.strip()
|
|
74
|
+
logger.info("storage_token uses the custom configuration, token_dir:%s.", raw_dir)
|
|
75
|
+
elif env_dir := os.getenv(self.DEFAULT_ENV_TOKEN_DIR):
|
|
76
|
+
# environment variable
|
|
77
|
+
raw_dir = env_dir.strip()
|
|
78
|
+
logger.info("storage_token uses environment variable configuration, %s:%s.", self.DEFAULT_ENV_TOKEN_DIR, raw_dir)
|
|
79
|
+
else:
|
|
80
|
+
# default
|
|
81
|
+
raw_dir = self.DEFAULT_TOKEN_PATH
|
|
82
|
+
logger.info("storage_token uses the default configuration, %s.", raw_dir)
|
|
83
|
+
|
|
84
|
+
# Resolve path
|
|
85
|
+
normalized_token_dir = Path(raw_dir).expanduser().absolute()
|
|
86
|
+
return normalized_token_dir
|
|
87
|
+
|
|
88
|
+
def _validate_path(self):
|
|
89
|
+
|
|
90
|
+
dir_path = self.storage_token_dir
|
|
91
|
+
os_type = platform.system().lower()
|
|
92
|
+
|
|
93
|
+
self._validate_path_syntax(dir_path, os_type)
|
|
94
|
+
self._validate_path_access(dir_path)
|
|
95
|
+
|
|
96
|
+
def _validate_path_syntax(self, path: Path, os_type: str):
|
|
97
|
+
"""Syntax validity check"""
|
|
98
|
+
invalid_pattern = self._INVALID_CHARS["windows"] if "windows" in os_type else self._INVALID_CHARS["posix"]
|
|
99
|
+
if invalid_pattern.search(str(path)):
|
|
100
|
+
msg = ("storage_token path contains illegal characters, path:%s." % (str(path)))
|
|
101
|
+
logger.warning(msg)
|
|
102
|
+
|
|
103
|
+
def _validate_path_access(self, path: Path):
|
|
104
|
+
"""Accessibility/permission check"""
|
|
105
|
+
if path.exists():
|
|
106
|
+
if not path.is_dir():
|
|
107
|
+
msg = ("storage_token path already exists but is not a directory, path:%s." % (str(path)))
|
|
108
|
+
logger.warning(msg)
|
|
109
|
+
return
|
|
110
|
+
if not os.access(path, os.R_OK | os.W_OK):
|
|
111
|
+
msg = ("storage_token directory has no read/write permission. Please check the configuration, path:%s." % (str(path)))
|
|
112
|
+
logger.warning(msg)
|
|
113
|
+
else:
|
|
114
|
+
parent_dir = path.parent
|
|
115
|
+
if not parent_dir.exists():
|
|
116
|
+
msg = ("storage_token parent directory does not exist, unable to create directory. Please check the configuration, parent path:%s." % (str(parent_dir)))
|
|
117
|
+
logger.warning(msg)
|
|
118
|
+
return
|
|
119
|
+
if not os.access(parent_dir, os.R_OK | os.W_OK | os.X_OK):
|
|
120
|
+
msg = ("storage_token parent directory has no read/write permission. Please check the configuration, parent path:%s." % (str(parent_dir)))
|
|
121
|
+
logger.warning(msg)
|
|
122
|
+
|
|
123
|
+
def _ensure_dir_exists(self):
|
|
124
|
+
"""Ensure the directory exists"""
|
|
125
|
+
try:
|
|
126
|
+
self.storage_token_dir.mkdir(parents=True, exist_ok=True)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
msg = ("storage_token failed to create directory, file:%s." % (str(self.storage_token_dir)))
|
|
129
|
+
logger.error(msg)
|
|
130
|
+
raise ClientException("ERROR_STORAGE_TOKEN", str(e)) from e
|
|
131
|
+
|
|
132
|
+
def _check_file_exists(self):
|
|
133
|
+
"""Check file exists"""
|
|
134
|
+
full_path = Path(self.token_file)
|
|
135
|
+
if full_path.is_file():
|
|
136
|
+
msg = ("storage_token Note: The token file already exists, the latest token configuration will be overwritten and written to the token file after successful 2FA verification. path:%s." % self.token_file)
|
|
137
|
+
logger.warning(msg)
|
|
138
|
+
|
|
139
|
+
def get_token_file_path(self):
|
|
140
|
+
return self.token_file
|
webull/data/__init__.py
CHANGED
|
@@ -69,6 +69,7 @@ class QuotesClient(mqttc.Client):
|
|
|
69
69
|
self._http_host = http_host
|
|
70
70
|
self._mqtt_host = mqtt_host
|
|
71
71
|
self._mqtt_port = mqtt_port
|
|
72
|
+
self._token_dir = None
|
|
72
73
|
self._quotes_decoder = QuotesDecoder()
|
|
73
74
|
|
|
74
75
|
api_client = ApiClient(app_key, app_secret, region_id)
|
|
@@ -158,6 +159,11 @@ class QuotesClient(mqttc.Client):
|
|
|
158
159
|
def on_quotes_message(self, func):
|
|
159
160
|
with self._callback_mutex:
|
|
160
161
|
self._on_quotes_message = func
|
|
162
|
+
|
|
163
|
+
def set_token_dir(self, token_dir):
|
|
164
|
+
self._token_dir = token_dir
|
|
165
|
+
if token_dir:
|
|
166
|
+
self.api_client.set_token_dir(token_dir)
|
|
161
167
|
|
|
162
168
|
def register_payload_decoder(self, type, decoder):
|
|
163
169
|
with self._callback_mutex:
|
|
@@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default()
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmessage.proto\"Z\n\x05\x42\x61sic\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x15\n\rinstrument_id\x18\x02 \x01(\t\x12\x11\n\ttimestamp\x18\x03 \x01(\t\x12\x17\n\x0ftrading_session\x18\x04 \x01(\t\"\
|
|
16
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rmessage.proto\"Z\n\x05\x42\x61sic\x12\x0e\n\x06symbol\x18\x01 \x01(\t\x12\x15\n\rinstrument_id\x18\x02 \x01(\t\x12\x11\n\ttimestamp\x18\x03 \x01(\t\x12\x17\n\x0ftrading_session\x18\x04 \x01(\t\"\xd6\x03\n\x08Snapshot\x12\x15\n\x05\x62\x61sic\x18\x01 \x01(\x0b\x32\x06.Basic\x12\x12\n\ntrade_time\x18\x02 \x01(\t\x12\r\n\x05price\x18\x03 \x01(\t\x12\x0c\n\x04open\x18\x04 \x01(\t\x12\x0c\n\x04high\x18\x05 \x01(\t\x12\x0b\n\x03low\x18\x06 \x01(\t\x12\x11\n\tpre_close\x18\x07 \x01(\t\x12\x0e\n\x06volume\x18\x08 \x01(\t\x12\x0e\n\x06\x63hange\x18\t \x01(\t\x12\x14\n\x0c\x63hange_ratio\x18\n \x01(\t\x12\x16\n\x0e\x65xt_trade_time\x18\x0b \x01(\t\x12\x11\n\text_price\x18\x0c \x01(\t\x12\x10\n\x08\x65xt_high\x18\r \x01(\t\x12\x0f\n\x07\x65xt_low\x18\x0e \x01(\t\x12\x12\n\next_volume\x18\x0f \x01(\t\x12\x12\n\next_change\x18\x10 \x01(\t\x12\x18\n\x10\x65xt_change_ratio\x18\x11 \x01(\t\x12\x16\n\x0eovn_trade_time\x18\x12 \x01(\t\x12\x11\n\tovn_price\x18\x13 \x01(\t\x12\x10\n\x08ovn_high\x18\x14 \x01(\t\x12\x0f\n\x07ovn_low\x18\x15 \x01(\t\x12\x12\n\novn_volume\x18\x16 \x01(\t\x12\x12\n\novn_change\x18\x17 \x01(\t\x12\x18\n\x10ovn_change_ratio\x18\x18 \x01(\t\"L\n\x05Quote\x12\x15\n\x05\x62\x61sic\x18\x01 \x01(\x0b\x32\x06.Basic\x12\x15\n\x04\x61sks\x18\x02 \x03(\x0b\x32\x07.AskBid\x12\x15\n\x04\x62ids\x18\x03 \x03(\x0b\x32\x07.AskBid\"X\n\x04Tick\x12\x15\n\x05\x62\x61sic\x18\x01 \x01(\x0b\x32\x06.Basic\x12\x0c\n\x04time\x18\x02 \x01(\t\x12\r\n\x05price\x18\x03 \x01(\t\x12\x0e\n\x06volume\x18\x04 \x01(\t\x12\x0c\n\x04side\x18\x05 \x01(\t\"U\n\x06\x41skBid\x12\r\n\x05price\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\t\x12\x15\n\x05order\x18\x03 \x03(\x0b\x32\x06.Order\x12\x17\n\x06\x62roker\x18\x04 \x03(\x0b\x32\x07.Broker\"#\n\x05Order\x12\x0c\n\x04mpid\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\t\"#\n\x06\x42roker\x12\x0b\n\x03\x62id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\tb\x06proto3')
|
|
17
17
|
|
|
18
18
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())
|
|
19
19
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'message_pb2', globals())
|
|
@@ -23,15 +23,15 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
|
23
23
|
_BASIC._serialized_start=17
|
|
24
24
|
_BASIC._serialized_end=107
|
|
25
25
|
_SNAPSHOT._serialized_start=110
|
|
26
|
-
_SNAPSHOT._serialized_end=
|
|
27
|
-
_QUOTE._serialized_start=
|
|
28
|
-
_QUOTE._serialized_end=
|
|
29
|
-
_TICK._serialized_start=
|
|
30
|
-
_TICK._serialized_end=
|
|
31
|
-
_ASKBID._serialized_start=
|
|
32
|
-
_ASKBID._serialized_end=
|
|
33
|
-
_ORDER._serialized_start=
|
|
34
|
-
_ORDER._serialized_end=
|
|
35
|
-
_BROKER._serialized_start=
|
|
36
|
-
_BROKER._serialized_end=
|
|
26
|
+
_SNAPSHOT._serialized_end=580
|
|
27
|
+
_QUOTE._serialized_start=582
|
|
28
|
+
_QUOTE._serialized_end=658
|
|
29
|
+
_TICK._serialized_start=660
|
|
30
|
+
_TICK._serialized_end=748
|
|
31
|
+
_ASKBID._serialized_start=750
|
|
32
|
+
_ASKBID._serialized_end=835
|
|
33
|
+
_ORDER._serialized_start=837
|
|
34
|
+
_ORDER._serialized_end=872
|
|
35
|
+
_BROKER._serialized_start=874
|
|
36
|
+
_BROKER._serialized_end=909
|
|
37
37
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -21,22 +21,40 @@ from webull.data.quotes.subscribe.basic_result import BasicResult
|
|
|
21
21
|
class SnapshotResult:
|
|
22
22
|
def __init__(self, pb_snapshot):
|
|
23
23
|
self.basic = BasicResult(pb_snapshot.basic)
|
|
24
|
+
self.last_trade_time = int(pb_snapshot.trade_time) if pb_snapshot.trade_time else None
|
|
25
|
+
self.price = Decimal(pb_snapshot.price) if pb_snapshot.price else None
|
|
24
26
|
self.open = Decimal(pb_snapshot.open) if pb_snapshot.open else None
|
|
25
27
|
self.high = Decimal(pb_snapshot.high) if pb_snapshot.high else None
|
|
26
28
|
self.low = Decimal(pb_snapshot.low) if pb_snapshot.low else None
|
|
27
|
-
self.
|
|
28
|
-
self.
|
|
29
|
-
|
|
30
|
-
self.
|
|
31
|
-
|
|
32
|
-
self.
|
|
33
|
-
|
|
34
|
-
self.
|
|
35
|
-
|
|
29
|
+
self.pre_close = Decimal(pb_snapshot.pre_close) if pb_snapshot.pre_close else None
|
|
30
|
+
self.close = Decimal(pb_snapshot.open) if pb_snapshot.open else None
|
|
31
|
+
self.volume = Decimal(pb_snapshot.volume) if pb_snapshot.volume else None
|
|
32
|
+
self.change = Decimal(pb_snapshot.change) if pb_snapshot.change else None
|
|
33
|
+
self.change_ratio = Decimal(pb_snapshot.change_ratio) if pb_snapshot.change_ratio else None
|
|
34
|
+
self.ext_trade_time = int(pb_snapshot.ext_trade_time) if pb_snapshot.ext_trade_time else None
|
|
35
|
+
self.ext_price = Decimal(pb_snapshot.ext_price) if pb_snapshot.ext_price else None
|
|
36
|
+
self.ext_high = Decimal(pb_snapshot.ext_high) if pb_snapshot.ext_high else None
|
|
37
|
+
self.ext_low = Decimal(pb_snapshot.ext_low) if pb_snapshot.ext_low else None
|
|
38
|
+
self.ext_volume = Decimal(pb_snapshot.ext_volume) if pb_snapshot.ext_volume else None
|
|
39
|
+
self.ext_change = Decimal(pb_snapshot.ext_change) if pb_snapshot.ext_change else None
|
|
40
|
+
self.ext_change_ratio = Decimal(pb_snapshot.ext_change_ratio) if pb_snapshot.ext_change_ratio else None
|
|
41
|
+
self.ovn_trade_time = int(pb_snapshot.ovn_trade_time) if pb_snapshot.ovn_trade_time else None
|
|
42
|
+
self.ovn_price = Decimal(pb_snapshot.ovn_price) if pb_snapshot.ovn_price else None
|
|
43
|
+
self.ovn_high = Decimal(pb_snapshot.ovn_high) if pb_snapshot.ovn_high else None
|
|
44
|
+
self.ovn_low = Decimal(pb_snapshot.ovn_low) if pb_snapshot.ovn_low else None
|
|
45
|
+
self.ovn_volume = Decimal(pb_snapshot.ovn_volume) if pb_snapshot.ovn_volume else None
|
|
46
|
+
self.ovn_change = Decimal(pb_snapshot.ovn_change) if pb_snapshot.ovn_change else None
|
|
47
|
+
self.ovn_change_ratio = Decimal(pb_snapshot.ovn_change_ratio) if pb_snapshot.ovn_change_ratio else None
|
|
36
48
|
|
|
37
49
|
def get_basic(self):
|
|
38
50
|
return self.basic
|
|
39
51
|
|
|
52
|
+
def get_last_trade_time(self):
|
|
53
|
+
return self.last_trade_time
|
|
54
|
+
|
|
55
|
+
def get_price(self):
|
|
56
|
+
return self.price
|
|
57
|
+
|
|
40
58
|
def get_open(self):
|
|
41
59
|
return self.open
|
|
42
60
|
|
|
@@ -46,12 +64,12 @@ class SnapshotResult:
|
|
|
46
64
|
def get_low(self):
|
|
47
65
|
return self.low
|
|
48
66
|
|
|
49
|
-
def get_price(self):
|
|
50
|
-
return self.price
|
|
51
|
-
|
|
52
67
|
def get_pre_close(self):
|
|
53
68
|
return self.pre_close
|
|
54
69
|
|
|
70
|
+
def get_close(self):
|
|
71
|
+
return self.close
|
|
72
|
+
|
|
55
73
|
def get_volume(self):
|
|
56
74
|
return self.volume
|
|
57
75
|
|
|
@@ -61,9 +79,55 @@ class SnapshotResult:
|
|
|
61
79
|
def get_change_ratio(self):
|
|
62
80
|
return self.change_ratio
|
|
63
81
|
|
|
82
|
+
def get_ext_trade_time(self):
|
|
83
|
+
return self.ext_trade_time
|
|
84
|
+
|
|
85
|
+
def get_ext_price(self):
|
|
86
|
+
return self.ext_price
|
|
87
|
+
|
|
88
|
+
def get_ext_high(self):
|
|
89
|
+
return self.ext_high
|
|
90
|
+
|
|
91
|
+
def get_ext_low(self):
|
|
92
|
+
return self.ext_low
|
|
93
|
+
|
|
94
|
+
def get_ext_volume(self):
|
|
95
|
+
return self.ext_volume
|
|
96
|
+
|
|
97
|
+
def get_ext_change(self):
|
|
98
|
+
return self.ext_change
|
|
99
|
+
|
|
100
|
+
def get_ext_change_ratio(self):
|
|
101
|
+
return self.ext_change_ratio
|
|
102
|
+
|
|
103
|
+
def get_ovn_trade_time(self):
|
|
104
|
+
return self.ovn_trade_time
|
|
105
|
+
|
|
106
|
+
def get_ovn_price(self):
|
|
107
|
+
return self.ovn_price
|
|
108
|
+
|
|
109
|
+
def get_ovn_high(self):
|
|
110
|
+
return self.ovn_high
|
|
111
|
+
|
|
112
|
+
def get_ovn_low(self):
|
|
113
|
+
return self.ovn_low
|
|
114
|
+
|
|
115
|
+
def get_ovn_volume(self):
|
|
116
|
+
return self.ovn_volume
|
|
117
|
+
|
|
118
|
+
def get_ovn_change(self):
|
|
119
|
+
return self.ovn_change
|
|
120
|
+
|
|
121
|
+
def get_ovn_change_ratio(self):
|
|
122
|
+
return self.ovn_change_ratio
|
|
123
|
+
|
|
64
124
|
def __repr__(self):
|
|
65
|
-
|
|
66
|
-
|
|
125
|
+
attrs = ['last_trade_time', 'price', 'open', 'high', 'low', 'pre_close', 'close', 'volume', 'change', 'change_ratio']
|
|
126
|
+
ext_attrs = [f"ext_{name}" for name in ['trade_time', 'price', 'high', 'low', 'volume', 'change', 'change_ratio']]
|
|
127
|
+
ovn_attrs = [f"ovn_{name}" for name in ['trade_time', 'price', 'high', 'low', 'volume', 'change', 'change_ratio']]
|
|
128
|
+
all_attrs = attrs + ext_attrs + ovn_attrs
|
|
129
|
+
attr_str = ', '.join(f"{name}:{getattr(self, name)}" for name in all_attrs)
|
|
130
|
+
return f"{self.basic}, {attr_str}"
|
|
67
131
|
|
|
68
132
|
def __str__(self):
|
|
69
133
|
return self.__repr__()
|
webull/trade/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '1.0.
|
|
1
|
+
__version__ = '1.0.8'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
import json
|
|
15
|
+
# coding=utf-8
|
|
16
|
+
|
|
17
|
+
from webull.core.request import ApiRequest
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BatchPlaceOrderRequest(ApiRequest):
|
|
21
|
+
def __init__(self):
|
|
22
|
+
super().__init__("/openapi/trade/order/batch-place", version='v2', method="POST", body_params={})
|
|
23
|
+
|
|
24
|
+
def set_account_id(self, account_id):
|
|
25
|
+
self.add_body_params("account_id", account_id)
|
|
26
|
+
|
|
27
|
+
def set_batch_orders(self, batch_orders):
|
|
28
|
+
self.add_body_params("batch_orders", batch_orders)
|
|
29
|
+
|
|
30
|
+
def add_custom_headers_from_order(self, batch_orders):
|
|
31
|
+
if not batch_orders:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
if isinstance(batch_orders, list) and batch_orders[0]:
|
|
35
|
+
first_order = batch_orders[0]
|
|
36
|
+
leg_list = first_order.get("legs")
|
|
37
|
+
if leg_list is not None and isinstance(leg_list, list):
|
|
38
|
+
for sub_leg in leg_list:
|
|
39
|
+
if (sub_leg and isinstance(sub_leg, dict)
|
|
40
|
+
and sub_leg.get("instrument_type") == "OPTION"):
|
|
41
|
+
instrument_type = sub_leg.get("instrument_type")
|
|
42
|
+
market = sub_leg.get("market")
|
|
43
|
+
category = market + "_" + instrument_type
|
|
44
|
+
if category is not None:
|
|
45
|
+
self.add_header("category", category)
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
market = first_order.get("market")
|
|
49
|
+
instrument_type = first_order.get("instrument_type")
|
|
50
|
+
category = market + "_" + instrument_type
|
|
51
|
+
if category is not None:
|
|
52
|
+
self.add_header("category", category)
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
# coding=utf-8
|
|
15
15
|
from webull.trade.request.v3.preview_order_request import PreviewOrderRequest
|
|
16
16
|
from webull.trade.request.v3.place_order_request import PlaceOrderRequest
|
|
17
|
+
from webull.trade.request.v3.batch_place_order_request import BatchPlaceOrderRequest
|
|
17
18
|
from webull.trade.request.v3.replace_order_request import ReplaceOrderRequest
|
|
18
19
|
from webull.trade.request.v3.cancel_order_request import CancelOrderRequest
|
|
19
20
|
from webull.trade.request.v3.get_order_detail_request import OrderDetailRequest
|
|
@@ -50,6 +51,18 @@ class OrderOperationV3:
|
|
|
50
51
|
response = self.client.get_response(place_order_request)
|
|
51
52
|
return response
|
|
52
53
|
|
|
54
|
+
def batch_place_order(self, account_id, batch_orders):
|
|
55
|
+
"""
|
|
56
|
+
This interface is currently supported only for Webull US.
|
|
57
|
+
Support for other regions will be available in future updates.
|
|
58
|
+
"""
|
|
59
|
+
batch_place_order_request = BatchPlaceOrderRequest()
|
|
60
|
+
batch_place_order_request.set_account_id(account_id=account_id)
|
|
61
|
+
batch_place_order_request.set_batch_orders(batch_orders=batch_orders)
|
|
62
|
+
batch_place_order_request.add_custom_headers_from_order(batch_orders)
|
|
63
|
+
response = self.client.get_response(batch_place_order_request)
|
|
64
|
+
return response
|
|
65
|
+
|
|
53
66
|
def replace_order(self, account_id, modify_orders, client_combo_order_id=None):
|
|
54
67
|
"""
|
|
55
68
|
This interface is currently supported only for Webull US.
|
{webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/RECORD
RENAMED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
samples/__init__.py,sha256=
|
|
1
|
+
samples/__init__.py,sha256=mFFUUCx5TqyW1TTFRrWDhXXVMJDMRxXWrkHanVtp9oY,22
|
|
2
|
+
samples/account/__init__.py,sha256=eoZ6GfifbqhMLNzjlqRDVil-yyBkOmVN9ujSgJWNBlY,15
|
|
3
|
+
samples/account/account_client.py,sha256=vwh-nI_JnjwcIeOr1sQPMzG8pAqZwliUusE49ZmYE3s,1796
|
|
4
|
+
samples/assets/__init__.py,sha256=eoZ6GfifbqhMLNzjlqRDVil-yyBkOmVN9ujSgJWNBlY,15
|
|
5
|
+
samples/assets/assets_client.py,sha256=p9LS2Td3gbUMkHn4LD_9RMCFuaQRUbsKvG4PhZBGJDc,1974
|
|
2
6
|
samples/data/__init__.py,sha256=eoZ6GfifbqhMLNzjlqRDVil-yyBkOmVN9ujSgJWNBlY,15
|
|
3
|
-
samples/data/data_client.py,sha256=
|
|
4
|
-
samples/data/data_streaming_client.py,sha256=
|
|
5
|
-
samples/data/data_streaming_client_async.py,sha256=
|
|
7
|
+
samples/data/data_client.py,sha256=rH91Yj7oIF6dBOOzF99cUn_12cgDLkw02SNIQM2nB1o,4868
|
|
8
|
+
samples/data/data_streaming_client.py,sha256=IdCUNdfo2_dR9OLgWZuIc9U9s-0-uNjwqkf9aLoG-44,4088
|
|
9
|
+
samples/data/data_streaming_client_async.py,sha256=o_u1ch8JdbcYnLCvDCCUzO-Z1IG7BUKncu_GB_kCbOs,4443
|
|
10
|
+
samples/order/__init__.py,sha256=eoZ6GfifbqhMLNzjlqRDVil-yyBkOmVN9ujSgJWNBlY,15
|
|
11
|
+
samples/order/order_option_client.py,sha256=njxr1QZc69_VbHsp9D9cOv1_-AekO2uNYKiY7o0-1Eg,3602
|
|
12
|
+
samples/order/order_stock_client.py,sha256=hYwVwVFVERk-YS7gRcW-LuGfAvcF8VJHZs1qwLcFhYU,3529
|
|
6
13
|
samples/trade/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
samples/trade/trade_client.py,sha256=
|
|
8
|
-
samples/trade/trade_client_v2.py,sha256=
|
|
9
|
-
samples/trade/trade_client_v3.py,sha256=
|
|
14
|
+
samples/trade/trade_client.py,sha256=LyWSvZJH4ByCkRORqP9WdkXfx65A2b6fhc9_GzL8DOM,6258
|
|
15
|
+
samples/trade/trade_client_v2.py,sha256=IO-zmRiUnCmIetTdiYunPh8jVK2VaPrOR77Dwvz1auY,10771
|
|
16
|
+
samples/trade/trade_client_v3.py,sha256=70f4VJYWcSJP3ppA8mRAoftJqjRTn5GTeGZck8eLCTg,16526
|
|
10
17
|
samples/trade/trade_event_client.py,sha256=uOx9EwFaves2yaTNm13BHpYC0IHXSoBL8BWPbDLeFw4,2469
|
|
11
|
-
webull/__init__.py,sha256=
|
|
12
|
-
webull/core/__init__.py,sha256=
|
|
13
|
-
webull/core/client.py,sha256=
|
|
18
|
+
webull/__init__.py,sha256=mFFUUCx5TqyW1TTFRrWDhXXVMJDMRxXWrkHanVtp9oY,22
|
|
19
|
+
webull/core/__init__.py,sha256=l5SgEaJNDBxUASBJsNGBJYu3AocnG4Mr89RIOokPiTA,225
|
|
20
|
+
webull/core/client.py,sha256=OE_ghPA558eE85MmaU_I3Tg0j-osCQFChTk-6dy4OW0,16398
|
|
14
21
|
webull/core/compat.py,sha256=HDis0D271oQ6OCpA7ViX10NJdhfXdoBfzx6nuV3vrnI,3114
|
|
15
22
|
webull/core/headers.py,sha256=7aMt3_YtaL9Yhqj2T1g7ESQgkI78SAXIk7hMX5U5FgE,2019
|
|
16
23
|
webull/core/request.py,sha256=BMR9kv3KDpaX2hh9BX7qPn5hhTB7XqJkMj2OSXPvoUk,8340
|
|
@@ -50,14 +57,15 @@ webull/core/http/protocol_type.py,sha256=DIPZ3i_r8nxaW5hVRVTyjwpKtWWTSHoGN_nA1SB
|
|
|
50
57
|
webull/core/http/request.py,sha256=GUrxb5n2ozVtb45-yBXe9iT0E61guez_udluMKF1k1w,3769
|
|
51
58
|
webull/core/http/response.py,sha256=Q4W2azXn9hqYuR1NVt8NlHmIaFAi0lmD3ejXRygk2KA,5868
|
|
52
59
|
webull/core/http/initializer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
webull/core/http/initializer/client_initializer.py,sha256=
|
|
60
|
+
webull/core/http/initializer/client_initializer.py,sha256=2Uya5T2i9qqwW0JTzjSk7HWsoHW1AOiEduAnx4Zu8k8,4197
|
|
54
61
|
webull/core/http/initializer/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
62
|
webull/core/http/initializer/config/config_operation.py,sha256=_1MILnzVu61XSV74yLsEzqLAt4GQNiJ9QpLN3kws9Q0,1766
|
|
56
63
|
webull/core/http/initializer/config/bean/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
64
|
webull/core/http/initializer/config/bean/query_config_request.py,sha256=WOLYv06KD6l47LH0VKs7_Npm3AmyPKj3b6nY9OrA7n0,1569
|
|
58
65
|
webull/core/http/initializer/token/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
-
webull/core/http/initializer/token/token_manager.py,sha256=
|
|
66
|
+
webull/core/http/initializer/token/token_manager.py,sha256=_5AFmiAtBBVgDf0VUaplZMyf74qbQ5o0N88XKEmGOis,11110
|
|
60
67
|
webull/core/http/initializer/token/token_operation.py,sha256=iI2H1cKfAe3gpVptHCniZso77kuPnf3jyg5b4QYQD5E,2577
|
|
68
|
+
webull/core/http/initializer/token/token_storage.py,sha256=ncGOBxR-Mtt-DFk29DTCv9T92PRRC6Ud1bVMTa46NE8,5834
|
|
61
69
|
webull/core/http/initializer/token/bean/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
70
|
webull/core/http/initializer/token/bean/access_token.py,sha256=wK5UrTnAMacO_vkhym-BcmbJdkHJcYHIpFcXJrQp1r8,1529
|
|
63
71
|
webull/core/http/initializer/token/bean/check_token_request.py,sha256=mYuwJPiwlnNGHxlgBMqkzGji9ZOOJ9c-o_hRwfFx0vA,1658
|
|
@@ -178,7 +186,7 @@ webull/core/vendored/requests/packages/urllib3/util/ssl_.py,sha256=aRUKc1WIyS-sU
|
|
|
178
186
|
webull/core/vendored/requests/packages/urllib3/util/timeout.py,sha256=sAyiBBds7eOk1oM3ulvVMWZiqx1B743puHqK92XwBcY,10325
|
|
179
187
|
webull/core/vendored/requests/packages/urllib3/util/url.py,sha256=_CgqbyNrQWubrv_y5aWhuutz3mnbj1cvTUe4VYbGYWA,7367
|
|
180
188
|
webull/core/vendored/requests/packages/urllib3/util/wait.py,sha256=0FHS8R3OrMU-97XWt8AxuUStkSGXTct9CfOwY_fWn7U,5971
|
|
181
|
-
webull/data/__init__.py,sha256=
|
|
189
|
+
webull/data/__init__.py,sha256=s8jHH-qZ5jY8Rsk-YS41NY0gFUU5YjdRBlDw0egN2-4,38
|
|
182
190
|
webull/data/data_client.py,sha256=JGouoFd37-sxpP6GyOJ0rRw7UuBmXqoIcis0jiRGJPQ,1871
|
|
183
191
|
webull/data/data_streaming_client.py,sha256=hYqrdKTeIB3Xmp7LMjMwN1XE6Y7bKXX0y-lGX1ty6W4,3870
|
|
184
192
|
webull/data/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -196,7 +204,7 @@ webull/data/common/timespan.py,sha256=Mg-dnvIWclVZRhIkjDiGzRlnOK457PWZ8sY5I8NXRB
|
|
|
196
204
|
webull/data/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
197
205
|
webull/data/internal/default_retry_policy.py,sha256=g94o5LKtbB5XoDiZWHp7yKGOWTTTOT89bz1ZSlT79mI,3307
|
|
198
206
|
webull/data/internal/exceptions.py,sha256=qtInXjns6ce6KQTtJOxz8M4bywuOJe2sNSAwGJqTWDs,1800
|
|
199
|
-
webull/data/internal/quotes_client.py,sha256=
|
|
207
|
+
webull/data/internal/quotes_client.py,sha256=PCOIteRz1QzwAva9D1DenrGOHOjm_XLrB250Qeqq1Fk,13009
|
|
200
208
|
webull/data/internal/quotes_decoder.py,sha256=Y849H41h-cnKPjFe9xDsvG3bWD3l7FnMcCM16yu5RuM,1420
|
|
201
209
|
webull/data/internal/quotes_payload_decoder.py,sha256=x2XGYDu4wSdc1LLrcIo3BXeb5jgqylFK7_8pfWv_xFU,998
|
|
202
210
|
webull/data/internal/quotes_topic.py,sha256=kG4RZTjj01U0e54qoOrJHlLrPeBzXekySasm2uoyXVo,1138
|
|
@@ -210,13 +218,13 @@ webull/data/quotes/subscribe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
210
218
|
webull/data/quotes/subscribe/ask_bid_result.py,sha256=EWu5dFMb_BwFGYPLe-f9uCD8_Ftl1wguhpU1qXi2zaY,1552
|
|
211
219
|
webull/data/quotes/subscribe/basic_result.py,sha256=l9pxGlt8HLHwE3plq_6J0OPZ_X4ZjjRfa1f9nExu2dE,1450
|
|
212
220
|
webull/data/quotes/subscribe/broker_result.py,sha256=E6pM0Ydr6tU_bPSOeyu1KJf7oYgbO-lIXLIdJQANH4I,1013
|
|
213
|
-
webull/data/quotes/subscribe/message_pb2.py,sha256=
|
|
221
|
+
webull/data/quotes/subscribe/message_pb2.py,sha256=JimxZN9hCEvt_NF0eYRNZJttEsCAc3bWAelE5Tva7yM,3136
|
|
214
222
|
webull/data/quotes/subscribe/order_result.py,sha256=bqLjRzCDK0jpQBMm00QxfgV-SPg04lOLYxc8SgpkFDw,912
|
|
215
223
|
webull/data/quotes/subscribe/payload_type.py,sha256=nacQubAzKjutfddNdilckpsbamCUt6nrBnzrxgMOrWQ,674
|
|
216
224
|
webull/data/quotes/subscribe/quote_decoder.py,sha256=vTDKi3TaQ79ODTydVcEEiJR2g0fQLPf5hUOqo7Mi1gw,1016
|
|
217
225
|
webull/data/quotes/subscribe/quote_result.py,sha256=2SoRzSGANphAut1jleNnfZtUhpqqbELRcs1biu440p8,1442
|
|
218
226
|
webull/data/quotes/subscribe/snapshot_decoder.py,sha256=dukBHXa971LHp9oPkgQ4mqL_8yIAU5XSlUIUjpMz2NY,1045
|
|
219
|
-
webull/data/quotes/subscribe/snapshot_result.py,sha256=
|
|
227
|
+
webull/data/quotes/subscribe/snapshot_result.py,sha256=U-YPGAVklcCsXZsp-9TPUUIfvqEgbPsou4Mhkkbxzp8,5077
|
|
220
228
|
webull/data/quotes/subscribe/tick_decoder.py,sha256=Qb__nJth2vsDMk2bWfNxY-mUGTUPzSZPO-ukHVxq80g,1008
|
|
221
229
|
webull/data/quotes/subscribe/tick_result.py,sha256=i2-4ZD1GK5EI3NhxkhSmUXfKnkBMjrwVBzLoDUY95OU,1467
|
|
222
230
|
webull/data/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -240,7 +248,7 @@ webull/data/request/get_snapshot_request.py,sha256=OD2PiaxmBCboFDaU8DFTwvfNd2zOb
|
|
|
240
248
|
webull/data/request/get_tick_request.py,sha256=rUHhQgC8Z_6ensLyCkg3DDK55SlEi0Bc_dhpCIIspDs,1366
|
|
241
249
|
webull/data/request/subscribe_request.py,sha256=dm93Q5Q4gigAMqAobolaYYSs3wM6QddPUZOjEtJsX-8,1471
|
|
242
250
|
webull/data/request/unsubscribe_request.py,sha256=hQA4mYM64PgmQEA2otl47-3nCXqjsSY_weFA3_wNbMM,1446
|
|
243
|
-
webull/trade/__init__.py,sha256=
|
|
251
|
+
webull/trade/__init__.py,sha256=iCEPnhz-knfGRAO4Ep2uQaYf4xwhPIjjcgAcNjga8kc,21
|
|
244
252
|
webull/trade/trade_client.py,sha256=_6lH4KO-jKgKWU28fWNsc9-KEHto-WvaP0gT96BJ_dw,2184
|
|
245
253
|
webull/trade/trade_events_client.py,sha256=4a7qupeT3dKJRKBQqXNhCai6ENzX6PA4yjTDHHxPR0w,8892
|
|
246
254
|
webull/trade/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -302,6 +310,7 @@ webull/trade/request/v2/preview_order_request.py,sha256=gRld4FcpBFQk7X24hzgs5lmM
|
|
|
302
310
|
webull/trade/request/v2/replace_option_request.py,sha256=0S86ToPdfZlZmf85jBQ39a1RkQ8wY7FJZA4hdxmyjN0,1189
|
|
303
311
|
webull/trade/request/v2/replace_order_request.py,sha256=rptKekCa6uSj1rzg8TT7jnuMVCDquhev6naPESqpihU,1193
|
|
304
312
|
webull/trade/request/v3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
313
|
+
webull/trade/request/v3/batch_place_order_request.py,sha256=QW6EZ-mZVTdLVbzJsr0VxA5Bca4hRgAq9_x6N9xVjnk,2115
|
|
305
314
|
webull/trade/request/v3/cancel_order_request.py,sha256=x3_5w-0_qwN6CjW2yuM4oLd51QuuTtXNVKZQ23MYscs,1024
|
|
306
315
|
webull/trade/request/v3/get_order_detail_request.py,sha256=jYuU8vHdE0fkXZdHqyuZ-rnhHUlQ9Qo5r7FvzmCfEKE,1000
|
|
307
316
|
webull/trade/request/v3/get_order_history_request.py,sha256=iCZEUs6JHOlRuA-u1zL1QvfD8h7gaEg1TR0X4mWxIVo,1304
|
|
@@ -318,10 +327,10 @@ webull/trade/trade/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
318
327
|
webull/trade/trade/v2/account_info_v2.py,sha256=IGY_BGTrZ0h7yQ_nDodNtmKen9gXW6heUFb7VLRQ9bY,2142
|
|
319
328
|
webull/trade/trade/v2/order_operation_v2.py,sha256=m54RH2j45CBBWEnqe4KxrsltAF44XKtPMT4kv8t7djQ,12745
|
|
320
329
|
webull/trade/trade/v3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
321
|
-
webull/trade/trade/v3/order_opration_v3.py,sha256=
|
|
322
|
-
webull_openapi_python_sdk-1.0.
|
|
323
|
-
webull_openapi_python_sdk-1.0.
|
|
324
|
-
webull_openapi_python_sdk-1.0.
|
|
325
|
-
webull_openapi_python_sdk-1.0.
|
|
326
|
-
webull_openapi_python_sdk-1.0.
|
|
327
|
-
webull_openapi_python_sdk-1.0.
|
|
330
|
+
webull/trade/trade/v3/order_opration_v3.py,sha256=_L10--m_XhoMe8YeYjQXVvRoTAWoRRzLU59UeneyMrU,7428
|
|
331
|
+
webull_openapi_python_sdk-1.0.8.dist-info/licenses/LICENSE,sha256=ALOnsLtb1aHxmDJg3-oMi0BO-i-cjfyZaOBfnnavKMc,11359
|
|
332
|
+
webull_openapi_python_sdk-1.0.8.dist-info/licenses/NOTICE,sha256=X5TApte6CPV10b96Cb70IRLusXmiRmK_R-dB-1tQM_I,2018
|
|
333
|
+
webull_openapi_python_sdk-1.0.8.dist-info/METADATA,sha256=jcAKegwV60urTKWSWSD1BhcsnEPVUU49Cz22cpSAKU8,702
|
|
334
|
+
webull_openapi_python_sdk-1.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
335
|
+
webull_openapi_python_sdk-1.0.8.dist-info/top_level.txt,sha256=h8pEjNDGWS2ZUZ2vYFpUShoMQT0ZRIQaD57QJWD8_aI,15
|
|
336
|
+
webull_openapi_python_sdk-1.0.8.dist-info/RECORD,,
|
{webull_openapi_python_sdk-1.0.6.dist-info → webull_openapi_python_sdk-1.0.8.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|