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,206 @@
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
+ # coding=utf-8
15
+ from webull.core.context.request_context_holder import RequestContextHolder
16
+ from webull.trade.request.v2.cancel_option_request import CancelOptionRequest
17
+ from webull.trade.request.v2.cancel_order_request import CancelOrderRequest
18
+ from webull.trade.request.v2.get_order_detail_request import OrderDetailRequest
19
+ from webull.trade.request.v2.get_order_history_request import OrderHistoryRequest
20
+ from webull.trade.request.v2.palce_order_request import PlaceOrderRequest
21
+ from webull.trade.request.v2.place_option_request import PlaceOptionRequest
22
+ from webull.trade.request.v2.preview_option_request import PreviewOptionRequest
23
+ from webull.trade.request.v2.preview_order_request import PreviewOrderRequest
24
+ from webull.trade.request.v2.replace_option_request import ReplaceOptionRequest
25
+ from webull.trade.request.v2.replace_order_request import ReplaceOrderRequest
26
+
27
+
28
+ class OrderOperationV2:
29
+ def __init__(self, api_client):
30
+ self.client = api_client
31
+
32
+ def preview_order(self, account_id, preview_orders):
33
+ """
34
+ This interface is currently available only to individual brokerage customers in Webull Japan
35
+ and institutional brokerage clients in Webull Hong Kong. It is not yet available to
36
+ Webull US brokerage customers, but support will be introduced progressively in the future.
37
+ """
38
+ preview_order_request = PreviewOrderRequest()
39
+ preview_order_request.set_account_id(account_id)
40
+ preview_order_request.set_new_orders(preview_orders)
41
+ preview_order_request.finalize_order()
42
+ response = self.client.get_response(preview_order_request)
43
+ return response
44
+
45
+ def place_order(self, account_id, new_orders):
46
+ """
47
+ This interface is currently available only to individual brokerage customers in Webull Japan
48
+ and institutional brokerage clients in Webull Hong Kong. It is not yet available to
49
+ Webull US brokerage customers, but support will be introduced progressively in the future.
50
+ """
51
+ place_order_req = PlaceOrderRequest()
52
+ place_order_req.set_account_id(account_id)
53
+ place_order_req.set_new_orders(new_orders)
54
+ place_order_req.finalize_order()
55
+ place_order_req.add_custom_headers_from_order(new_orders)
56
+ place_order_req.add_custom_headers_from_context()
57
+ response = self.client.get_response(place_order_req)
58
+ return response
59
+
60
+ def replace_order(self, account_id, modify_orders):
61
+ """
62
+ This interface is currently available only to individual brokerage customers in Webull Japan
63
+ and institutional brokerage clients in Webull Hong Kong. It is not yet available to
64
+ Webull US brokerage customers, but support will be introduced progressively in the future.
65
+ """
66
+ replace_order_request = ReplaceOrderRequest()
67
+ replace_order_request.set_account_id(account_id)
68
+ replace_order_request.set_modify_orders(modify_orders)
69
+ replace_order_request.finalize_order()
70
+ response = self.client.get_response(replace_order_request)
71
+ return response
72
+
73
+ def cancel_order_v2(self, account_id, client_order_id):
74
+ """
75
+ This interface is currently available only to individual brokerage customers in Webull Japan
76
+ and institutional brokerage clients in Webull Hong Kong. It is not yet available to
77
+ Webull US brokerage customers, but support will be introduced progressively in the future.
78
+ """
79
+ cancel_order_request = CancelOrderRequest()
80
+ cancel_order_request.set_account_id(account_id)
81
+ cancel_order_request.set_client_order_id(client_order_id)
82
+ response = self.client.get_response(cancel_order_request)
83
+ return response
84
+
85
+ def get_order_detail(self, account_id, client_order_id):
86
+ """
87
+ This interface is exclusively available for Webull Hong Kong brokerage clients.
88
+ Currently, it does not support Webull Japan or Webull U.S. clients,
89
+ but support will be gradually introduced in the future.
90
+ """
91
+ order_detail_request = OrderDetailRequest()
92
+ order_detail_request.set_account_id(account_id)
93
+ order_detail_request.set_client_order_id(client_order_id)
94
+ response = self.client.get_response(order_detail_request)
95
+ return response
96
+
97
+ def get_order_history_request(self, account_id, page_size=None, start_date=None, end_date=None, last_client_order_id=None):
98
+ """
99
+ Historical orders, query the records of the past 7 days. If they are group orders, will be returned together,
100
+ and the number of orders returned on one page may exceed the page_size.
101
+
102
+ :param account_id: Account ID
103
+ :param page_size: Limit the number of records per query to 10 by default.
104
+ :param start_date: Start date (if empty, the default is the last 7 days), in the format of yyyy-MM-dd.
105
+ :param end_date: End date (if empty, the default is the last 7 days), in the format of yyyy-MM-dd.
106
+ :param last_client_order_id: The last order ID from the previous response. For the first page query,
107
+ this parameter is not required.
108
+ """
109
+ order_history_request = OrderHistoryRequest()
110
+ order_history_request.set_account_id(account_id=account_id)
111
+ if page_size:
112
+ order_history_request.set_page_size(page_size=page_size)
113
+ if start_date:
114
+ order_history_request.set_start_date(start_date=start_date)
115
+ if end_date:
116
+ order_history_request.set_end_date(end_date=end_date)
117
+ if last_client_order_id:
118
+ order_history_request.set_last_client_order_id(last_client_order_id=last_client_order_id)
119
+ response = self.client.get_response(order_history_request)
120
+ return response
121
+ def query_order_detail(self, account_id, client_order_id):
122
+ """
123
+ This interface is currently available only to individual and institutional clients
124
+ of Webull Hong Kong brokerages. It is not yet supported for clients of Webull US
125
+ and Webull Japan brokerages, but support will be gradually introduced in the future.
126
+ Paging query pending orders.
127
+
128
+ :param account_id: Account ID
129
+ :param client_order_id: The 3rd party order ID.
130
+ """
131
+ order_detail_request = OrderDetailRequest()
132
+ order_detail_request.set_account_id(account_id)
133
+ order_detail_request.set_client_order_id(client_order_id)
134
+ response = self.client.get_response(order_detail_request)
135
+ return response
136
+
137
+ def preview_option(self, account_id, new_orders):
138
+ """
139
+ This interface is currently available only to individual and institutional clients
140
+ of Webull Hong Kong brokerages. It is not yet supported for clients of Webull US
141
+ and Webull Japan brokerages, but support will be gradually introduced in the future.
142
+ """
143
+ preview_option_request = PreviewOptionRequest()
144
+ preview_option_request.set_new_orders(new_orders)
145
+ preview_option_request.set_account_id(account_id)
146
+ response = self.client.get_response(preview_option_request)
147
+ return response
148
+
149
+ def place_option(self, account_id, new_orders):
150
+ """
151
+ This interface is currently available only to individual and institutional clients
152
+ of Webull Hong Kong brokerages. It is not yet supported for clients of Webull US
153
+ and Webull Japan brokerages, but support will be gradually introduced in the future.
154
+ """
155
+ place_option_request = PlaceOptionRequest()
156
+ place_option_request.set_new_orders(new_orders)
157
+ place_option_request.set_account_id(account_id)
158
+ place_option_request.add_custom_headers_from_order(new_orders)
159
+ place_option_request.add_custom_headers_from_context()
160
+ response = self.client.get_response(place_option_request)
161
+ return response
162
+
163
+ def replace_option(self, account_id, modify_orders):
164
+ """
165
+ This interface is currently available only to individual and institutional clients
166
+ of Webull Hong Kong brokerages. It is not yet supported for clients of Webull US
167
+ and Webull Japan brokerages, but support will be gradually introduced in the future.
168
+ """
169
+ replace_option_request = ReplaceOptionRequest()
170
+ replace_option_request.set_modify_orders(modify_orders)
171
+ replace_option_request.set_account_id(account_id)
172
+ response = self.client.get_response(replace_option_request)
173
+ return response
174
+
175
+ def cancel_option(self, account_id, client_order_id):
176
+ """
177
+ This interface is currently available only to individual and institutional clients
178
+ of Webull Hong Kong brokerages. It is not yet supported for clients of Webull US
179
+ and Webull Japan brokerages, but support will be gradually introduced in the future.
180
+ """
181
+ cancel_option_request = CancelOptionRequest()
182
+ cancel_option_request.set_client_order_id(client_order_id)
183
+ cancel_option_request.set_account_id(account_id)
184
+ response = self.client.get_response(cancel_option_request)
185
+ return response
186
+
187
+ def add_custom_headers(self, headers_map: dict):
188
+ """
189
+ This is an optional feature; you can still make a request without setting it.
190
+ If set, you can specify certain headers to perform specific operations.
191
+ Note: If you set a header, call remove_custom_headers to clean up the header after the request is completed.
192
+
193
+ Currently supported header keys and functions:
194
+ Key:category {See Also: category}
195
+ Function: Frequency limit rules, please refer to the document for details. currently only supports Hong Kong
196
+ """
197
+ if not headers_map or len(headers_map) == 0:
198
+ return
199
+
200
+ RequestContextHolder.get().update(headers_map)
201
+
202
+ def remove_custom_headers(self):
203
+ """
204
+ Clearing headers after the request is completed.
205
+ """
206
+ RequestContextHolder.clear()
@@ -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
+ import logging
15
+ import sys
16
+
17
+ from webull.core.http.initializer.client_initializer import ClientInitializer
18
+ from webull.trade.trade.account_info import Account
19
+ from webull.trade.trade.order_operation import OrderOperation
20
+ from webull.trade.trade.trade_calendar import TradeCalendar
21
+ from webull.trade.trade.trade_instrument import TradeInstrument
22
+ from webull.trade.trade.v2.account_info_v2 import AccountV2
23
+ from webull.trade.trade.v2.order_operation_v2 import OrderOperationV2
24
+
25
+
26
+ class TradeClient:
27
+ def __init__(self, api_client):
28
+ self._init_logger(api_client)
29
+ ClientInitializer.initializer(api_client)
30
+ self.account = Account(api_client)
31
+ self.account_v2 = AccountV2(api_client)
32
+ self.order = OrderOperation(api_client)
33
+ self.order_v2 = OrderOperationV2(api_client)
34
+ self.trade_instrument = TradeInstrument(api_client)
35
+ self.trade_calendar = TradeCalendar(api_client)
36
+
37
+ def _init_logger(self, api_client):
38
+ # No logger configured, using default console and local file logging.
39
+ if not getattr(api_client, '_stream_logger_set', False) and not getattr(api_client, '_file_logger_set', False):
40
+ log_format = '%(thread)d %(asctime)s %(name)s %(levelname)s %(message)s'
41
+ log_file_path = 'webull_trade_sdk.log'
42
+ api_client.set_stream_logger(stream=sys.stdout, format_string=log_format)
43
+ api_client.set_file_logger(path=log_file_path, log_level=logging.INFO, format_string=log_format)
@@ -0,0 +1,233 @@
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 json
17
+ import logging
18
+ import threading
19
+ import time
20
+
21
+ import grpc
22
+ from webull.core.common import api_type
23
+ from webull.core.endpoint.default_endpoint_resolver import \
24
+ DefaultEndpointResolver
25
+ from webull.core.endpoint.resolver_endpoint_request import \
26
+ ResolveEndpointRequest
27
+ from webull.core.retry.retry_condition import RetryCondition
28
+
29
+ import webull.trade.events.events_pb2 as pb
30
+ import webull.trade.events.events_pb2_grpc as pb_grpc
31
+ from webull.trade.events.default_retry_policy import (
32
+ DefaultSubscribeRetryPolicy, SubscribeRetryPolicyContext)
33
+ from webull.trade.events.signature_composer import calc_signature
34
+
35
+ # contentTypes
36
+ JSON = "application/json"
37
+ TEXT = "text/plain"
38
+
39
+ DEFAULT_REGION_ID = "us"
40
+
41
+
42
+ class TradeEventsClient():
43
+ def __init__(self, app_key, app_secret, region_id=DEFAULT_REGION_ID, host=None, port=443, tls_enable=True, retry_policy=None):
44
+ self._app_key = app_key
45
+ self._app_secret = app_secret
46
+ self._region_id = region_id
47
+ self._tls_enable = tls_enable
48
+ self._endpoint_resolver = DefaultEndpointResolver(self)
49
+ if not host:
50
+ endpoint_request = ResolveEndpointRequest(
51
+ self._region_id, api_type=api_type.EVENTS)
52
+ endpoint = self._endpoint_resolver.resolve(endpoint_request)
53
+ self._host = endpoint
54
+ else:
55
+ self._host = host
56
+ self._port = port
57
+ if retry_policy:
58
+ self._retry_policy = retry_policy
59
+ else:
60
+ self._retry_policy = DefaultSubscribeRetryPolicy()
61
+
62
+ self._logger = None
63
+ self._in_callback_mutex = threading.Lock()
64
+ self._callback_mutex = threading.RLock()
65
+ # callbacks
66
+ self._on_connect = None
67
+ self._on_events_message = None
68
+ self._on_log = None
69
+
70
+ def _build_request(self, app_key, app_secret, accounts):
71
+ request = pb.SubscribeRequest(
72
+ subscribeType=1, # only 1 allowed now
73
+ timestamp=int(time.time() * 1000), # millis
74
+ accounts=accounts,
75
+ )
76
+ signature, metadata = calc_signature(app_key, app_secret, request)
77
+ return request, metadata
78
+
79
+ def _stream_processing(self, stub, accounts):
80
+ retry_policy_context = SubscribeRetryPolicyContext(None, 0, None)
81
+ retries = 0
82
+ final_exception = None
83
+ while True:
84
+ request, metadata = self._build_request(
85
+ self._app_key, self._app_secret, accounts)
86
+ try:
87
+ response_iterator = stub.Subscribe(
88
+ request=request, metadata=metadata)
89
+ for response in response_iterator:
90
+ self._easy_handler(response)
91
+ except grpc.RpcError as rpc_error:
92
+ state = rpc_error._state
93
+ final_exception = rpc_error
94
+ retry_policy_context = SubscribeRetryPolicyContext(
95
+ None, retries, state.code)
96
+ self._easy_log(logging.ERROR, "grpc error code:%s, error msg:%s, details:%s",
97
+ state.code, state.details, state.debug_error_string)
98
+ except Exception as exception:
99
+ final_exception = exception
100
+ retry_policy_context = SubscribeRetryPolicyContext(
101
+ exception, retries, None)
102
+ self._easy_log(logging.ERROR, "grpc exception:%s", exception)
103
+ retryable = self._retry_policy.should_retry(retry_policy_context)
104
+ if retryable & RetryCondition.NO_RETRY:
105
+ self._easy_log(
106
+ logging.ERROR, "processing will stopped due to not be retryable, retry_context:%s", retry_policy_context)
107
+ break
108
+ retry_policy_context.retryable = retryable
109
+ time_to_sleep = self._retry_policy.compute_delay_before_next_retry(
110
+ retry_policy_context)
111
+ self._easy_log(logging.INFO, "next retry will be started in %s ms, retry_context:%s",
112
+ time_to_sleep, retry_policy_context)
113
+ time.sleep(time_to_sleep / 1000.0)
114
+ retries += 1
115
+ retry_policy_context.retries_attempted = retries
116
+ if final_exception:
117
+ raise final_exception
118
+
119
+ @property
120
+ def on_connect(self):
121
+ return self._on_connect
122
+
123
+ @on_connect.setter
124
+ def on_connect(self, func):
125
+ with self._callback_mutex:
126
+ self._on_connect = func
127
+
128
+ @property
129
+ def on_events_message(self):
130
+ return self._on_events_message
131
+
132
+ @on_events_message.setter
133
+ def on_events_message(self, func):
134
+ with self._callback_mutex:
135
+ self._on_events_message = func
136
+
137
+ @property
138
+ def on_log(self):
139
+ return self._on_log
140
+
141
+ @on_log.setter
142
+ def on_log(self, func):
143
+ self._on_log = func
144
+
145
+ def _easy_log(self, level, fmt, *args):
146
+ if self.on_log is not None:
147
+ buf = fmt % args
148
+ try:
149
+ self.on_log(level, buf)
150
+ except Exception:
151
+ pass
152
+ if self._logger is not None:
153
+ self._logger.log(level, fmt, *args)
154
+
155
+ def enable_logger(self, logger=None):
156
+ if logger is None:
157
+ if self._logger is not None:
158
+ return
159
+ logger = logging.getLogger(__name__)
160
+ self._logger = logger
161
+
162
+ def disable_logger(self):
163
+ self._logger = None
164
+
165
+ def _handle_subscribe_success(self, response):
166
+ self._easy_log(
167
+ logging.INFO, "subscribe success, response:%s", response)
168
+ with self._callback_mutex:
169
+ on_connect = self.on_connect
170
+ if not on_connect:
171
+ return
172
+ with self._in_callback_mutex:
173
+ try:
174
+ on_connect(self, response.payload, response)
175
+ except Exception as err:
176
+ self._easy_log(
177
+ logging.ERROR, 'Caught exception in on_connect: %s', err)
178
+ raise err
179
+
180
+ def _handle_default(self, response, level):
181
+ self._easy_log(level, "response:%s", response)
182
+
183
+ def _handle_message(self, response):
184
+ self._easy_log(
185
+ logging.DEBUG, "message received, response:%s", response)
186
+ with self._callback_mutex:
187
+ on_events_message = self.on_events_message
188
+ if not on_events_message:
189
+ return
190
+ with self._in_callback_mutex:
191
+ content_type = response.contentType
192
+ _payload = response.payload
193
+ if JSON == content_type:
194
+ try:
195
+ _payload = json.loads(response.payload)
196
+ except Exception as err:
197
+ self._easy_log(
198
+ logging.ERROR, 'Caught exception in decode message: %s, %s', _payload, err)
199
+ raise err
200
+ try:
201
+ on_events_message(response.eventType,
202
+ response.subscribeType, _payload, response)
203
+ except Exception as err:
204
+ self._easy_log(
205
+ logging.ERROR, 'Caught exception in on_events_message: %s', err)
206
+ raise err
207
+
208
+ def _easy_handler(self, response):
209
+ event_type = response.eventType
210
+ if event_type == pb.SubscribeSuccess:
211
+ self._handle_subscribe_success(response)
212
+ elif event_type == pb.Ping:
213
+ self._handle_default(response, logging.DEBUG)
214
+ elif event_type == pb.AuthError:
215
+ self._handle_default(response, logging.FATAL)
216
+ elif event_type == pb.NumOfConnExceed:
217
+ self._handle_default(response, logging.FATAL)
218
+ elif event_type == pb.SubscribeExpired:
219
+ self._handle_default(response, logging.FATAL)
220
+ else:
221
+ self._handle_message(response)
222
+
223
+ def do_subscribe(self, accounts):
224
+ target = self._host + ":" + str(self._port)
225
+ if self._tls_enable:
226
+ ssl_channel_credentials = grpc.ssl_channel_credentials()
227
+ with grpc.secure_channel(target, ssl_channel_credentials) as channel:
228
+ stub = pb_grpc.EventServiceStub(channel)
229
+ self._stream_processing(stub, accounts)
230
+ else:
231
+ with grpc.insecure_channel(target) as channel:
232
+ stub = pb_grpc.EventServiceStub(channel)
233
+ self._stream_processing(stub, accounts)
@@ -0,0 +1,28 @@
1
+ Metadata-Version: 2.4
2
+ Name: webull-openapi-python-sdk
3
+ Version: 1.0.0
4
+ Summary: Webull Python SDK.
5
+ Home-page:
6
+ Author: Webull
7
+ Author-email:
8
+ License: Apache License 2.0
9
+ Platform: any
10
+ Requires-Python: >=3.8,<3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ License-File: NOTICE
14
+ Requires-Dist: jmespath<1.0.0,>=0.9.3
15
+ Requires-Dist: cryptography>=2.6.0
16
+ Requires-Dist: cachetools==5.2.0
17
+ Requires-Dist: paho-mqtt==1.6.1
18
+ Requires-Dist: protobuf==4.21.12
19
+ Requires-Dist: grpcio==1.51.1
20
+ Requires-Dist: grpcio-tools==1.51.1
21
+ Dynamic: author
22
+ Dynamic: description-content-type
23
+ Dynamic: license
24
+ Dynamic: license-file
25
+ Dynamic: platform
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary