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,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)