webull-openapi-python-sdk 1.0.0__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 -0
- samples/data/__init__.py +1 -0
- samples/data/data_client.py +57 -0
- samples/data/data_streaming_client.py +86 -0
- samples/data/data_streaming_client_async.py +101 -0
- samples/trade/__init__.py +0 -0
- samples/trade/trade_client.py +163 -0
- samples/trade/trade_client_v2.py +181 -0
- samples/trade/trade_event_client.py +47 -0
- webull/__init__.py +1 -0
- webull/core/__init__.py +12 -0
- webull/core/auth/__init__.py +0 -0
- webull/core/auth/algorithm/__init__.py +0 -0
- webull/core/auth/algorithm/sha_hmac1.py +65 -0
- webull/core/auth/algorithm/sha_hmac256.py +75 -0
- webull/core/auth/composer/__init__.py +0 -0
- webull/core/auth/composer/default_signature_composer.py +125 -0
- webull/core/auth/credentials.py +46 -0
- webull/core/auth/signers/__init__.py +0 -0
- webull/core/auth/signers/app_key_signer.py +72 -0
- webull/core/auth/signers/signer.py +48 -0
- webull/core/auth/signers/signer_factory.py +58 -0
- webull/core/cache/__init__.py +225 -0
- webull/core/client.py +410 -0
- webull/core/common/__init__.py +0 -0
- webull/core/common/api_type.py +19 -0
- webull/core/common/easy_enum.py +35 -0
- webull/core/common/region.py +7 -0
- webull/core/compat.py +85 -0
- webull/core/context/__init__.py +0 -0
- webull/core/context/request_context_holder.py +33 -0
- webull/core/data/endpoints.json +22 -0
- webull/core/data/retry_config.json +15 -0
- webull/core/endpoint/__init__.py +8 -0
- webull/core/endpoint/chained_endpoint_resolver.py +57 -0
- webull/core/endpoint/default_endpoint_resolver.py +60 -0
- webull/core/endpoint/local_config_regional_endpoint_resolver.py +77 -0
- webull/core/endpoint/resolver_endpoint_request.py +46 -0
- webull/core/endpoint/user_customized_endpoint_resolver.py +55 -0
- webull/core/exception/__init__.py +0 -0
- webull/core/exception/error_code.py +23 -0
- webull/core/exception/error_msg.py +21 -0
- webull/core/exception/exceptions.py +53 -0
- webull/core/headers.py +57 -0
- webull/core/http/__init__.py +0 -0
- webull/core/http/initializer/__init__.py +0 -0
- webull/core/http/initializer/client_initializer.py +79 -0
- webull/core/http/initializer/token/__init__.py +0 -0
- webull/core/http/initializer/token/bean/__init__.py +0 -0
- webull/core/http/initializer/token/bean/access_token.py +40 -0
- webull/core/http/initializer/token/bean/check_token_request.py +44 -0
- webull/core/http/initializer/token/bean/create_token_request.py +45 -0
- webull/core/http/initializer/token/bean/refresh_token_request.py +44 -0
- webull/core/http/initializer/token/token_manager.py +208 -0
- webull/core/http/initializer/token/token_operation.py +72 -0
- webull/core/http/method_type.py +43 -0
- webull/core/http/protocol_type.py +43 -0
- webull/core/http/request.py +121 -0
- webull/core/http/response.py +166 -0
- webull/core/request.py +278 -0
- webull/core/retry/__init__.py +0 -0
- webull/core/retry/backoff_strategy.py +102 -0
- webull/core/retry/retry_condition.py +214 -0
- webull/core/retry/retry_policy.py +63 -0
- webull/core/retry/retry_policy_context.py +51 -0
- webull/core/utils/__init__.py +0 -0
- webull/core/utils/common.py +62 -0
- webull/core/utils/data.py +25 -0
- webull/core/utils/desensitize.py +33 -0
- webull/core/utils/validation.py +49 -0
- webull/core/vendored/__init__.py +0 -0
- webull/core/vendored/requests/__init__.py +94 -0
- webull/core/vendored/requests/__version__.py +28 -0
- webull/core/vendored/requests/_internal_utils.py +56 -0
- webull/core/vendored/requests/adapters.py +539 -0
- webull/core/vendored/requests/api.py +166 -0
- webull/core/vendored/requests/auth.py +307 -0
- webull/core/vendored/requests/certs.py +34 -0
- webull/core/vendored/requests/compat.py +85 -0
- webull/core/vendored/requests/cookies.py +555 -0
- webull/core/vendored/requests/exceptions.py +136 -0
- webull/core/vendored/requests/help.py +134 -0
- webull/core/vendored/requests/hooks.py +48 -0
- webull/core/vendored/requests/models.py +960 -0
- webull/core/vendored/requests/packages/__init__.py +17 -0
- webull/core/vendored/requests/packages/certifi/__init__.py +17 -0
- webull/core/vendored/requests/packages/certifi/__main__.py +16 -0
- webull/core/vendored/requests/packages/certifi/cacert.pem +4433 -0
- webull/core/vendored/requests/packages/certifi/core.py +51 -0
- webull/core/vendored/requests/packages/chardet/__init__.py +53 -0
- webull/core/vendored/requests/packages/chardet/big5freq.py +400 -0
- webull/core/vendored/requests/packages/chardet/big5prober.py +61 -0
- webull/core/vendored/requests/packages/chardet/chardistribution.py +247 -0
- webull/core/vendored/requests/packages/chardet/charsetgroupprober.py +120 -0
- webull/core/vendored/requests/packages/chardet/charsetprober.py +159 -0
- webull/core/vendored/requests/packages/chardet/cli/__init__.py +1 -0
- webull/core/vendored/requests/packages/chardet/cli/chardetect.py +99 -0
- webull/core/vendored/requests/packages/chardet/codingstatemachine.py +102 -0
- webull/core/vendored/requests/packages/chardet/compat.py +48 -0
- webull/core/vendored/requests/packages/chardet/cp949prober.py +63 -0
- webull/core/vendored/requests/packages/chardet/enums.py +90 -0
- webull/core/vendored/requests/packages/chardet/escprober.py +115 -0
- webull/core/vendored/requests/packages/chardet/escsm.py +260 -0
- webull/core/vendored/requests/packages/chardet/eucjpprober.py +106 -0
- webull/core/vendored/requests/packages/chardet/euckrfreq.py +209 -0
- webull/core/vendored/requests/packages/chardet/euckrprober.py +61 -0
- webull/core/vendored/requests/packages/chardet/euctwfreq.py +401 -0
- webull/core/vendored/requests/packages/chardet/euctwprober.py +60 -0
- webull/core/vendored/requests/packages/chardet/gb2312freq.py +297 -0
- webull/core/vendored/requests/packages/chardet/gb2312prober.py +60 -0
- webull/core/vendored/requests/packages/chardet/hebrewprober.py +306 -0
- webull/core/vendored/requests/packages/chardet/jisfreq.py +339 -0
- webull/core/vendored/requests/packages/chardet/jpcntx.py +247 -0
- webull/core/vendored/requests/packages/chardet/langbulgarianmodel.py +242 -0
- webull/core/vendored/requests/packages/chardet/langcyrillicmodel.py +347 -0
- webull/core/vendored/requests/packages/chardet/langgreekmodel.py +239 -0
- webull/core/vendored/requests/packages/chardet/langhebrewmodel.py +214 -0
- webull/core/vendored/requests/packages/chardet/langhungarianmodel.py +239 -0
- webull/core/vendored/requests/packages/chardet/langthaimodel.py +213 -0
- webull/core/vendored/requests/packages/chardet/langturkishmodel.py +207 -0
- webull/core/vendored/requests/packages/chardet/latin1prober.py +159 -0
- webull/core/vendored/requests/packages/chardet/mbcharsetprober.py +105 -0
- webull/core/vendored/requests/packages/chardet/mbcsgroupprober.py +68 -0
- webull/core/vendored/requests/packages/chardet/mbcssm.py +586 -0
- webull/core/vendored/requests/packages/chardet/sbcharsetprober.py +146 -0
- webull/core/vendored/requests/packages/chardet/sbcsgroupprober.py +87 -0
- webull/core/vendored/requests/packages/chardet/sjisprober.py +106 -0
- webull/core/vendored/requests/packages/chardet/universaldetector.py +300 -0
- webull/core/vendored/requests/packages/chardet/utf8prober.py +96 -0
- webull/core/vendored/requests/packages/chardet/version.py +23 -0
- webull/core/vendored/requests/packages/urllib3/__init__.py +114 -0
- webull/core/vendored/requests/packages/urllib3/_collections.py +346 -0
- webull/core/vendored/requests/packages/urllib3/connection.py +405 -0
- webull/core/vendored/requests/packages/urllib3/connectionpool.py +910 -0
- webull/core/vendored/requests/packages/urllib3/contrib/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_appengine_environ.py +44 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/bindings.py +607 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/low_level.py +360 -0
- webull/core/vendored/requests/packages/urllib3/contrib/appengine.py +303 -0
- webull/core/vendored/requests/packages/urllib3/contrib/ntlmpool.py +125 -0
- webull/core/vendored/requests/packages/urllib3/contrib/pyopenssl.py +484 -0
- webull/core/vendored/requests/packages/urllib3/contrib/securetransport.py +818 -0
- webull/core/vendored/requests/packages/urllib3/contrib/socks.py +206 -0
- webull/core/vendored/requests/packages/urllib3/exceptions.py +260 -0
- webull/core/vendored/requests/packages/urllib3/fields.py +192 -0
- webull/core/vendored/requests/packages/urllib3/filepost.py +112 -0
- webull/core/vendored/requests/packages/urllib3/packages/__init__.py +19 -0
- webull/core/vendored/requests/packages/urllib3/packages/backports/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/packages/backports/makefile.py +67 -0
- webull/core/vendored/requests/packages/urllib3/packages/ordered_dict.py +273 -0
- webull/core/vendored/requests/packages/urllib3/packages/six.py +882 -0
- webull/core/vendored/requests/packages/urllib3/packages/socks.py +887 -0
- webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +19 -0
- webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +170 -0
- webull/core/vendored/requests/packages/urllib3/poolmanager.py +467 -0
- webull/core/vendored/requests/packages/urllib3/request.py +164 -0
- webull/core/vendored/requests/packages/urllib3/response.py +721 -0
- webull/core/vendored/requests/packages/urllib3/util/__init__.py +68 -0
- webull/core/vendored/requests/packages/urllib3/util/connection.py +148 -0
- webull/core/vendored/requests/packages/urllib3/util/queue.py +35 -0
- webull/core/vendored/requests/packages/urllib3/util/request.py +132 -0
- webull/core/vendored/requests/packages/urllib3/util/response.py +101 -0
- webull/core/vendored/requests/packages/urllib3/util/retry.py +426 -0
- webull/core/vendored/requests/packages/urllib3/util/selectors.py +601 -0
- webull/core/vendored/requests/packages/urllib3/util/ssl_.py +396 -0
- webull/core/vendored/requests/packages/urllib3/util/timeout.py +256 -0
- webull/core/vendored/requests/packages/urllib3/util/url.py +252 -0
- webull/core/vendored/requests/packages/urllib3/util/wait.py +164 -0
- webull/core/vendored/requests/packages.py +28 -0
- webull/core/vendored/requests/sessions.py +750 -0
- webull/core/vendored/requests/status_codes.py +105 -0
- webull/core/vendored/requests/structures.py +119 -0
- webull/core/vendored/requests/utils.py +916 -0
- webull/core/vendored/six.py +905 -0
- webull/data/__init__.py +3 -0
- webull/data/common/__init__.py +0 -0
- webull/data/common/category.py +26 -0
- webull/data/common/connect_ack.py +29 -0
- webull/data/common/direction.py +25 -0
- webull/data/common/exchange_code.py +33 -0
- webull/data/common/exercise_style.py +22 -0
- webull/data/common/expiration_cycle.py +26 -0
- webull/data/common/instrument_status.py +23 -0
- webull/data/common/option_type.py +20 -0
- webull/data/common/subscribe_type.py +22 -0
- webull/data/common/timespan.py +29 -0
- webull/data/data_client.py +35 -0
- webull/data/data_streaming_client.py +89 -0
- webull/data/internal/__init__.py +0 -0
- webull/data/internal/default_retry_policy.py +84 -0
- webull/data/internal/exceptions.py +60 -0
- webull/data/internal/quotes_client.py +314 -0
- webull/data/internal/quotes_decoder.py +40 -0
- webull/data/internal/quotes_payload_decoder.py +35 -0
- webull/data/internal/quotes_topic.py +36 -0
- webull/data/quotes/__init__.py +0 -0
- webull/data/quotes/instrument.py +33 -0
- webull/data/quotes/market_data.py +187 -0
- webull/data/quotes/market_streaming_data.py +66 -0
- webull/data/quotes/subscribe/__init__.py +0 -0
- webull/data/quotes/subscribe/ask_bid_result.py +49 -0
- webull/data/quotes/subscribe/basic_result.py +45 -0
- webull/data/quotes/subscribe/broker_result.py +33 -0
- webull/data/quotes/subscribe/message_pb2.py +37 -0
- webull/data/quotes/subscribe/order_result.py +30 -0
- webull/data/quotes/subscribe/payload_type.py +19 -0
- webull/data/quotes/subscribe/quote_decoder.py +28 -0
- webull/data/quotes/subscribe/quote_result.py +47 -0
- webull/data/quotes/subscribe/snapshot_decoder.py +30 -0
- webull/data/quotes/subscribe/snapshot_result.py +69 -0
- webull/data/quotes/subscribe/tick_decoder.py +29 -0
- webull/data/quotes/subscribe/tick_result.py +47 -0
- webull/data/request/__init__.py +0 -0
- webull/data/request/get_batch_historical_bars_request.py +43 -0
- webull/data/request/get_corp_action_request.py +47 -0
- webull/data/request/get_eod_bars_request.py +32 -0
- webull/data/request/get_historical_bars_request.py +43 -0
- webull/data/request/get_instruments_request.py +30 -0
- webull/data/request/get_quotes_request.py +35 -0
- webull/data/request/get_snapshot_request.py +38 -0
- webull/data/request/get_tick_request.py +37 -0
- webull/data/request/subscribe_request.py +43 -0
- webull/data/request/unsubscribe_request.py +42 -0
- webull/trade/__init__.py +2 -0
- webull/trade/common/__init__.py +0 -0
- webull/trade/common/account_type.py +22 -0
- webull/trade/common/category.py +29 -0
- webull/trade/common/combo_ticker_type.py +23 -0
- webull/trade/common/combo_type.py +31 -0
- webull/trade/common/currency.py +24 -0
- webull/trade/common/forbid_reason.py +27 -0
- webull/trade/common/instrument_type.py +27 -0
- webull/trade/common/markets.py +27 -0
- webull/trade/common/order_entrust_type.py +21 -0
- webull/trade/common/order_side.py +23 -0
- webull/trade/common/order_status.py +25 -0
- webull/trade/common/order_tif.py +24 -0
- webull/trade/common/order_type.py +30 -0
- webull/trade/common/trade_policy.py +22 -0
- webull/trade/common/trading_date_type.py +24 -0
- webull/trade/common/trailing_type.py +23 -0
- webull/trade/events/__init__.py +0 -0
- webull/trade/events/default_retry_policy.py +64 -0
- webull/trade/events/events_pb2.py +43 -0
- webull/trade/events/events_pb2_grpc.py +66 -0
- webull/trade/events/signature_composer.py +61 -0
- webull/trade/events/types.py +21 -0
- webull/trade/request/__init__.py +0 -0
- webull/trade/request/cancel_order_request.py +28 -0
- webull/trade/request/get_account_balance_request.py +28 -0
- webull/trade/request/get_account_positions_request.py +30 -0
- webull/trade/request/get_account_profile_request.py +26 -0
- webull/trade/request/get_app_subscriptions.py +28 -0
- webull/trade/request/get_open_orders_request.py +30 -0
- webull/trade/request/get_order_detail_request.py +27 -0
- webull/trade/request/get_today_orders_request.py +31 -0
- webull/trade/request/get_trade_calendar_request.py +30 -0
- webull/trade/request/get_trade_instrument_detail_request.py +24 -0
- webull/trade/request/get_trade_security_detail_request.py +42 -0
- webull/trade/request/get_tradeable_instruments_request.py +27 -0
- webull/trade/request/palce_order_request.py +91 -0
- webull/trade/request/place_order_request_v2.py +58 -0
- webull/trade/request/replace_order_request.py +73 -0
- webull/trade/request/replace_order_request_v2.py +38 -0
- webull/trade/request/v2/__init__.py +0 -0
- webull/trade/request/v2/cancel_option_request.py +28 -0
- webull/trade/request/v2/cancel_order_request.py +28 -0
- webull/trade/request/v2/get_account_balance_request.py +28 -0
- webull/trade/request/v2/get_account_list.py +23 -0
- webull/trade/request/v2/get_account_positions_request.py +24 -0
- webull/trade/request/v2/get_order_detail_request.py +26 -0
- webull/trade/request/v2/get_order_history_request.py +35 -0
- webull/trade/request/v2/palce_order_request.py +87 -0
- webull/trade/request/v2/place_option_request.py +64 -0
- webull/trade/request/v2/preview_option_request.py +28 -0
- webull/trade/request/v2/preview_order_request.py +59 -0
- webull/trade/request/v2/replace_option_request.py +28 -0
- webull/trade/request/v2/replace_order_request.py +57 -0
- webull/trade/trade/__init__.py +0 -0
- webull/trade/trade/account_info.py +83 -0
- webull/trade/trade/order_operation.py +246 -0
- webull/trade/trade/trade_calendar.py +37 -0
- webull/trade/trade/trade_instrument.py +72 -0
- webull/trade/trade/v2/__init__.py +0 -0
- webull/trade/trade/v2/account_info_v2.py +55 -0
- webull/trade/trade/v2/order_operation_v2.py +206 -0
- webull/trade/trade_client.py +43 -0
- webull/trade/trade_events_client.py +233 -0
- webull_openapi_python_sdk-1.0.0.dist-info/METADATA +28 -0
- webull_openapi_python_sdk-1.0.0.dist-info/RECORD +295 -0
- webull_openapi_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- webull_openapi_python_sdk-1.0.0.dist-info/licenses/LICENSE +202 -0
- webull_openapi_python_sdk-1.0.0.dist-info/licenses/NOTICE +56 -0
- webull_openapi_python_sdk-1.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
"""
|
|
37
|
+
This file borrowed some of its methods from a modified fork of the
|
|
38
|
+
https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-core/aliyunsdkcore/http/http_request.py
|
|
39
|
+
which was part of Alibaba Group.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
class Request:
|
|
43
|
+
content_length = "Content-Length"
|
|
44
|
+
content_type = "Content-Type"
|
|
45
|
+
|
|
46
|
+
def __init__(self, host="", url="/", method=None, headers=None):
|
|
47
|
+
self.__host = host
|
|
48
|
+
self.__url = url
|
|
49
|
+
self.__method = method
|
|
50
|
+
self.__content_type = None
|
|
51
|
+
self.__content = None
|
|
52
|
+
self.__encoding = None
|
|
53
|
+
self.__headers = headers or {}
|
|
54
|
+
self.__body = None
|
|
55
|
+
|
|
56
|
+
def get_host(self):
|
|
57
|
+
return self.__host
|
|
58
|
+
|
|
59
|
+
def set_host(self, host):
|
|
60
|
+
self.__host = host
|
|
61
|
+
|
|
62
|
+
def get_body(self):
|
|
63
|
+
return self.__body
|
|
64
|
+
|
|
65
|
+
def set_body(self, body):
|
|
66
|
+
self.__body = body
|
|
67
|
+
|
|
68
|
+
def get_url(self):
|
|
69
|
+
return self.__url
|
|
70
|
+
|
|
71
|
+
def set_url(self, url):
|
|
72
|
+
self.__url = url
|
|
73
|
+
|
|
74
|
+
def get_encoding(self):
|
|
75
|
+
return self.__encoding
|
|
76
|
+
|
|
77
|
+
def set_encoding(self, encoding):
|
|
78
|
+
self.__encoding = encoding
|
|
79
|
+
|
|
80
|
+
def get_content_type(self):
|
|
81
|
+
return self.__content_type
|
|
82
|
+
|
|
83
|
+
def set_content_type(self, content_type):
|
|
84
|
+
self.__content_type = content_type
|
|
85
|
+
|
|
86
|
+
def get_method(self):
|
|
87
|
+
return self.__method
|
|
88
|
+
|
|
89
|
+
def set_method(self, method):
|
|
90
|
+
self.__method = method
|
|
91
|
+
|
|
92
|
+
def get_content(self):
|
|
93
|
+
return self.__content
|
|
94
|
+
|
|
95
|
+
def get_header_value(self, name):
|
|
96
|
+
return self.__headers.get(name)
|
|
97
|
+
|
|
98
|
+
def put_header_parameter(self, key, value):
|
|
99
|
+
if key is not None and value is not None:
|
|
100
|
+
self.__headers[key] = value
|
|
101
|
+
|
|
102
|
+
def remove_header_parameter(self, key):
|
|
103
|
+
if key is not None and key in self.__headers:
|
|
104
|
+
self.__headers.pop(key)
|
|
105
|
+
|
|
106
|
+
def set_content(self, content, encoding, format="application/json"):
|
|
107
|
+
self.__content = content
|
|
108
|
+
if content is None:
|
|
109
|
+
self.remove_header_parameter(self.content_type)
|
|
110
|
+
self.remove_header_parameter(self.content_length)
|
|
111
|
+
self.__content_type = None
|
|
112
|
+
self.__encoding = None
|
|
113
|
+
else:
|
|
114
|
+
content_length = len(content)
|
|
115
|
+
self.__headers[self.content_length] = str(content_length)
|
|
116
|
+
self.__headers[self.content_type] = format
|
|
117
|
+
self.__encoding = encoding
|
|
118
|
+
|
|
119
|
+
def get_headers(self):
|
|
120
|
+
return self.__headers
|
|
121
|
+
|
|
@@ -0,0 +1,166 @@
|
|
|
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
|
+
"""
|
|
37
|
+
This file borrowed some of its methods from a modified fork of the
|
|
38
|
+
https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-core/aliyunsdkcore/http/http_response.py
|
|
39
|
+
which was part of Alibaba Group.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
import os
|
|
43
|
+
import logging
|
|
44
|
+
from webull.core.vendored.requests import Request, Session
|
|
45
|
+
from webull.core.http.request import Request as HttpRequest
|
|
46
|
+
from webull.core.http import protocol_type as PT
|
|
47
|
+
from webull.core.vendored.requests import status_codes
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
logger.setLevel(logging.DEBUG)
|
|
52
|
+
logger.propagate = False
|
|
53
|
+
ch = logging.StreamHandler()
|
|
54
|
+
logger.addHandler(ch)
|
|
55
|
+
|
|
56
|
+
DEFAULT_CONNECT_TIMEOUT = 5
|
|
57
|
+
|
|
58
|
+
ENV_DEBUG = "WEBULL_API_DEBUG"
|
|
59
|
+
DEBUG_VAL = "sdk"
|
|
60
|
+
|
|
61
|
+
class Response(HttpRequest):
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
host="",
|
|
65
|
+
url="/",
|
|
66
|
+
method="GET",
|
|
67
|
+
headers={},
|
|
68
|
+
protocol=PT.HTTP,
|
|
69
|
+
content=None,
|
|
70
|
+
port=None,
|
|
71
|
+
key_file=None,
|
|
72
|
+
cert_file=None,
|
|
73
|
+
read_timeout=None,
|
|
74
|
+
connect_timeout=None,
|
|
75
|
+
verify=None):
|
|
76
|
+
HttpRequest.__init__(
|
|
77
|
+
self,
|
|
78
|
+
host=host,
|
|
79
|
+
url=url,
|
|
80
|
+
method=method,
|
|
81
|
+
headers=headers)
|
|
82
|
+
self.__ssl_enable = False
|
|
83
|
+
if protocol is PT.HTTPS:
|
|
84
|
+
self.__ssl_enable = True
|
|
85
|
+
self.__key_file = key_file
|
|
86
|
+
self.__cert_file = cert_file
|
|
87
|
+
self.__port = port
|
|
88
|
+
self.__connection = None
|
|
89
|
+
self.__read_timeout = read_timeout
|
|
90
|
+
self.__connect_timeout = connect_timeout
|
|
91
|
+
self.__verify = verify
|
|
92
|
+
self.set_body(content)
|
|
93
|
+
|
|
94
|
+
def set_ssl_enable(self, enable):
|
|
95
|
+
self.__ssl_enable = enable
|
|
96
|
+
|
|
97
|
+
def get_ssl_enabled(self):
|
|
98
|
+
return self.__ssl_enable
|
|
99
|
+
|
|
100
|
+
@staticmethod
|
|
101
|
+
def prepare_http_debug(request, symbol):
|
|
102
|
+
base = ''
|
|
103
|
+
for key, value in request.headers.items():
|
|
104
|
+
base += '\n%s %s : %s' % (symbol, key, value)
|
|
105
|
+
return base
|
|
106
|
+
|
|
107
|
+
def do_http_debug(self, request, response):
|
|
108
|
+
# logger the request
|
|
109
|
+
request_base = '\n> %s %s' % (self.get_method().upper(), self.get_url())
|
|
110
|
+
request_base += '\n> Host : %s' % self.get_host()
|
|
111
|
+
logger.debug(request_base + self.prepare_http_debug(request, '>'))
|
|
112
|
+
|
|
113
|
+
# logger the response
|
|
114
|
+
response_base = '\n< %s %s' % (
|
|
115
|
+
response.status_code, status_codes._codes.get(response.status_code)[0].upper())
|
|
116
|
+
logger.debug(response_base + self.prepare_http_debug(response, '<'))
|
|
117
|
+
|
|
118
|
+
logger.debug("\n<\n" + response.text)
|
|
119
|
+
|
|
120
|
+
def get_verify_value(self):
|
|
121
|
+
if self.__verify is not None:
|
|
122
|
+
return self.__verify
|
|
123
|
+
return os.environ.get('WEBULL_API_CA_BUNDLE', True)
|
|
124
|
+
|
|
125
|
+
def get_response_object(self):
|
|
126
|
+
with Session() as s:
|
|
127
|
+
current_protocol = 'https://' if self.get_ssl_enabled() else 'http://'
|
|
128
|
+
host = self.get_host()
|
|
129
|
+
if host.startswith('https://') or\
|
|
130
|
+
not host.startswith('https://') and current_protocol == 'https://':
|
|
131
|
+
port = ':%s' % self.__port if self.__port != 80 and self.__port != 443 else ''
|
|
132
|
+
else:
|
|
133
|
+
port = ':%s' % self.__port if self.__port != 80 else ''
|
|
134
|
+
|
|
135
|
+
if host.startswith('http://') or host.startswith('https://'):
|
|
136
|
+
url = host + port + self.get_url()
|
|
137
|
+
else:
|
|
138
|
+
url = current_protocol + host + port + self.get_url()
|
|
139
|
+
|
|
140
|
+
req = Request(method=self.get_method(), url=url,
|
|
141
|
+
data=self.get_body(),
|
|
142
|
+
headers=self.get_headers(),
|
|
143
|
+
)
|
|
144
|
+
prepped = s.prepare_request(req)
|
|
145
|
+
|
|
146
|
+
proxy_https = os.environ.get('HTTPS_PROXY') or os.environ.get(
|
|
147
|
+
'https_proxy')
|
|
148
|
+
proxy_http = os.environ.get(
|
|
149
|
+
'HTTP_PROXY') or os.environ.get('http_proxy')
|
|
150
|
+
|
|
151
|
+
proxies = {
|
|
152
|
+
"http": proxy_http,
|
|
153
|
+
"https": proxy_https,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
response = s.send(prepped, proxies=proxies,
|
|
157
|
+
timeout=(self.__connect_timeout, self.__read_timeout),
|
|
158
|
+
allow_redirects=False, verify=self.get_verify_value(), cert=None)
|
|
159
|
+
|
|
160
|
+
http_debug = os.environ.get(ENV_DEBUG)
|
|
161
|
+
|
|
162
|
+
if http_debug is not None and http_debug.lower() == DEBUG_VAL:
|
|
163
|
+
# http debug information
|
|
164
|
+
self.do_http_debug(prepped, response)
|
|
165
|
+
|
|
166
|
+
return response.status_code, response.headers, response.content, response
|
webull/core/request.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
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
|
+
# coding=utf-8
|
|
16
|
+
|
|
17
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
18
|
+
# or more contributor license agreements. See the NOTICE file
|
|
19
|
+
# distributed with this work for additional information
|
|
20
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
21
|
+
# to you under the Apache License, Version 2.0 (the
|
|
22
|
+
# "License"); you may not use this file except in compliance
|
|
23
|
+
# with the License. You may obtain a copy of the License at
|
|
24
|
+
#
|
|
25
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
26
|
+
#
|
|
27
|
+
#
|
|
28
|
+
#
|
|
29
|
+
# Unless required by applicable law or agreed to in writing,
|
|
30
|
+
# software distributed under the License is distributed on an
|
|
31
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
32
|
+
# KIND, either express or implied. See the License for the
|
|
33
|
+
# specific language governing permissions and limitations
|
|
34
|
+
# under the License.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
This file borrowed some of its methods from a modified fork of the
|
|
38
|
+
https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-core/aliyunsdkcore/request.py
|
|
39
|
+
which was part of Alibaba Group.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
import abc
|
|
43
|
+
from webull.core.auth.algorithm import sha_hmac1
|
|
44
|
+
from webull.core.vendored.six import iterkeys
|
|
45
|
+
from webull.core.vendored.six import iteritems
|
|
46
|
+
from webull.core.vendored.six import add_metaclass
|
|
47
|
+
from webull.core.http import protocol_type
|
|
48
|
+
from webull.core.exception import exceptions, error_code
|
|
49
|
+
from webull.core.vendored.requests.structures import CaseInsensitiveDict
|
|
50
|
+
from webull.core.vendored.six.moves.urllib.parse import urlencode
|
|
51
|
+
from webull.core.auth.composer import default_signature_composer as sc
|
|
52
|
+
import webull.core.headers as hd
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
_default_protocol_type = protocol_type.HTTPS
|
|
56
|
+
|
|
57
|
+
def set_default_protocol_type(user_protocol_type):
|
|
58
|
+
global _default_protocol_type
|
|
59
|
+
if user_protocol_type == protocol_type.HTTP or user_protocol_type == protocol_type.HTTPS:
|
|
60
|
+
_default_protocol_type = user_protocol_type
|
|
61
|
+
else:
|
|
62
|
+
raise exceptions.ClientException(error_code.SDK_INVALID_PARAMETER)
|
|
63
|
+
|
|
64
|
+
def get_default_protocol_type():
|
|
65
|
+
return _default_protocol_type
|
|
66
|
+
|
|
67
|
+
@add_metaclass(abc.ABCMeta)
|
|
68
|
+
class BaseRequest:
|
|
69
|
+
def __init__(self,
|
|
70
|
+
version=None,
|
|
71
|
+
action_name=None,
|
|
72
|
+
accept_format=None,
|
|
73
|
+
protocol_type=None,
|
|
74
|
+
method=None):
|
|
75
|
+
"""
|
|
76
|
+
:param version:
|
|
77
|
+
:param action_name:
|
|
78
|
+
:param params:
|
|
79
|
+
:param resource_owner_account:
|
|
80
|
+
:param protocol_type:
|
|
81
|
+
:param accept_format:
|
|
82
|
+
:return:
|
|
83
|
+
"""
|
|
84
|
+
self._version = version
|
|
85
|
+
self._action_name = action_name
|
|
86
|
+
self._protocol_type = protocol_type
|
|
87
|
+
if self._protocol_type is None:
|
|
88
|
+
self._protocol_type = _default_protocol_type
|
|
89
|
+
self._accept_format = accept_format
|
|
90
|
+
self._params = {}
|
|
91
|
+
self._method = method
|
|
92
|
+
self._header = {}
|
|
93
|
+
if version is not None:
|
|
94
|
+
self.add_header(hd.VERSION, version)
|
|
95
|
+
self._body_params = {}
|
|
96
|
+
self._uri_pattern = None
|
|
97
|
+
self._uri_params = None
|
|
98
|
+
self._content = None
|
|
99
|
+
self._extra_user_agent = {}
|
|
100
|
+
self.string_to_sign = ''
|
|
101
|
+
self._request_connect_timeout = None
|
|
102
|
+
self._request_read_timeout = None
|
|
103
|
+
self.endpoint = None
|
|
104
|
+
|
|
105
|
+
def add_query_param(self, k, v):
|
|
106
|
+
self._params[k] = v
|
|
107
|
+
|
|
108
|
+
def add_body_params(self, k, v):
|
|
109
|
+
self._body_params[k] = v
|
|
110
|
+
|
|
111
|
+
def get_body_params(self):
|
|
112
|
+
return self._body_params
|
|
113
|
+
|
|
114
|
+
def get_uri_pattern(self):
|
|
115
|
+
return self._uri_pattern
|
|
116
|
+
|
|
117
|
+
def get_uri_params(self):
|
|
118
|
+
return self._uri_params
|
|
119
|
+
|
|
120
|
+
def get_version(self):
|
|
121
|
+
return self._version
|
|
122
|
+
|
|
123
|
+
def get_action_name(self):
|
|
124
|
+
return self._action_name
|
|
125
|
+
|
|
126
|
+
def get_accept_format(self):
|
|
127
|
+
return self._accept_format
|
|
128
|
+
|
|
129
|
+
def get_protocol_type(self):
|
|
130
|
+
return self._protocol_type
|
|
131
|
+
|
|
132
|
+
def get_query_params(self):
|
|
133
|
+
return self._params
|
|
134
|
+
|
|
135
|
+
def get_method(self):
|
|
136
|
+
return self._method
|
|
137
|
+
|
|
138
|
+
def set_uri_pattern(self, pattern):
|
|
139
|
+
self._uri_pattern = pattern
|
|
140
|
+
|
|
141
|
+
def set_uri_params(self, params):
|
|
142
|
+
self._uri_params = params
|
|
143
|
+
|
|
144
|
+
def set_method(self, method):
|
|
145
|
+
self._method = method
|
|
146
|
+
|
|
147
|
+
def set_version(self, version):
|
|
148
|
+
self._header[hd.VERSION] = version
|
|
149
|
+
self._version = version
|
|
150
|
+
|
|
151
|
+
def set_action_name(self, action_name):
|
|
152
|
+
self._action_name = action_name
|
|
153
|
+
|
|
154
|
+
def set_accept_format(self, accept_format):
|
|
155
|
+
self._accept_format = accept_format
|
|
156
|
+
|
|
157
|
+
def set_protocol_type(self, protocol_type):
|
|
158
|
+
self._protocol_type = protocol_type
|
|
159
|
+
|
|
160
|
+
def set_query_params(self, params):
|
|
161
|
+
self._params = params
|
|
162
|
+
|
|
163
|
+
def set_body_params(self, body_params):
|
|
164
|
+
self._body_params = body_params
|
|
165
|
+
|
|
166
|
+
def set_content(self, content):
|
|
167
|
+
"""
|
|
168
|
+
:param content: ByteArray
|
|
169
|
+
:return:
|
|
170
|
+
"""
|
|
171
|
+
self._content = content
|
|
172
|
+
|
|
173
|
+
def get_content(self):
|
|
174
|
+
"""
|
|
175
|
+
:return: ByteArray
|
|
176
|
+
"""
|
|
177
|
+
return self._content
|
|
178
|
+
|
|
179
|
+
def get_headers(self):
|
|
180
|
+
"""
|
|
181
|
+
:return: Dict
|
|
182
|
+
"""
|
|
183
|
+
return self._header
|
|
184
|
+
|
|
185
|
+
def set_headers(self, headers):
|
|
186
|
+
"""
|
|
187
|
+
:param headers: Dict
|
|
188
|
+
:return:
|
|
189
|
+
"""
|
|
190
|
+
self._header = headers
|
|
191
|
+
|
|
192
|
+
def add_header(self, k, v):
|
|
193
|
+
self._header[k] = v
|
|
194
|
+
|
|
195
|
+
def set_user_agent(self, agent):
|
|
196
|
+
self.add_header(hd.NATIVE_USER_AGENT, agent)
|
|
197
|
+
|
|
198
|
+
def append_user_agent(self, key, value):
|
|
199
|
+
self._extra_user_agent.update({key: value})
|
|
200
|
+
|
|
201
|
+
def request_user_agent(self):
|
|
202
|
+
request_user_agent = {}
|
|
203
|
+
if hd.NATIVE_USER_AGENT in self.get_headers():
|
|
204
|
+
request_user_agent.update({
|
|
205
|
+
'request': self.get_headers().get(hd.NATIVE_USER_AGENT)
|
|
206
|
+
})
|
|
207
|
+
else:
|
|
208
|
+
request_user_agent.update(self._extra_user_agent)
|
|
209
|
+
|
|
210
|
+
return CaseInsensitiveDict(request_user_agent)
|
|
211
|
+
|
|
212
|
+
def set_content_type(self, content_type):
|
|
213
|
+
self.add_header(hd.NATIVE_CONTENT_TYPE, content_type)
|
|
214
|
+
|
|
215
|
+
@abc.abstractmethod
|
|
216
|
+
def get_signed_header(self, host, app_key, app_secret):
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
def get_connect_timeout(self):
|
|
220
|
+
return self._request_connect_timeout
|
|
221
|
+
|
|
222
|
+
def set_connect_timeout(self, connect_timeout):
|
|
223
|
+
self._request_connect_timeout = connect_timeout
|
|
224
|
+
|
|
225
|
+
def get_read_timeout(self):
|
|
226
|
+
return self._request_read_timeout
|
|
227
|
+
|
|
228
|
+
def set_read_timeout(self, read_timeout):
|
|
229
|
+
self._request_read_timeout = read_timeout
|
|
230
|
+
|
|
231
|
+
def set_endpoint(self, endpoint):
|
|
232
|
+
self.endpoint = endpoint
|
|
233
|
+
|
|
234
|
+
def get_endpoint(self):
|
|
235
|
+
return self.endpoint
|
|
236
|
+
class ApiRequest(BaseRequest):
|
|
237
|
+
def __init__(
|
|
238
|
+
self,
|
|
239
|
+
request_path,
|
|
240
|
+
version=None,
|
|
241
|
+
method='POST',
|
|
242
|
+
headers=None,
|
|
243
|
+
query_params=None,
|
|
244
|
+
body_params=None,
|
|
245
|
+
protocol=None,
|
|
246
|
+
signer_spec=sha_hmac1):
|
|
247
|
+
BaseRequest.__init__(
|
|
248
|
+
self,
|
|
249
|
+
version,
|
|
250
|
+
request_path,
|
|
251
|
+
"JSON",
|
|
252
|
+
protocol,
|
|
253
|
+
method)
|
|
254
|
+
self._method = method
|
|
255
|
+
self._header = headers or {}
|
|
256
|
+
self._params = query_params or {}
|
|
257
|
+
if version is not None:
|
|
258
|
+
self.add_header(hd.VERSION, version)
|
|
259
|
+
self._signer_spec = signer_spec
|
|
260
|
+
self.set_body_params(body_params)
|
|
261
|
+
|
|
262
|
+
def get_signed_header(self, host, app_key, app_secret):
|
|
263
|
+
sc.calc_signature(self._header, host, self._action_name, self._params, self._body_params, app_key, app_secret, self._signer_spec)
|
|
264
|
+
return self._header
|
|
265
|
+
|
|
266
|
+
def get_url(self):
|
|
267
|
+
"""
|
|
268
|
+
Compose request url without domain
|
|
269
|
+
:return: String
|
|
270
|
+
"""
|
|
271
|
+
url = self._action_name
|
|
272
|
+
if self._params:
|
|
273
|
+
if not url.endswith("?"):
|
|
274
|
+
url += "?"
|
|
275
|
+
url += urlencode(self._params)
|
|
276
|
+
if url.endswith("?"):
|
|
277
|
+
url = url[0:(len(url) - 1)]
|
|
278
|
+
return url
|
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
"""
|
|
37
|
+
This file borrowed some of its methods from a modified fork of the
|
|
38
|
+
https://github.com/aliyun/aliyun-openapi-python-sdk/blob/master/aliyun-python-sdk-core/aliyunsdkcore/retry/backoff_strategy.py
|
|
39
|
+
which was part of Alibaba Group.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
import random
|
|
43
|
+
from webull.core.retry.retry_condition import RetryCondition
|
|
44
|
+
|
|
45
|
+
class BackoffStrategy(object):
|
|
46
|
+
def compute_delay_before_next_retry(self, retry_policy_context):
|
|
47
|
+
"""Compute delay for request need to be retried, in milliseconds"""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
class FixedDelayStrategy(BackoffStrategy):
|
|
51
|
+
def __init__(self, fixed_delay):
|
|
52
|
+
self.fixed_delay = fixed_delay
|
|
53
|
+
|
|
54
|
+
def compute_delay_before_next_retry(self, retry_policy_context):
|
|
55
|
+
return self.fixed_delay
|
|
56
|
+
|
|
57
|
+
class NoDelayStrategy(FixedDelayStrategy):
|
|
58
|
+
def __init__(self):
|
|
59
|
+
FixedDelayStrategy.__init__(self, 0)
|
|
60
|
+
|
|
61
|
+
class ExponentialBackoffStrategy(BackoffStrategy):
|
|
62
|
+
MAX_RETRY_LIMIT = 30 # to avoid integer overflow during delay calculation
|
|
63
|
+
|
|
64
|
+
def __init__(self, base_delay_in_milliseconds, max_delay_in_milliseconds):
|
|
65
|
+
self.base_delay_in_milliseconds = base_delay_in_milliseconds
|
|
66
|
+
self.max_delay_in_milliseconds = max_delay_in_milliseconds
|
|
67
|
+
|
|
68
|
+
def compute_delay_before_next_retry(self, retry_policy_context):
|
|
69
|
+
retries = min(self.MAX_RETRY_LIMIT, retry_policy_context.retries_attempted)
|
|
70
|
+
delay = min(self.max_delay_in_milliseconds, self.base_delay_in_milliseconds << retries)
|
|
71
|
+
return delay
|
|
72
|
+
|
|
73
|
+
class JitteredExponentialBackoffStrategy(ExponentialBackoffStrategy):
|
|
74
|
+
def compute_delay_before_next_retry(self, retry_policy_context):
|
|
75
|
+
delay = ExponentialBackoffStrategy.compute_delay_before_next_retry(self, retry_policy_context)
|
|
76
|
+
return delay / 2 + random.randint(0, int(delay / 2))
|
|
77
|
+
|
|
78
|
+
class DefaultMixedBackoffStrategy(BackoffStrategy):
|
|
79
|
+
|
|
80
|
+
# in milliseconds
|
|
81
|
+
SDK_DEFAULT_BASE_DELAY = 100
|
|
82
|
+
SDK_DEFAULT_TROTTLED_BASE_DELAY = 500
|
|
83
|
+
SDK_DEFAULT_MAX_BACKOFF = 20 * 1000
|
|
84
|
+
|
|
85
|
+
def __init__(self):
|
|
86
|
+
self._default_backoff_strategy = ExponentialBackoffStrategy(
|
|
87
|
+
self.SDK_DEFAULT_BASE_DELAY,
|
|
88
|
+
self.SDK_DEFAULT_MAX_BACKOFF
|
|
89
|
+
)
|
|
90
|
+
self._default_throttled_backoff_strategy = JitteredExponentialBackoffStrategy(
|
|
91
|
+
self.SDK_DEFAULT_TROTTLED_BASE_DELAY,
|
|
92
|
+
self.SDK_DEFAULT_MAX_BACKOFF
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def compute_delay_before_next_retry(self, retry_policy_context):
|
|
96
|
+
retryable = retry_policy_context.retryable
|
|
97
|
+
if retryable & RetryCondition.SHOULD_RETRY_WITH_THROTTLING_BACKOFF:
|
|
98
|
+
return self._default_throttled_backoff_strategy.compute_delay_before_next_retry(
|
|
99
|
+
retry_policy_context)
|
|
100
|
+
else:
|
|
101
|
+
return self._default_backoff_strategy.compute_delay_before_next_retry(
|
|
102
|
+
retry_policy_context)
|