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.
Files changed (295) hide show
  1. samples/__init__.py +1 -0
  2. samples/data/__init__.py +1 -0
  3. samples/data/data_client.py +57 -0
  4. samples/data/data_streaming_client.py +86 -0
  5. samples/data/data_streaming_client_async.py +101 -0
  6. samples/trade/__init__.py +0 -0
  7. samples/trade/trade_client.py +163 -0
  8. samples/trade/trade_client_v2.py +181 -0
  9. samples/trade/trade_event_client.py +47 -0
  10. webull/__init__.py +1 -0
  11. webull/core/__init__.py +12 -0
  12. webull/core/auth/__init__.py +0 -0
  13. webull/core/auth/algorithm/__init__.py +0 -0
  14. webull/core/auth/algorithm/sha_hmac1.py +65 -0
  15. webull/core/auth/algorithm/sha_hmac256.py +75 -0
  16. webull/core/auth/composer/__init__.py +0 -0
  17. webull/core/auth/composer/default_signature_composer.py +125 -0
  18. webull/core/auth/credentials.py +46 -0
  19. webull/core/auth/signers/__init__.py +0 -0
  20. webull/core/auth/signers/app_key_signer.py +72 -0
  21. webull/core/auth/signers/signer.py +48 -0
  22. webull/core/auth/signers/signer_factory.py +58 -0
  23. webull/core/cache/__init__.py +225 -0
  24. webull/core/client.py +410 -0
  25. webull/core/common/__init__.py +0 -0
  26. webull/core/common/api_type.py +19 -0
  27. webull/core/common/easy_enum.py +35 -0
  28. webull/core/common/region.py +7 -0
  29. webull/core/compat.py +85 -0
  30. webull/core/context/__init__.py +0 -0
  31. webull/core/context/request_context_holder.py +33 -0
  32. webull/core/data/endpoints.json +22 -0
  33. webull/core/data/retry_config.json +15 -0
  34. webull/core/endpoint/__init__.py +8 -0
  35. webull/core/endpoint/chained_endpoint_resolver.py +57 -0
  36. webull/core/endpoint/default_endpoint_resolver.py +60 -0
  37. webull/core/endpoint/local_config_regional_endpoint_resolver.py +77 -0
  38. webull/core/endpoint/resolver_endpoint_request.py +46 -0
  39. webull/core/endpoint/user_customized_endpoint_resolver.py +55 -0
  40. webull/core/exception/__init__.py +0 -0
  41. webull/core/exception/error_code.py +23 -0
  42. webull/core/exception/error_msg.py +21 -0
  43. webull/core/exception/exceptions.py +53 -0
  44. webull/core/headers.py +57 -0
  45. webull/core/http/__init__.py +0 -0
  46. webull/core/http/initializer/__init__.py +0 -0
  47. webull/core/http/initializer/client_initializer.py +79 -0
  48. webull/core/http/initializer/token/__init__.py +0 -0
  49. webull/core/http/initializer/token/bean/__init__.py +0 -0
  50. webull/core/http/initializer/token/bean/access_token.py +40 -0
  51. webull/core/http/initializer/token/bean/check_token_request.py +44 -0
  52. webull/core/http/initializer/token/bean/create_token_request.py +45 -0
  53. webull/core/http/initializer/token/bean/refresh_token_request.py +44 -0
  54. webull/core/http/initializer/token/token_manager.py +208 -0
  55. webull/core/http/initializer/token/token_operation.py +72 -0
  56. webull/core/http/method_type.py +43 -0
  57. webull/core/http/protocol_type.py +43 -0
  58. webull/core/http/request.py +121 -0
  59. webull/core/http/response.py +166 -0
  60. webull/core/request.py +278 -0
  61. webull/core/retry/__init__.py +0 -0
  62. webull/core/retry/backoff_strategy.py +102 -0
  63. webull/core/retry/retry_condition.py +214 -0
  64. webull/core/retry/retry_policy.py +63 -0
  65. webull/core/retry/retry_policy_context.py +51 -0
  66. webull/core/utils/__init__.py +0 -0
  67. webull/core/utils/common.py +62 -0
  68. webull/core/utils/data.py +25 -0
  69. webull/core/utils/desensitize.py +33 -0
  70. webull/core/utils/validation.py +49 -0
  71. webull/core/vendored/__init__.py +0 -0
  72. webull/core/vendored/requests/__init__.py +94 -0
  73. webull/core/vendored/requests/__version__.py +28 -0
  74. webull/core/vendored/requests/_internal_utils.py +56 -0
  75. webull/core/vendored/requests/adapters.py +539 -0
  76. webull/core/vendored/requests/api.py +166 -0
  77. webull/core/vendored/requests/auth.py +307 -0
  78. webull/core/vendored/requests/certs.py +34 -0
  79. webull/core/vendored/requests/compat.py +85 -0
  80. webull/core/vendored/requests/cookies.py +555 -0
  81. webull/core/vendored/requests/exceptions.py +136 -0
  82. webull/core/vendored/requests/help.py +134 -0
  83. webull/core/vendored/requests/hooks.py +48 -0
  84. webull/core/vendored/requests/models.py +960 -0
  85. webull/core/vendored/requests/packages/__init__.py +17 -0
  86. webull/core/vendored/requests/packages/certifi/__init__.py +17 -0
  87. webull/core/vendored/requests/packages/certifi/__main__.py +16 -0
  88. webull/core/vendored/requests/packages/certifi/cacert.pem +4433 -0
  89. webull/core/vendored/requests/packages/certifi/core.py +51 -0
  90. webull/core/vendored/requests/packages/chardet/__init__.py +53 -0
  91. webull/core/vendored/requests/packages/chardet/big5freq.py +400 -0
  92. webull/core/vendored/requests/packages/chardet/big5prober.py +61 -0
  93. webull/core/vendored/requests/packages/chardet/chardistribution.py +247 -0
  94. webull/core/vendored/requests/packages/chardet/charsetgroupprober.py +120 -0
  95. webull/core/vendored/requests/packages/chardet/charsetprober.py +159 -0
  96. webull/core/vendored/requests/packages/chardet/cli/__init__.py +1 -0
  97. webull/core/vendored/requests/packages/chardet/cli/chardetect.py +99 -0
  98. webull/core/vendored/requests/packages/chardet/codingstatemachine.py +102 -0
  99. webull/core/vendored/requests/packages/chardet/compat.py +48 -0
  100. webull/core/vendored/requests/packages/chardet/cp949prober.py +63 -0
  101. webull/core/vendored/requests/packages/chardet/enums.py +90 -0
  102. webull/core/vendored/requests/packages/chardet/escprober.py +115 -0
  103. webull/core/vendored/requests/packages/chardet/escsm.py +260 -0
  104. webull/core/vendored/requests/packages/chardet/eucjpprober.py +106 -0
  105. webull/core/vendored/requests/packages/chardet/euckrfreq.py +209 -0
  106. webull/core/vendored/requests/packages/chardet/euckrprober.py +61 -0
  107. webull/core/vendored/requests/packages/chardet/euctwfreq.py +401 -0
  108. webull/core/vendored/requests/packages/chardet/euctwprober.py +60 -0
  109. webull/core/vendored/requests/packages/chardet/gb2312freq.py +297 -0
  110. webull/core/vendored/requests/packages/chardet/gb2312prober.py +60 -0
  111. webull/core/vendored/requests/packages/chardet/hebrewprober.py +306 -0
  112. webull/core/vendored/requests/packages/chardet/jisfreq.py +339 -0
  113. webull/core/vendored/requests/packages/chardet/jpcntx.py +247 -0
  114. webull/core/vendored/requests/packages/chardet/langbulgarianmodel.py +242 -0
  115. webull/core/vendored/requests/packages/chardet/langcyrillicmodel.py +347 -0
  116. webull/core/vendored/requests/packages/chardet/langgreekmodel.py +239 -0
  117. webull/core/vendored/requests/packages/chardet/langhebrewmodel.py +214 -0
  118. webull/core/vendored/requests/packages/chardet/langhungarianmodel.py +239 -0
  119. webull/core/vendored/requests/packages/chardet/langthaimodel.py +213 -0
  120. webull/core/vendored/requests/packages/chardet/langturkishmodel.py +207 -0
  121. webull/core/vendored/requests/packages/chardet/latin1prober.py +159 -0
  122. webull/core/vendored/requests/packages/chardet/mbcharsetprober.py +105 -0
  123. webull/core/vendored/requests/packages/chardet/mbcsgroupprober.py +68 -0
  124. webull/core/vendored/requests/packages/chardet/mbcssm.py +586 -0
  125. webull/core/vendored/requests/packages/chardet/sbcharsetprober.py +146 -0
  126. webull/core/vendored/requests/packages/chardet/sbcsgroupprober.py +87 -0
  127. webull/core/vendored/requests/packages/chardet/sjisprober.py +106 -0
  128. webull/core/vendored/requests/packages/chardet/universaldetector.py +300 -0
  129. webull/core/vendored/requests/packages/chardet/utf8prober.py +96 -0
  130. webull/core/vendored/requests/packages/chardet/version.py +23 -0
  131. webull/core/vendored/requests/packages/urllib3/__init__.py +114 -0
  132. webull/core/vendored/requests/packages/urllib3/_collections.py +346 -0
  133. webull/core/vendored/requests/packages/urllib3/connection.py +405 -0
  134. webull/core/vendored/requests/packages/urllib3/connectionpool.py +910 -0
  135. webull/core/vendored/requests/packages/urllib3/contrib/__init__.py +0 -0
  136. webull/core/vendored/requests/packages/urllib3/contrib/_appengine_environ.py +44 -0
  137. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/__init__.py +0 -0
  138. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/bindings.py +607 -0
  139. webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/low_level.py +360 -0
  140. webull/core/vendored/requests/packages/urllib3/contrib/appengine.py +303 -0
  141. webull/core/vendored/requests/packages/urllib3/contrib/ntlmpool.py +125 -0
  142. webull/core/vendored/requests/packages/urllib3/contrib/pyopenssl.py +484 -0
  143. webull/core/vendored/requests/packages/urllib3/contrib/securetransport.py +818 -0
  144. webull/core/vendored/requests/packages/urllib3/contrib/socks.py +206 -0
  145. webull/core/vendored/requests/packages/urllib3/exceptions.py +260 -0
  146. webull/core/vendored/requests/packages/urllib3/fields.py +192 -0
  147. webull/core/vendored/requests/packages/urllib3/filepost.py +112 -0
  148. webull/core/vendored/requests/packages/urllib3/packages/__init__.py +19 -0
  149. webull/core/vendored/requests/packages/urllib3/packages/backports/__init__.py +0 -0
  150. webull/core/vendored/requests/packages/urllib3/packages/backports/makefile.py +67 -0
  151. webull/core/vendored/requests/packages/urllib3/packages/ordered_dict.py +273 -0
  152. webull/core/vendored/requests/packages/urllib3/packages/six.py +882 -0
  153. webull/core/vendored/requests/packages/urllib3/packages/socks.py +887 -0
  154. webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +19 -0
  155. webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +170 -0
  156. webull/core/vendored/requests/packages/urllib3/poolmanager.py +467 -0
  157. webull/core/vendored/requests/packages/urllib3/request.py +164 -0
  158. webull/core/vendored/requests/packages/urllib3/response.py +721 -0
  159. webull/core/vendored/requests/packages/urllib3/util/__init__.py +68 -0
  160. webull/core/vendored/requests/packages/urllib3/util/connection.py +148 -0
  161. webull/core/vendored/requests/packages/urllib3/util/queue.py +35 -0
  162. webull/core/vendored/requests/packages/urllib3/util/request.py +132 -0
  163. webull/core/vendored/requests/packages/urllib3/util/response.py +101 -0
  164. webull/core/vendored/requests/packages/urllib3/util/retry.py +426 -0
  165. webull/core/vendored/requests/packages/urllib3/util/selectors.py +601 -0
  166. webull/core/vendored/requests/packages/urllib3/util/ssl_.py +396 -0
  167. webull/core/vendored/requests/packages/urllib3/util/timeout.py +256 -0
  168. webull/core/vendored/requests/packages/urllib3/util/url.py +252 -0
  169. webull/core/vendored/requests/packages/urllib3/util/wait.py +164 -0
  170. webull/core/vendored/requests/packages.py +28 -0
  171. webull/core/vendored/requests/sessions.py +750 -0
  172. webull/core/vendored/requests/status_codes.py +105 -0
  173. webull/core/vendored/requests/structures.py +119 -0
  174. webull/core/vendored/requests/utils.py +916 -0
  175. webull/core/vendored/six.py +905 -0
  176. webull/data/__init__.py +3 -0
  177. webull/data/common/__init__.py +0 -0
  178. webull/data/common/category.py +26 -0
  179. webull/data/common/connect_ack.py +29 -0
  180. webull/data/common/direction.py +25 -0
  181. webull/data/common/exchange_code.py +33 -0
  182. webull/data/common/exercise_style.py +22 -0
  183. webull/data/common/expiration_cycle.py +26 -0
  184. webull/data/common/instrument_status.py +23 -0
  185. webull/data/common/option_type.py +20 -0
  186. webull/data/common/subscribe_type.py +22 -0
  187. webull/data/common/timespan.py +29 -0
  188. webull/data/data_client.py +35 -0
  189. webull/data/data_streaming_client.py +89 -0
  190. webull/data/internal/__init__.py +0 -0
  191. webull/data/internal/default_retry_policy.py +84 -0
  192. webull/data/internal/exceptions.py +60 -0
  193. webull/data/internal/quotes_client.py +314 -0
  194. webull/data/internal/quotes_decoder.py +40 -0
  195. webull/data/internal/quotes_payload_decoder.py +35 -0
  196. webull/data/internal/quotes_topic.py +36 -0
  197. webull/data/quotes/__init__.py +0 -0
  198. webull/data/quotes/instrument.py +33 -0
  199. webull/data/quotes/market_data.py +187 -0
  200. webull/data/quotes/market_streaming_data.py +66 -0
  201. webull/data/quotes/subscribe/__init__.py +0 -0
  202. webull/data/quotes/subscribe/ask_bid_result.py +49 -0
  203. webull/data/quotes/subscribe/basic_result.py +45 -0
  204. webull/data/quotes/subscribe/broker_result.py +33 -0
  205. webull/data/quotes/subscribe/message_pb2.py +37 -0
  206. webull/data/quotes/subscribe/order_result.py +30 -0
  207. webull/data/quotes/subscribe/payload_type.py +19 -0
  208. webull/data/quotes/subscribe/quote_decoder.py +28 -0
  209. webull/data/quotes/subscribe/quote_result.py +47 -0
  210. webull/data/quotes/subscribe/snapshot_decoder.py +30 -0
  211. webull/data/quotes/subscribe/snapshot_result.py +69 -0
  212. webull/data/quotes/subscribe/tick_decoder.py +29 -0
  213. webull/data/quotes/subscribe/tick_result.py +47 -0
  214. webull/data/request/__init__.py +0 -0
  215. webull/data/request/get_batch_historical_bars_request.py +43 -0
  216. webull/data/request/get_corp_action_request.py +47 -0
  217. webull/data/request/get_eod_bars_request.py +32 -0
  218. webull/data/request/get_historical_bars_request.py +43 -0
  219. webull/data/request/get_instruments_request.py +30 -0
  220. webull/data/request/get_quotes_request.py +35 -0
  221. webull/data/request/get_snapshot_request.py +38 -0
  222. webull/data/request/get_tick_request.py +37 -0
  223. webull/data/request/subscribe_request.py +43 -0
  224. webull/data/request/unsubscribe_request.py +42 -0
  225. webull/trade/__init__.py +2 -0
  226. webull/trade/common/__init__.py +0 -0
  227. webull/trade/common/account_type.py +22 -0
  228. webull/trade/common/category.py +29 -0
  229. webull/trade/common/combo_ticker_type.py +23 -0
  230. webull/trade/common/combo_type.py +31 -0
  231. webull/trade/common/currency.py +24 -0
  232. webull/trade/common/forbid_reason.py +27 -0
  233. webull/trade/common/instrument_type.py +27 -0
  234. webull/trade/common/markets.py +27 -0
  235. webull/trade/common/order_entrust_type.py +21 -0
  236. webull/trade/common/order_side.py +23 -0
  237. webull/trade/common/order_status.py +25 -0
  238. webull/trade/common/order_tif.py +24 -0
  239. webull/trade/common/order_type.py +30 -0
  240. webull/trade/common/trade_policy.py +22 -0
  241. webull/trade/common/trading_date_type.py +24 -0
  242. webull/trade/common/trailing_type.py +23 -0
  243. webull/trade/events/__init__.py +0 -0
  244. webull/trade/events/default_retry_policy.py +64 -0
  245. webull/trade/events/events_pb2.py +43 -0
  246. webull/trade/events/events_pb2_grpc.py +66 -0
  247. webull/trade/events/signature_composer.py +61 -0
  248. webull/trade/events/types.py +21 -0
  249. webull/trade/request/__init__.py +0 -0
  250. webull/trade/request/cancel_order_request.py +28 -0
  251. webull/trade/request/get_account_balance_request.py +28 -0
  252. webull/trade/request/get_account_positions_request.py +30 -0
  253. webull/trade/request/get_account_profile_request.py +26 -0
  254. webull/trade/request/get_app_subscriptions.py +28 -0
  255. webull/trade/request/get_open_orders_request.py +30 -0
  256. webull/trade/request/get_order_detail_request.py +27 -0
  257. webull/trade/request/get_today_orders_request.py +31 -0
  258. webull/trade/request/get_trade_calendar_request.py +30 -0
  259. webull/trade/request/get_trade_instrument_detail_request.py +24 -0
  260. webull/trade/request/get_trade_security_detail_request.py +42 -0
  261. webull/trade/request/get_tradeable_instruments_request.py +27 -0
  262. webull/trade/request/palce_order_request.py +91 -0
  263. webull/trade/request/place_order_request_v2.py +58 -0
  264. webull/trade/request/replace_order_request.py +73 -0
  265. webull/trade/request/replace_order_request_v2.py +38 -0
  266. webull/trade/request/v2/__init__.py +0 -0
  267. webull/trade/request/v2/cancel_option_request.py +28 -0
  268. webull/trade/request/v2/cancel_order_request.py +28 -0
  269. webull/trade/request/v2/get_account_balance_request.py +28 -0
  270. webull/trade/request/v2/get_account_list.py +23 -0
  271. webull/trade/request/v2/get_account_positions_request.py +24 -0
  272. webull/trade/request/v2/get_order_detail_request.py +26 -0
  273. webull/trade/request/v2/get_order_history_request.py +35 -0
  274. webull/trade/request/v2/palce_order_request.py +87 -0
  275. webull/trade/request/v2/place_option_request.py +64 -0
  276. webull/trade/request/v2/preview_option_request.py +28 -0
  277. webull/trade/request/v2/preview_order_request.py +59 -0
  278. webull/trade/request/v2/replace_option_request.py +28 -0
  279. webull/trade/request/v2/replace_order_request.py +57 -0
  280. webull/trade/trade/__init__.py +0 -0
  281. webull/trade/trade/account_info.py +83 -0
  282. webull/trade/trade/order_operation.py +246 -0
  283. webull/trade/trade/trade_calendar.py +37 -0
  284. webull/trade/trade/trade_instrument.py +72 -0
  285. webull/trade/trade/v2/__init__.py +0 -0
  286. webull/trade/trade/v2/account_info_v2.py +55 -0
  287. webull/trade/trade/v2/order_operation_v2.py +206 -0
  288. webull/trade/trade_client.py +43 -0
  289. webull/trade/trade_events_client.py +233 -0
  290. webull_openapi_python_sdk-1.0.0.dist-info/METADATA +28 -0
  291. webull_openapi_python_sdk-1.0.0.dist-info/RECORD +295 -0
  292. webull_openapi_python_sdk-1.0.0.dist-info/WHEEL +5 -0
  293. webull_openapi_python_sdk-1.0.0.dist-info/licenses/LICENSE +202 -0
  294. webull_openapi_python_sdk-1.0.0.dist-info/licenses/NOTICE +56 -0
  295. webull_openapi_python_sdk-1.0.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,214 @@
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/retry_condition.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ import logging
43
+ import jmespath
44
+ from webull.core.utils import data
45
+ from webull.core.utils import validation
46
+ from webull.core.exception.exceptions import ClientException, ServerException
47
+ from webull.core.exception import error_code
48
+ from webull.core.common.api_type import DEFAULT as HTTP_API_TYPE
49
+
50
+
51
+ logger = logging.getLogger(__name__)
52
+
53
+
54
+ def _find_data_in_retry_config(key_name, request, retry_config, api_type=HTTP_API_TYPE):
55
+ path = '"{0}"."{1}"'.format(api_type, key_name)
56
+ return jmespath.search(path, retry_config)
57
+
58
+
59
+ class RetryCondition(object):
60
+ BLANK_STATUS = 0
61
+ NO_RETRY = 1
62
+ RETRY = 2
63
+ SHOULD_RETRY_WITH_THROTTLING_BACKOFF = 4
64
+
65
+ def should_retry(self, retry_policy_context):
66
+ """Decide whether the previous request should be retried."""
67
+ pass
68
+
69
+
70
+ class NoRetryCondition(RetryCondition):
71
+ def should_retry(self, retry_policy_context):
72
+ return RetryCondition.NO_RETRY
73
+
74
+
75
+ class MaxRetryTimesCondition(RetryCondition):
76
+
77
+ def __init__(self, max_retry_times):
78
+ validation.assert_integer_positive(max_retry_times, "max_retry_times")
79
+ self.max_retry_times = max_retry_times
80
+
81
+ def should_retry(self, retry_policy_context):
82
+
83
+ if retry_policy_context.retries_attempted < self.max_retry_times:
84
+ return RetryCondition.RETRY
85
+ else:
86
+ logger.debug("Reached the maximum number of retry. Attempts:%d",
87
+ retry_policy_context.retries_attempted)
88
+ return RetryCondition.NO_RETRY
89
+
90
+
91
+ class RetryOnExceptionCondition(RetryCondition):
92
+ def __init__(self, retry_config):
93
+ self.retry_config = retry_config
94
+
95
+ def should_retry(self, retry_policy_context):
96
+ request = retry_policy_context.original_request
97
+ exception = retry_policy_context.exception
98
+
99
+ if isinstance(exception, ClientException) and exception.get_error_code() == error_code.SDK_HTTP_ERROR:
100
+ logger.debug("Retryable ClientException:%s", exception)
101
+ return RetryCondition.RETRY
102
+
103
+ if isinstance(exception, ServerException):
104
+ _error_code = exception.get_error_code()
105
+ errors = _find_data_in_retry_config(
106
+ "RetryableNormalErrors", request, self.retry_config)
107
+ if isinstance(errors, list) and _error_code in errors:
108
+ logger.debug("Retryable ServerException:%s", exception)
109
+ return RetryCondition.RETRY
110
+ errors = _find_data_in_retry_config(
111
+ "RetryableThrottlingErrors", request, self.retry_config)
112
+ if isinstance(errors, list) and _error_code in errors:
113
+ logger.debug("Retryable ThrottlingError:%s", exception)
114
+ return RetryCondition.RETRY | RetryCondition.SHOULD_RETRY_WITH_THROTTLING_BACKOFF
115
+
116
+ return RetryCondition.NO_RETRY
117
+
118
+
119
+ class RetryOnMethodTypeCondition(RetryCondition):
120
+ def __init__(self, retry_config):
121
+ self.retry_config = retry_config
122
+
123
+ def should_retry(self, retry_policy_context):
124
+ request = retry_policy_context.original_request
125
+ exception = retry_policy_context.exception
126
+
127
+ if isinstance(exception, (ClientException, ServerException)):
128
+ method = request.get_method()
129
+ methods = _find_data_in_retry_config(
130
+ "RetryableMethods", request, self.retry_config)
131
+ if isinstance(methods, list) and method in methods:
132
+ logger.debug("Retryable MethodType:%s", method)
133
+ return RetryCondition.RETRY
134
+
135
+ return RetryCondition.NO_RETRY
136
+
137
+
138
+ class RetryOnHttpStatusCondition(RetryCondition):
139
+
140
+ DEFAULT_RETRYABLE_HTTP_STATUS_LIST = [
141
+ 500, 502, 503, 504
142
+ ]
143
+
144
+ def __init__(self, retryable_http_status_list=None):
145
+ if retryable_http_status_list:
146
+ self.retryable_http_status_list = retryable_http_status_list
147
+ else:
148
+ self.retryable_http_status_list = self.DEFAULT_RETRYABLE_HTTP_STATUS_LIST
149
+
150
+ def should_retry(self, retry_policy_context):
151
+ if retry_policy_context.http_status_code in self.retryable_http_status_list:
152
+ logger.debug(
153
+ "Retryable HTTP error occurred. HTTP status code: %s",
154
+ retry_policy_context.http_status_code)
155
+ return RetryCondition.RETRY
156
+ else:
157
+ return RetryCondition.NO_RETRY
158
+
159
+
160
+ class MergeRetryCondition(RetryCondition):
161
+ def __init__(self, conditions):
162
+ self.conditions = conditions
163
+
164
+ def should_retry(self, retry_policy_context):
165
+ retryable = RetryCondition.BLANK_STATUS
166
+ for condition in self.conditions:
167
+ retryable |= condition.should_retry(retry_policy_context)
168
+ return retryable
169
+
170
+
171
+ class MergeAndRetryCondition(RetryCondition):
172
+ def __init__(self, conditions):
173
+ self.conditions = conditions
174
+
175
+ def should_retry(self, retry_policy_context):
176
+ retryable = RetryCondition.BLANK_STATUS
177
+ no_retry = RetryCondition.NO_RETRY
178
+ retry_mask = RetryCondition.RETRY | RetryCondition.SHOULD_RETRY_WITH_THROTTLING_BACKOFF
179
+ for condition in self.conditions:
180
+ ret = condition.should_retry(retry_policy_context)
181
+ retryable |= ret & retry_mask
182
+ no_retry &= ret & RetryCondition.NO_RETRY
183
+ return retryable | no_retry
184
+
185
+
186
+ class DefaultMixedRetryCondition(RetryCondition):
187
+ def __init__(self, max_retry_times, retry_config):
188
+ RetryCondition.__init__(self)
189
+ self._condition = MergeRetryCondition([
190
+ MaxRetryTimesCondition(max_retry_times),
191
+ RetryOnMethodTypeCondition(retry_config),
192
+ MergeAndRetryCondition([
193
+ RetryOnExceptionCondition(retry_config),
194
+ RetryOnHttpStatusCondition(),
195
+ ]),
196
+ ])
197
+
198
+ def should_retry(self, retry_policy_context):
199
+ return self._condition.should_retry(retry_policy_context)
200
+
201
+
202
+ class DefaultConfigRetryCondition(DefaultMixedRetryCondition):
203
+ DEFAULT_MAX_RETRY_TIMES = 3
204
+ RETRY_CONFIG_FILE = "retry_config.json"
205
+ _loaded_config = None
206
+
207
+ def __init__(self, max_retry_times=None):
208
+ if not self._loaded_config:
209
+ self._loaded_config = data._load_json_from_data_dir(
210
+ self.RETRY_CONFIG_FILE)
211
+ if max_retry_times is None:
212
+ max_retry_times = self.DEFAULT_MAX_RETRY_TIMES
213
+ DefaultMixedRetryCondition.__init__(
214
+ self, max_retry_times, self._loaded_config)
@@ -0,0 +1,63 @@
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/retry_policy.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ from webull.core.retry.retry_condition import RetryCondition, NoRetryCondition, \
43
+ DefaultConfigRetryCondition
44
+ from webull.core.retry.backoff_strategy import BackoffStrategy, NoDelayStrategy, DefaultMixedBackoffStrategy
45
+
46
+
47
+ class RetryPolicy(RetryCondition, BackoffStrategy):
48
+ def __init__(self, retry_condition, backoff_strategy):
49
+ self.retry_condition = retry_condition
50
+ self.backoff_strategy = backoff_strategy
51
+
52
+ def should_retry(self, retry_policy_context):
53
+ return self.retry_condition.should_retry(retry_policy_context)
54
+
55
+ def compute_delay_before_next_retry(self, retry_policy_context):
56
+ return self.backoff_strategy.compute_delay_before_next_retry(retry_policy_context)
57
+
58
+
59
+ NO_RETRY_POLICY = RetryPolicy(NoRetryCondition(), NoDelayStrategy())
60
+
61
+
62
+ def get_default_retry_policy(max_retry_times=None):
63
+ return RetryPolicy(DefaultConfigRetryCondition(max_retry_times), DefaultMixedBackoffStrategy())
@@ -0,0 +1,51 @@
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/retry_policy_context.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ from webull.core.retry.retry_condition import RetryCondition
43
+
44
+ class RetryPolicyContext:
45
+
46
+ def __init__(self, original_request, exception, retries_attempted, http_status_code):
47
+ self.original_request = original_request
48
+ self.exception = exception
49
+ self.retries_attempted = retries_attempted
50
+ self.http_status_code = http_status_code
51
+ self.retryable = RetryCondition.BLANK_STATUS
File without changes
@@ -0,0 +1,62 @@
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
+ import base64
17
+ import hashlib
18
+ import socket
19
+ from datetime import datetime
20
+ import uuid
21
+ import json
22
+ from webull.core.compat import ensure_bytes, ensure_string
23
+
24
+ TIME_ZONE = "UTC"
25
+ FORMAT_ISO_8601 = "%Y-%m-%dT%H:%M:%SZ"
26
+ FORMAT_ISO_8601_MILLIS = "%Y-%m-%dT%H:%M:%S.%fZ"
27
+
28
+ def get_uuid():
29
+ name = socket.gethostname() + str(uuid.uuid1())
30
+ namespace = uuid.NAMESPACE_URL
31
+ return str(uuid.uuid5(namespace, name))
32
+
33
+ def get_iso_8601_date(dt_as_utc=None):
34
+ if dt_as_utc:
35
+ return dt_as_utc.strftime(FORMAT_ISO_8601)
36
+ d = datetime.utcnow()
37
+ return d.strftime(FORMAT_ISO_8601)
38
+
39
+ def get_iso_8601_date_with_millis(dt_as_utc=None):
40
+ if dt_as_utc:
41
+ d = dt_as_utc
42
+ else:
43
+ d = datetime.utcnow()
44
+ ret = d.strftime(FORMAT_ISO_8601_MILLIS)
45
+ if len(ret) != 27:
46
+ raise RuntimeError("failed to convent timestamp, result: %s" % ret)
47
+ return ret[:-4] + ret[-1:]
48
+
49
+ def parse_timestamp_to_dt(timestamp_of_millis):
50
+ return datetime.utcfromtimestamp(timestamp_of_millis / 1000.0)
51
+
52
+ def md5_sum(content):
53
+ content_bytes = ensure_bytes(content)
54
+ md5_bytes = hashlib.md5(content_bytes).digest()
55
+ return ensure_string(base64.standard_b64encode(md5_bytes))
56
+
57
+ def md5_hex(content):
58
+ content_bytes = ensure_bytes(content)
59
+ return hashlib.md5(content_bytes).hexdigest()
60
+
61
+ def json_dumps_compact(content):
62
+ return json.dumps(content, ensure_ascii=False, separators=(',', ':'))
@@ -0,0 +1,25 @@
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
+ import json
18
+ import os.path
19
+ import webull.core
20
+
21
+ def _load_json_from_data_dir(name):
22
+ entry_dir = os.path.dirname(os.path.abspath(webull.core.__file__))
23
+ json_file = os.path.join(entry_dir, "data", name)
24
+ with open(json_file) as fp:
25
+ return json.load(fp)
@@ -0,0 +1,33 @@
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
+
18
+ def desensitize_token(token, keep_length=6, mask_length=6):
19
+ if not token:
20
+ return token
21
+
22
+ total_length = len(token)
23
+
24
+ # 如果字符串长度不足需要保留长度的两倍,则返回全部内容
25
+ if total_length <= keep_length * 2:
26
+ return token
27
+
28
+ # 提取前保留部分、后保留部分,并用星号连接
29
+ front_part = token[:keep_length]
30
+ rear_part = token[-keep_length:] if keep_length > 0 else ''
31
+ mask = '*' * mask_length
32
+
33
+ return f"{front_part}{mask}{rear_part}"
@@ -0,0 +1,49 @@
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/utils/validation.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ from webull.core.exception.exceptions import ClientException
43
+ from webull.core.exception import error_code
44
+
45
+ def assert_integer_positive(integer, name):
46
+ if isinstance(integer, int) and integer > 0:
47
+ return
48
+ raise ClientException(error_code.SDK_INVALID_PARAMETER,
49
+ "{0} should be a positive integer.".format(name))
File without changes
@@ -0,0 +1,94 @@
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
+ # __
18
+ # /__) _ _ _ _ _/ _
19
+ # / ( (- (/ (/ (- _) / _)
20
+ # /
21
+
22
+ """
23
+ Requests HTTP Library
24
+ ~~~~~~~~~~~~~~~~~~~~~
25
+
26
+ Requests is an HTTP library, written in Python, for human beings. Basic GET
27
+ usage:
28
+
29
+ >>> import requests
30
+ >>> r = requests.get('https://www.python.org')
31
+ >>> r.status_code
32
+ 200
33
+ >>> 'Python is a programming language' in r.content
34
+ True
35
+
36
+ ... or POST:
37
+
38
+ >>> payload = dict(key1='value1', key2='value2')
39
+ >>> r = requests.post('http://httpbin.org/post', data=payload)
40
+ >>> print(r.text)
41
+ {
42
+ ...
43
+ "form": {
44
+ "key2": "value2",
45
+ "key1": "value1"
46
+ },
47
+ ...
48
+ }
49
+
50
+ The other HTTP methods are supported - see `requests.api`. Full documentation
51
+ is at <http://python-requests.org>.
52
+
53
+ :copyright: (c) 2017 by Kenneth Reitz.
54
+ :license: Apache 2.0, see LICENSE for more details.
55
+ """
56
+
57
+ __title__ = 'requests'
58
+ __version__ = '2.18.3'
59
+
60
+ # Attempt to enable urllib3's SNI support, if possible
61
+ try:
62
+ from .packages.urllib3.contrib import pyopenssl
63
+ pyopenssl.inject_into_urllib3()
64
+ except ImportError:
65
+ pass
66
+
67
+
68
+ from . import utils
69
+ from .models import Request, Response, PreparedRequest
70
+ from .api import request, get, head, post, patch, put, delete, options
71
+ from .sessions import session, Session
72
+ from .status_codes import codes
73
+ from .exceptions import (
74
+ RequestException, Timeout, URLRequired,
75
+ TooManyRedirects, HTTPError, ConnectionError,
76
+ FileModeWarning, ConnectTimeout, ReadTimeout
77
+ )
78
+
79
+ # Set default logging handler to avoid "No handler found" warnings.
80
+ import logging
81
+ try: # Python 2.7+
82
+ from logging import NullHandler
83
+ except ImportError:
84
+ class NullHandler(logging.Handler):
85
+ def emit(self, record):
86
+ """
87
+ null handler
88
+ """
89
+ pass
90
+
91
+ logging.getLogger(__name__).addHandler(NullHandler())
92
+ logging.getLogger(__name__).setLevel(logging.CRITICAL)
93
+
94
+
@@ -0,0 +1,28 @@
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
+ # .-. .-. .-. . . .-. .-. .-. .-.
16
+ # |( |- |.| | | |- `-. | `-.
17
+ # ' ' `-' `-`.`-' `-' `-' ' `-'
18
+
19
+ __title__ = 'requests'
20
+ __description__ = 'Python HTTP for Humans.'
21
+ __url__ = 'http://python-requests.org'
22
+ __version__ = '2.18.3'
23
+ __build__ = 0x021803
24
+ __author__ = 'Kenneth Reitz'
25
+ __author_email__ = 'me@kennethreitz.org'
26
+ __license__ = 'Apache 2.0'
27
+ __copyright__ = 'Copyright 2017 Kenneth Reitz'
28
+ __cake__ = u'\u2728 \U0001f370 \u2728'
@@ -0,0 +1,56 @@
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
+ """
18
+ requests._internal_utils
19
+ ~~~~~~~~~~~~~~
20
+
21
+ Provides utility functions that are consumed internally by Requests
22
+ which depend on extremely few external helpers (such as compat)
23
+ """
24
+
25
+ from .compat import is_py2, builtin_str, str
26
+
27
+
28
+ def to_native_string(string, encoding='ascii'):
29
+ """Given a string object, regardless of type, returns a representation of
30
+ that string in the native string type, encoding and decoding where
31
+ necessary. This assumes ASCII unless told otherwise.
32
+ """
33
+ if isinstance(string, builtin_str):
34
+ out = string
35
+ else:
36
+ if is_py2:
37
+ out = string.encode(encoding)
38
+ else:
39
+ out = string.decode(encoding)
40
+
41
+ return out
42
+
43
+
44
+ def unicode_is_ascii(u_string):
45
+ """Determine if unicode string only contains ASCII characters.
46
+
47
+ :param str u_string: unicode string to check. Must be unicode
48
+ and not Python 2 `str`.
49
+ :rtype: bool
50
+ """
51
+ assert isinstance(u_string, str)
52
+ try:
53
+ u_string.encode('ascii')
54
+ return True
55
+ except UnicodeEncodeError:
56
+ return False