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,79 @@
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
+
38
+ from webull.core.http.initializer.token.token_manager import TokenManager
39
+
40
+ logger = logging.getLogger(__name__)
41
+
42
+
43
+ class ClientInitializer:
44
+ """Client initialization logic."""
45
+
46
+ @staticmethod
47
+ def initializer(api_client):
48
+ """Client Initialization"""
49
+ ClientInitializer.init_token(api_client)
50
+
51
+ @staticmethod
52
+ def init_token(api_client):
53
+ """Initialize token"""
54
+ if not ClientInitializer.check_region_token_enable(api_client):
55
+ return
56
+
57
+ token_manager = TokenManager()
58
+ token_manager.init_token(api_client)
59
+
60
+ @staticmethod
61
+ def check_region_token_enable(api_client):
62
+ """
63
+ Check whether token checking is enabled in the specified region
64
+ """
65
+ if api_client is None:
66
+ logger.warning("check_region_token_enable api_client is null, return False")
67
+ return False
68
+
69
+ if not api_client.get_region_id():
70
+ logger.warning("check_region_token_enable region_id is null, return False")
71
+ return False
72
+
73
+ enable_region_ids = ["hk"]
74
+ result = api_client.get_region_id() in enable_region_ids
75
+ logger.info(
76
+ "check_region_token_enable result is %s, enable regionIds is %s. current regionsId is %s",
77
+ result, enable_region_ids, api_client.get_region_id()
78
+ )
79
+ return result
File without changes
File without changes
@@ -0,0 +1,40 @@
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
+ class AccessToken:
37
+ def __init__(self, token, expires, status):
38
+ self.token = token
39
+ self.expires = expires
40
+ self.status = status
@@ -0,0 +1,44 @@
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
+ from webull.core.request import ApiRequest
37
+
38
+
39
+ class CheckTokenRequest(ApiRequest):
40
+ def __init__(self):
41
+ super().__init__("/auth/token/check", version='v2', method="POST", body_params={})
42
+
43
+ def set_token(self, token):
44
+ self.add_body_params("token", token)
@@ -0,0 +1,45 @@
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
+ from webull.core.request import ApiRequest
37
+
38
+
39
+ class CreateTokenRequest(ApiRequest):
40
+ def __init__(self):
41
+ super().__init__("/auth/token/create", version='v2', method="POST", body_params={})
42
+
43
+ def set_token(self, token):
44
+ if token:
45
+ self.add_body_params("token", token)
@@ -0,0 +1,44 @@
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
+ from webull.core.request import ApiRequest
37
+
38
+
39
+ class RefreshTokenRequest(ApiRequest):
40
+ def __init__(self):
41
+ super().__init__("/auth/token/refresh", version='v2', method="POST", body_params={})
42
+
43
+ def set_token(self, token):
44
+ self.add_body_params("token", token)
@@ -0,0 +1,208 @@
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 time
39
+ from pathlib import Path
40
+
41
+ from webull.core import compat
42
+ from webull.core.utils import desensitize
43
+ from webull.core.exception.exceptions import ClientException
44
+ from webull.core.http.initializer.token.bean.access_token import AccessToken
45
+ from webull.core.http.initializer.token.token_operation import TokenOperation
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ 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
+
54
+ def __init__(self):
55
+ dir_path = Path(self.CONF_ENV_TOKEN_DIR) if self.CONF_ENV_TOKEN_DIR else Path(self.DEFAULT_ENV_TOKEN_DIR)
56
+ self.token_file_path = dir_path / self.TOKEN_FILE_NAME
57
+
58
+ def init_token(self, api_client):
59
+ local_access_token = self.load_token_from_local()
60
+ local_token = local_access_token.token if local_access_token else None
61
+ server_access_token = self.fetch_token_from_server(api_client, local_token)
62
+ self.save_token_to_local(server_access_token)
63
+
64
+ if server_access_token.get("status") != 1:
65
+ msg = ("init_token status not verified error. token:%s expires:%s status:%s" %
66
+ (desensitize.desensitize_token(server_access_token.get("token")), server_access_token.get("expires"), server_access_token.get("status")))
67
+ logger.error(msg)
68
+ raise ClientException("ERROR_INIT_TOKEN", msg)
69
+
70
+ logger.info("init_token finished. token:%s expires:%s status:%s",
71
+ desensitize.desensitize_token(server_access_token.get("token")), server_access_token.get("expires"), server_access_token.get("status"))
72
+ access_token = server_access_token.get("token")
73
+ api_client.set_token(access_token)
74
+ return access_token
75
+
76
+ def load_token_from_local(self):
77
+ if not self.token_file_path.exists():
78
+ logger.info("load_token_from_local file not exists, file:%s.", self.token_file_path)
79
+ return None
80
+ try:
81
+ logger.info("load_token_from_local reading token from file...")
82
+ with open(self.token_file_path, "r", encoding="utf-8") as f:
83
+ token = f.readline().strip()
84
+
85
+ str_expires = f.readline().strip()
86
+ if str_expires and str_expires.isdigit():
87
+ expires = int(str_expires)
88
+ else:
89
+ expires = 0
90
+
91
+ str_status = f.readline().strip()
92
+ if str_status and str_status.isdigit():
93
+ status = int(str_status)
94
+ else:
95
+ status = 0
96
+
97
+ logger.info("load_token_from_local read local token result. token:%s expires:%s status:%s",
98
+ desensitize.desensitize_token(token), expires, status)
99
+ return AccessToken(token=token, expires=expires, status=status)
100
+ except Exception as e:
101
+ logger.error("load_token_from_local failed, file:%s.", self.token_file_path, exc_info=True)
102
+ raise ClientException("ERROR_LOAD_TOKEN") from e
103
+
104
+ def save_token_to_local(self, server_access_token):
105
+ try:
106
+ logger.info("save_token_to_local writing token to local file. token:%s expires:%s status:%s",
107
+ server_access_token.get("token"), server_access_token.get("expires"), server_access_token.get("status"))
108
+ self.token_file_path.parent.mkdir(parents=True, exist_ok=True)
109
+ with open(self.token_file_path, "w", encoding="utf-8") as f:
110
+ f.write(server_access_token.get("token") + "\n")
111
+ f.write(str(server_access_token.get("expires")) + "\n")
112
+ f.write(str(server_access_token.get("status")) + "\n")
113
+ logger.info("save_token_to_local writing token to file completed.")
114
+ except Exception as e:
115
+ logger.error("save_token_to_local failed, file:%s.", self.token_file_path, exc_info=True)
116
+ raise ClientException("ERROR_SAVE_TOKEN") from e
117
+
118
+ def fetch_token_from_server(self, api_client, local_token ):
119
+ token_operation = TokenOperation(api_client)
120
+
121
+ create_access_token = self.create_token(token_operation, local_token)
122
+ if create_access_token.get("status") == 1:
123
+ logger.info("fetch_token_from_server create_token status is verified, no further check required, returning directly. token:%s expires:%s status:%s",
124
+ desensitize.desensitize_token(create_access_token.get("token")), create_access_token.get("expires"), create_access_token.get("status"))
125
+ return create_access_token
126
+
127
+ return self.check_token(api_client, token_operation, create_access_token)
128
+
129
+ @staticmethod
130
+ def create_token(token_operation, local_token):
131
+ response = token_operation.create_token(local_token)
132
+
133
+ if response.status_code != 200:
134
+ msg = "fetch_token_from_server create_token returned non-200 response, raising exception. status_code:%s" % response.status_code
135
+ logger.error(compat.ensure_string(msg))
136
+ raise ClientException("ERROR_CREATE_TOKEN", msg)
137
+
138
+ create_access_token = response.json()
139
+ if not create_access_token:
140
+ msg = "fetch_token_from_server create_token result is empty."
141
+ logger.error(msg)
142
+ raise ClientException("ERROR_CREATE_TOKEN", msg)
143
+
144
+ if not create_access_token.get("token") or not create_access_token.get("expires") or create_access_token.get("status") is None:
145
+ msg = ("fetch_token_from_server create_token result is empty. token:%s expires:%s status:%s" %
146
+ (desensitize.desensitize_token(create_access_token.get("token")), create_access_token.get("expires"), create_access_token.get("status")))
147
+ logger.error(msg)
148
+ raise ClientException("ERROR_CREATE_TOKEN", msg)
149
+
150
+ logger.info("fetch_token_from_server create_token result. token:%s expires:%s status:%s" ,
151
+ desensitize.desensitize_token(create_access_token.get("token")), create_access_token.get("expires"), create_access_token.get("status"))
152
+ return create_access_token
153
+
154
+ @staticmethod
155
+ def check_token(api_client, token_operation, create_access_token):
156
+ duration_seconds = api_client.get_token_check_duration_seconds()
157
+ wait_time = api_client.get_token_check_interval_seconds()
158
+ start_time = time.time()
159
+ logger.info("fetch_token_from_server started check_token loop. token:%s duration_seconds:%s interval_seconds:%s",
160
+ desensitize.desensitize_token(create_access_token.get("token")), duration_seconds, wait_time)
161
+
162
+ while True:
163
+ loop_start = time.time()
164
+ response = token_operation.check_token(create_access_token.get("token"))
165
+
166
+ if response.status_code != 200:
167
+ msg = "fetch_token_from_server check_token returned non-200 response, raising exception. status_code:%s" % response.status_code
168
+ logger.error(compat.ensure_string(msg))
169
+ raise ClientException("ERROR_CHECK_TOKEN", msg)
170
+
171
+ check_access_token = response.json()
172
+ if not check_access_token:
173
+ msg = "fetch_token_from_server check_token result is empty."
174
+ logger.error(msg)
175
+ raise ClientException("ERROR_CHECK_TOKEN", msg)
176
+
177
+ logger.info("fetch_token_from_server result of current check_token loop. token:%s expires:%s status:%s",
178
+ desensitize.desensitize_token(check_access_token.get("token")),
179
+ check_access_token.get("expires"), check_access_token.get("status"))
180
+
181
+ if not check_access_token.get("token") or not check_access_token.get("expires") or check_access_token.get(
182
+ "status") is None:
183
+ msg = ("fetch_token_from_server check_token result is empty. token:%s expires:%s status:%s" %
184
+ (desensitize.desensitize_token(check_access_token.get("token")),
185
+ check_access_token.get("expires"), check_access_token.get("status")))
186
+ logger.error(msg)
187
+ raise ClientException("ERROR_CHECK_TOKEN", msg)
188
+
189
+ if check_access_token.get("status") == 1:
190
+ logger.info(
191
+ "fetch_token_from_server check_token status is verified, no further check required, returning directly. token:%s expires:%s status:%s",
192
+ desensitize.desensitize_token(create_access_token.get("token")), create_access_token.get("expires"),
193
+ create_access_token.get("status"))
194
+ return check_access_token
195
+
196
+ elapsed = time.time() - start_time
197
+ curr_elapsed = time.time() - loop_start
198
+ sleep_time = wait_time - curr_elapsed
199
+ if elapsed >= duration_seconds:
200
+ break
201
+
202
+ logger.info("fetch_token_from_server status not verified, check_token loop will start, waiting %s seconds... (elapsed %ss / %ss). For more details, please visit the OpenAPI official website." % (wait_time, int(elapsed), duration_seconds))
203
+ if sleep_time > 0:
204
+ time.sleep(sleep_time)
205
+
206
+ logger.warning(
207
+ "fetch_token_from_server check_token loop completed. reached the maximum retries without passing validation, returning create_access_token. For more details, please visit the OpenAPI official website.")
208
+ return create_access_token
@@ -0,0 +1,72 @@
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
+
38
+ from webull.core.http.initializer.token.bean.check_token_request import CheckTokenRequest
39
+ from webull.core.http.initializer.token.bean.create_token_request import CreateTokenRequest
40
+ from webull.core.http.initializer.token.bean.refresh_token_request import RefreshTokenRequest
41
+
42
+
43
+ class TokenOperation:
44
+ def __init__(self, api_client):
45
+ self.client = api_client
46
+
47
+ def create_token(self, token):
48
+ """
49
+ Create a token
50
+ """
51
+ create_token_request = CreateTokenRequest()
52
+ create_token_request.set_token(token)
53
+ response = self.client.get_response(create_token_request)
54
+ return response
55
+
56
+ def check_token(self, token):
57
+ """
58
+ Check whether the token is verified
59
+ """
60
+ check_token_request = CheckTokenRequest()
61
+ check_token_request.set_token(token)
62
+ response = self.client.get_response(check_token_request)
63
+ return response
64
+
65
+ def refresh_token(self, token):
66
+ """
67
+ Refresh token
68
+ """
69
+ refresh_token_request = RefreshTokenRequest()
70
+ refresh_token_request.set_token(token)
71
+ response = self.client.get_response(refresh_token_request)
72
+ return response
@@ -0,0 +1,43 @@
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/method_type.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ GET = "GET"
43
+ POST = "POST"
@@ -0,0 +1,43 @@
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/protocol_type.py
39
+ which was part of Alibaba Group.
40
+ """
41
+
42
+ HTTP = "http"
43
+ HTTPS = "https"