webull-openapi-python-sdk 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- samples/__init__.py +1 -0
- samples/data/__init__.py +1 -0
- samples/data/data_client.py +57 -0
- samples/data/data_streaming_client.py +86 -0
- samples/data/data_streaming_client_async.py +101 -0
- samples/trade/__init__.py +0 -0
- samples/trade/trade_client.py +163 -0
- samples/trade/trade_client_v2.py +181 -0
- samples/trade/trade_event_client.py +47 -0
- webull/__init__.py +1 -0
- webull/core/__init__.py +12 -0
- webull/core/auth/__init__.py +0 -0
- webull/core/auth/algorithm/__init__.py +0 -0
- webull/core/auth/algorithm/sha_hmac1.py +65 -0
- webull/core/auth/algorithm/sha_hmac256.py +75 -0
- webull/core/auth/composer/__init__.py +0 -0
- webull/core/auth/composer/default_signature_composer.py +125 -0
- webull/core/auth/credentials.py +46 -0
- webull/core/auth/signers/__init__.py +0 -0
- webull/core/auth/signers/app_key_signer.py +72 -0
- webull/core/auth/signers/signer.py +48 -0
- webull/core/auth/signers/signer_factory.py +58 -0
- webull/core/cache/__init__.py +225 -0
- webull/core/client.py +410 -0
- webull/core/common/__init__.py +0 -0
- webull/core/common/api_type.py +19 -0
- webull/core/common/easy_enum.py +35 -0
- webull/core/common/region.py +7 -0
- webull/core/compat.py +85 -0
- webull/core/context/__init__.py +0 -0
- webull/core/context/request_context_holder.py +33 -0
- webull/core/data/endpoints.json +22 -0
- webull/core/data/retry_config.json +15 -0
- webull/core/endpoint/__init__.py +8 -0
- webull/core/endpoint/chained_endpoint_resolver.py +57 -0
- webull/core/endpoint/default_endpoint_resolver.py +60 -0
- webull/core/endpoint/local_config_regional_endpoint_resolver.py +77 -0
- webull/core/endpoint/resolver_endpoint_request.py +46 -0
- webull/core/endpoint/user_customized_endpoint_resolver.py +55 -0
- webull/core/exception/__init__.py +0 -0
- webull/core/exception/error_code.py +23 -0
- webull/core/exception/error_msg.py +21 -0
- webull/core/exception/exceptions.py +53 -0
- webull/core/headers.py +57 -0
- webull/core/http/__init__.py +0 -0
- webull/core/http/initializer/__init__.py +0 -0
- webull/core/http/initializer/client_initializer.py +79 -0
- webull/core/http/initializer/token/__init__.py +0 -0
- webull/core/http/initializer/token/bean/__init__.py +0 -0
- webull/core/http/initializer/token/bean/access_token.py +40 -0
- webull/core/http/initializer/token/bean/check_token_request.py +44 -0
- webull/core/http/initializer/token/bean/create_token_request.py +45 -0
- webull/core/http/initializer/token/bean/refresh_token_request.py +44 -0
- webull/core/http/initializer/token/token_manager.py +208 -0
- webull/core/http/initializer/token/token_operation.py +72 -0
- webull/core/http/method_type.py +43 -0
- webull/core/http/protocol_type.py +43 -0
- webull/core/http/request.py +121 -0
- webull/core/http/response.py +166 -0
- webull/core/request.py +278 -0
- webull/core/retry/__init__.py +0 -0
- webull/core/retry/backoff_strategy.py +102 -0
- webull/core/retry/retry_condition.py +214 -0
- webull/core/retry/retry_policy.py +63 -0
- webull/core/retry/retry_policy_context.py +51 -0
- webull/core/utils/__init__.py +0 -0
- webull/core/utils/common.py +62 -0
- webull/core/utils/data.py +25 -0
- webull/core/utils/desensitize.py +33 -0
- webull/core/utils/validation.py +49 -0
- webull/core/vendored/__init__.py +0 -0
- webull/core/vendored/requests/__init__.py +94 -0
- webull/core/vendored/requests/__version__.py +28 -0
- webull/core/vendored/requests/_internal_utils.py +56 -0
- webull/core/vendored/requests/adapters.py +539 -0
- webull/core/vendored/requests/api.py +166 -0
- webull/core/vendored/requests/auth.py +307 -0
- webull/core/vendored/requests/certs.py +34 -0
- webull/core/vendored/requests/compat.py +85 -0
- webull/core/vendored/requests/cookies.py +555 -0
- webull/core/vendored/requests/exceptions.py +136 -0
- webull/core/vendored/requests/help.py +134 -0
- webull/core/vendored/requests/hooks.py +48 -0
- webull/core/vendored/requests/models.py +960 -0
- webull/core/vendored/requests/packages/__init__.py +17 -0
- webull/core/vendored/requests/packages/certifi/__init__.py +17 -0
- webull/core/vendored/requests/packages/certifi/__main__.py +16 -0
- webull/core/vendored/requests/packages/certifi/cacert.pem +4433 -0
- webull/core/vendored/requests/packages/certifi/core.py +51 -0
- webull/core/vendored/requests/packages/chardet/__init__.py +53 -0
- webull/core/vendored/requests/packages/chardet/big5freq.py +400 -0
- webull/core/vendored/requests/packages/chardet/big5prober.py +61 -0
- webull/core/vendored/requests/packages/chardet/chardistribution.py +247 -0
- webull/core/vendored/requests/packages/chardet/charsetgroupprober.py +120 -0
- webull/core/vendored/requests/packages/chardet/charsetprober.py +159 -0
- webull/core/vendored/requests/packages/chardet/cli/__init__.py +1 -0
- webull/core/vendored/requests/packages/chardet/cli/chardetect.py +99 -0
- webull/core/vendored/requests/packages/chardet/codingstatemachine.py +102 -0
- webull/core/vendored/requests/packages/chardet/compat.py +48 -0
- webull/core/vendored/requests/packages/chardet/cp949prober.py +63 -0
- webull/core/vendored/requests/packages/chardet/enums.py +90 -0
- webull/core/vendored/requests/packages/chardet/escprober.py +115 -0
- webull/core/vendored/requests/packages/chardet/escsm.py +260 -0
- webull/core/vendored/requests/packages/chardet/eucjpprober.py +106 -0
- webull/core/vendored/requests/packages/chardet/euckrfreq.py +209 -0
- webull/core/vendored/requests/packages/chardet/euckrprober.py +61 -0
- webull/core/vendored/requests/packages/chardet/euctwfreq.py +401 -0
- webull/core/vendored/requests/packages/chardet/euctwprober.py +60 -0
- webull/core/vendored/requests/packages/chardet/gb2312freq.py +297 -0
- webull/core/vendored/requests/packages/chardet/gb2312prober.py +60 -0
- webull/core/vendored/requests/packages/chardet/hebrewprober.py +306 -0
- webull/core/vendored/requests/packages/chardet/jisfreq.py +339 -0
- webull/core/vendored/requests/packages/chardet/jpcntx.py +247 -0
- webull/core/vendored/requests/packages/chardet/langbulgarianmodel.py +242 -0
- webull/core/vendored/requests/packages/chardet/langcyrillicmodel.py +347 -0
- webull/core/vendored/requests/packages/chardet/langgreekmodel.py +239 -0
- webull/core/vendored/requests/packages/chardet/langhebrewmodel.py +214 -0
- webull/core/vendored/requests/packages/chardet/langhungarianmodel.py +239 -0
- webull/core/vendored/requests/packages/chardet/langthaimodel.py +213 -0
- webull/core/vendored/requests/packages/chardet/langturkishmodel.py +207 -0
- webull/core/vendored/requests/packages/chardet/latin1prober.py +159 -0
- webull/core/vendored/requests/packages/chardet/mbcharsetprober.py +105 -0
- webull/core/vendored/requests/packages/chardet/mbcsgroupprober.py +68 -0
- webull/core/vendored/requests/packages/chardet/mbcssm.py +586 -0
- webull/core/vendored/requests/packages/chardet/sbcharsetprober.py +146 -0
- webull/core/vendored/requests/packages/chardet/sbcsgroupprober.py +87 -0
- webull/core/vendored/requests/packages/chardet/sjisprober.py +106 -0
- webull/core/vendored/requests/packages/chardet/universaldetector.py +300 -0
- webull/core/vendored/requests/packages/chardet/utf8prober.py +96 -0
- webull/core/vendored/requests/packages/chardet/version.py +23 -0
- webull/core/vendored/requests/packages/urllib3/__init__.py +114 -0
- webull/core/vendored/requests/packages/urllib3/_collections.py +346 -0
- webull/core/vendored/requests/packages/urllib3/connection.py +405 -0
- webull/core/vendored/requests/packages/urllib3/connectionpool.py +910 -0
- webull/core/vendored/requests/packages/urllib3/contrib/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_appengine_environ.py +44 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/bindings.py +607 -0
- webull/core/vendored/requests/packages/urllib3/contrib/_securetransport/low_level.py +360 -0
- webull/core/vendored/requests/packages/urllib3/contrib/appengine.py +303 -0
- webull/core/vendored/requests/packages/urllib3/contrib/ntlmpool.py +125 -0
- webull/core/vendored/requests/packages/urllib3/contrib/pyopenssl.py +484 -0
- webull/core/vendored/requests/packages/urllib3/contrib/securetransport.py +818 -0
- webull/core/vendored/requests/packages/urllib3/contrib/socks.py +206 -0
- webull/core/vendored/requests/packages/urllib3/exceptions.py +260 -0
- webull/core/vendored/requests/packages/urllib3/fields.py +192 -0
- webull/core/vendored/requests/packages/urllib3/filepost.py +112 -0
- webull/core/vendored/requests/packages/urllib3/packages/__init__.py +19 -0
- webull/core/vendored/requests/packages/urllib3/packages/backports/__init__.py +0 -0
- webull/core/vendored/requests/packages/urllib3/packages/backports/makefile.py +67 -0
- webull/core/vendored/requests/packages/urllib3/packages/ordered_dict.py +273 -0
- webull/core/vendored/requests/packages/urllib3/packages/six.py +882 -0
- webull/core/vendored/requests/packages/urllib3/packages/socks.py +887 -0
- webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +19 -0
- webull/core/vendored/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +170 -0
- webull/core/vendored/requests/packages/urllib3/poolmanager.py +467 -0
- webull/core/vendored/requests/packages/urllib3/request.py +164 -0
- webull/core/vendored/requests/packages/urllib3/response.py +721 -0
- webull/core/vendored/requests/packages/urllib3/util/__init__.py +68 -0
- webull/core/vendored/requests/packages/urllib3/util/connection.py +148 -0
- webull/core/vendored/requests/packages/urllib3/util/queue.py +35 -0
- webull/core/vendored/requests/packages/urllib3/util/request.py +132 -0
- webull/core/vendored/requests/packages/urllib3/util/response.py +101 -0
- webull/core/vendored/requests/packages/urllib3/util/retry.py +426 -0
- webull/core/vendored/requests/packages/urllib3/util/selectors.py +601 -0
- webull/core/vendored/requests/packages/urllib3/util/ssl_.py +396 -0
- webull/core/vendored/requests/packages/urllib3/util/timeout.py +256 -0
- webull/core/vendored/requests/packages/urllib3/util/url.py +252 -0
- webull/core/vendored/requests/packages/urllib3/util/wait.py +164 -0
- webull/core/vendored/requests/packages.py +28 -0
- webull/core/vendored/requests/sessions.py +750 -0
- webull/core/vendored/requests/status_codes.py +105 -0
- webull/core/vendored/requests/structures.py +119 -0
- webull/core/vendored/requests/utils.py +916 -0
- webull/core/vendored/six.py +905 -0
- webull/data/__init__.py +3 -0
- webull/data/common/__init__.py +0 -0
- webull/data/common/category.py +26 -0
- webull/data/common/connect_ack.py +29 -0
- webull/data/common/direction.py +25 -0
- webull/data/common/exchange_code.py +33 -0
- webull/data/common/exercise_style.py +22 -0
- webull/data/common/expiration_cycle.py +26 -0
- webull/data/common/instrument_status.py +23 -0
- webull/data/common/option_type.py +20 -0
- webull/data/common/subscribe_type.py +22 -0
- webull/data/common/timespan.py +29 -0
- webull/data/data_client.py +35 -0
- webull/data/data_streaming_client.py +89 -0
- webull/data/internal/__init__.py +0 -0
- webull/data/internal/default_retry_policy.py +84 -0
- webull/data/internal/exceptions.py +60 -0
- webull/data/internal/quotes_client.py +314 -0
- webull/data/internal/quotes_decoder.py +40 -0
- webull/data/internal/quotes_payload_decoder.py +35 -0
- webull/data/internal/quotes_topic.py +36 -0
- webull/data/quotes/__init__.py +0 -0
- webull/data/quotes/instrument.py +33 -0
- webull/data/quotes/market_data.py +187 -0
- webull/data/quotes/market_streaming_data.py +66 -0
- webull/data/quotes/subscribe/__init__.py +0 -0
- webull/data/quotes/subscribe/ask_bid_result.py +49 -0
- webull/data/quotes/subscribe/basic_result.py +45 -0
- webull/data/quotes/subscribe/broker_result.py +33 -0
- webull/data/quotes/subscribe/message_pb2.py +37 -0
- webull/data/quotes/subscribe/order_result.py +30 -0
- webull/data/quotes/subscribe/payload_type.py +19 -0
- webull/data/quotes/subscribe/quote_decoder.py +28 -0
- webull/data/quotes/subscribe/quote_result.py +47 -0
- webull/data/quotes/subscribe/snapshot_decoder.py +30 -0
- webull/data/quotes/subscribe/snapshot_result.py +69 -0
- webull/data/quotes/subscribe/tick_decoder.py +29 -0
- webull/data/quotes/subscribe/tick_result.py +47 -0
- webull/data/request/__init__.py +0 -0
- webull/data/request/get_batch_historical_bars_request.py +43 -0
- webull/data/request/get_corp_action_request.py +47 -0
- webull/data/request/get_eod_bars_request.py +32 -0
- webull/data/request/get_historical_bars_request.py +43 -0
- webull/data/request/get_instruments_request.py +30 -0
- webull/data/request/get_quotes_request.py +35 -0
- webull/data/request/get_snapshot_request.py +38 -0
- webull/data/request/get_tick_request.py +37 -0
- webull/data/request/subscribe_request.py +43 -0
- webull/data/request/unsubscribe_request.py +42 -0
- webull/trade/__init__.py +2 -0
- webull/trade/common/__init__.py +0 -0
- webull/trade/common/account_type.py +22 -0
- webull/trade/common/category.py +29 -0
- webull/trade/common/combo_ticker_type.py +23 -0
- webull/trade/common/combo_type.py +31 -0
- webull/trade/common/currency.py +24 -0
- webull/trade/common/forbid_reason.py +27 -0
- webull/trade/common/instrument_type.py +27 -0
- webull/trade/common/markets.py +27 -0
- webull/trade/common/order_entrust_type.py +21 -0
- webull/trade/common/order_side.py +23 -0
- webull/trade/common/order_status.py +25 -0
- webull/trade/common/order_tif.py +24 -0
- webull/trade/common/order_type.py +30 -0
- webull/trade/common/trade_policy.py +22 -0
- webull/trade/common/trading_date_type.py +24 -0
- webull/trade/common/trailing_type.py +23 -0
- webull/trade/events/__init__.py +0 -0
- webull/trade/events/default_retry_policy.py +64 -0
- webull/trade/events/events_pb2.py +43 -0
- webull/trade/events/events_pb2_grpc.py +66 -0
- webull/trade/events/signature_composer.py +61 -0
- webull/trade/events/types.py +21 -0
- webull/trade/request/__init__.py +0 -0
- webull/trade/request/cancel_order_request.py +28 -0
- webull/trade/request/get_account_balance_request.py +28 -0
- webull/trade/request/get_account_positions_request.py +30 -0
- webull/trade/request/get_account_profile_request.py +26 -0
- webull/trade/request/get_app_subscriptions.py +28 -0
- webull/trade/request/get_open_orders_request.py +30 -0
- webull/trade/request/get_order_detail_request.py +27 -0
- webull/trade/request/get_today_orders_request.py +31 -0
- webull/trade/request/get_trade_calendar_request.py +30 -0
- webull/trade/request/get_trade_instrument_detail_request.py +24 -0
- webull/trade/request/get_trade_security_detail_request.py +42 -0
- webull/trade/request/get_tradeable_instruments_request.py +27 -0
- webull/trade/request/palce_order_request.py +91 -0
- webull/trade/request/place_order_request_v2.py +58 -0
- webull/trade/request/replace_order_request.py +73 -0
- webull/trade/request/replace_order_request_v2.py +38 -0
- webull/trade/request/v2/__init__.py +0 -0
- webull/trade/request/v2/cancel_option_request.py +28 -0
- webull/trade/request/v2/cancel_order_request.py +28 -0
- webull/trade/request/v2/get_account_balance_request.py +28 -0
- webull/trade/request/v2/get_account_list.py +23 -0
- webull/trade/request/v2/get_account_positions_request.py +24 -0
- webull/trade/request/v2/get_order_detail_request.py +26 -0
- webull/trade/request/v2/get_order_history_request.py +35 -0
- webull/trade/request/v2/palce_order_request.py +87 -0
- webull/trade/request/v2/place_option_request.py +64 -0
- webull/trade/request/v2/preview_option_request.py +28 -0
- webull/trade/request/v2/preview_order_request.py +59 -0
- webull/trade/request/v2/replace_option_request.py +28 -0
- webull/trade/request/v2/replace_order_request.py +57 -0
- webull/trade/trade/__init__.py +0 -0
- webull/trade/trade/account_info.py +83 -0
- webull/trade/trade/order_operation.py +246 -0
- webull/trade/trade/trade_calendar.py +37 -0
- webull/trade/trade/trade_instrument.py +72 -0
- webull/trade/trade/v2/__init__.py +0 -0
- webull/trade/trade/v2/account_info_v2.py +55 -0
- webull/trade/trade/v2/order_operation_v2.py +206 -0
- webull/trade/trade_client.py +43 -0
- webull/trade/trade_events_client.py +233 -0
- webull_openapi_python_sdk-1.0.0.dist-info/METADATA +28 -0
- webull_openapi_python_sdk-1.0.0.dist-info/RECORD +295 -0
- webull_openapi_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- webull_openapi_python_sdk-1.0.0.dist-info/licenses/LICENSE +202 -0
- webull_openapi_python_sdk-1.0.0.dist-info/licenses/NOTICE +56 -0
- webull_openapi_python_sdk-1.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# coding=utf-8
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
import sys
|
|
19
|
+
import threading
|
|
20
|
+
import time
|
|
21
|
+
import uuid
|
|
22
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
23
|
+
|
|
24
|
+
import paho.mqtt.client as mqttc
|
|
25
|
+
|
|
26
|
+
import webull.core.exception.error_code as error_code
|
|
27
|
+
from webull.core.client import ApiClient
|
|
28
|
+
from webull.core.common import api_type
|
|
29
|
+
from webull.core.endpoint.default_endpoint_resolver import DefaultEndpointResolver
|
|
30
|
+
from webull.core.endpoint.resolver_endpoint_request import ResolveEndpointRequest
|
|
31
|
+
from webull.core.exception.exceptions import ClientException
|
|
32
|
+
from webull.core.http.initializer.client_initializer import ClientInitializer
|
|
33
|
+
from webull.core.retry.retry_condition import RetryCondition
|
|
34
|
+
from webull.data.common.connect_ack import ConnectAck
|
|
35
|
+
from webull.data.internal.default_retry_policy import DefaultQuotesRetryPolicy, QuotesRetryPolicyContext
|
|
36
|
+
from webull.data.internal.exceptions import ConnectException, LoopException
|
|
37
|
+
from webull.data.internal.quotes_decoder import QuotesDecoder
|
|
38
|
+
|
|
39
|
+
DEFAULT_REGION_ID = "us"
|
|
40
|
+
|
|
41
|
+
LOG_INFO = mqttc.MQTT_LOG_INFO
|
|
42
|
+
LOG_NOTICE = mqttc.MQTT_LOG_NOTICE
|
|
43
|
+
LOG_WARNING = mqttc.MQTT_LOG_WARNING
|
|
44
|
+
LOG_ERR = mqttc.MQTT_LOG_ERR
|
|
45
|
+
LOG_DEBUG = mqttc.MQTT_LOG_DEBUG
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
|
|
49
|
+
class QuotesClient(mqttc.Client):
|
|
50
|
+
LOG_FORMAT = '%(thread)d %(threadName)s %(asctime)s %(name)s %(levelname)s %(message)s'
|
|
51
|
+
def __init__(self, app_key, app_secret, region_id, session_id,
|
|
52
|
+
http_host=None,
|
|
53
|
+
mqtt_host=None,
|
|
54
|
+
mqtt_port=1883,
|
|
55
|
+
tls_enable=True,
|
|
56
|
+
transport="tcp",
|
|
57
|
+
retry_policy=None,
|
|
58
|
+
downgrade_message=None):
|
|
59
|
+
self._endpoint_resolver = DefaultEndpointResolver(self)
|
|
60
|
+
self._client_id = session_id
|
|
61
|
+
self._app_key = app_key
|
|
62
|
+
self._app_secret = app_secret
|
|
63
|
+
self._region_id = region_id
|
|
64
|
+
self._out_api_message_mutex = threading.Lock()
|
|
65
|
+
self._quotes_session_id = session_id
|
|
66
|
+
self._quotes_subscribe = None
|
|
67
|
+
self._quotes_unsubscribe = None
|
|
68
|
+
self._on_quotes_message = None
|
|
69
|
+
self._http_host = http_host
|
|
70
|
+
self._mqtt_host = mqtt_host
|
|
71
|
+
self._mqtt_port = mqtt_port
|
|
72
|
+
self._quotes_decoder = QuotesDecoder()
|
|
73
|
+
|
|
74
|
+
api_client = ApiClient(app_key, app_secret, region_id)
|
|
75
|
+
if http_host:
|
|
76
|
+
api_client.add_endpoint(region_id, http_host)
|
|
77
|
+
self._api_client = api_client
|
|
78
|
+
|
|
79
|
+
def _quotes_message(client, userdata, message):
|
|
80
|
+
decoded = client._quotes_decoder.decode(message)
|
|
81
|
+
if decoded:
|
|
82
|
+
client._easy_log(
|
|
83
|
+
LOG_DEBUG, 'decoded message topic: %s, payload: %s', decoded[0], decoded[1])
|
|
84
|
+
_on_quotes_message = client._on_quotes_message
|
|
85
|
+
no_callback_topic = ['echo','notice']
|
|
86
|
+
if _on_quotes_message and decoded[0] not in no_callback_topic:
|
|
87
|
+
_on_quotes_message(client, decoded[0], decoded[1])
|
|
88
|
+
else:
|
|
89
|
+
client._easy_log(
|
|
90
|
+
LOG_ERR, 'unexpected decoding for message topic: %s', message.topic)
|
|
91
|
+
|
|
92
|
+
def _quotes_on_connect(client, userdata, flags, rc):
|
|
93
|
+
if rc == 0:
|
|
94
|
+
with self._callback_mutex:
|
|
95
|
+
_quotes_subscribe = self._quotes_subscribe
|
|
96
|
+
if _quotes_subscribe:
|
|
97
|
+
with self._out_api_message_mutex:
|
|
98
|
+
try:
|
|
99
|
+
_quotes_subscribe(
|
|
100
|
+
client, self._api_client, self._quotes_session_id)
|
|
101
|
+
except Exception as e:
|
|
102
|
+
self._easy_log(
|
|
103
|
+
LOG_ERR, 'Caught exception in on_quotes_subscribe: %s', e)
|
|
104
|
+
raise
|
|
105
|
+
else:
|
|
106
|
+
raise ClientException(
|
|
107
|
+
error_code.SDK_INVALID_PARAMETER, "on_quotes_subscribe func must be set")
|
|
108
|
+
else:
|
|
109
|
+
error_msg = ''
|
|
110
|
+
ack = ConnectAck.from_code(rc)
|
|
111
|
+
if ack is not None:
|
|
112
|
+
error_msg = ack.value[1]
|
|
113
|
+
raise ConnectException(rc, error_msg)
|
|
114
|
+
|
|
115
|
+
self._quotes_message = _quotes_message
|
|
116
|
+
self._quotes_on_connect = _quotes_on_connect
|
|
117
|
+
mqttc.Client.__init__(self, self._client_id, transport=transport, reconnect_on_failure=False)
|
|
118
|
+
self.username_pw_set(self._app_key, uuid.uuid4().hex)
|
|
119
|
+
if tls_enable:
|
|
120
|
+
self.tls_set()
|
|
121
|
+
if retry_policy:
|
|
122
|
+
self._retry_policy = retry_policy
|
|
123
|
+
else:
|
|
124
|
+
self._retry_policy = DefaultQuotesRetryPolicy()
|
|
125
|
+
self._mqtt_host = mqtt_host
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def on_quotes_subscribe(self):
|
|
129
|
+
return self._quotes_subscribe
|
|
130
|
+
|
|
131
|
+
@on_quotes_subscribe.setter
|
|
132
|
+
def on_quotes_subscribe(self, func):
|
|
133
|
+
with self._callback_mutex:
|
|
134
|
+
self._quotes_subscribe = func
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def on_quotes_unsubscribe(self):
|
|
138
|
+
return self._quotes_unsubscribe
|
|
139
|
+
|
|
140
|
+
@on_quotes_unsubscribe.setter
|
|
141
|
+
def on_quotes_unsubscribe(self, func):
|
|
142
|
+
with self._callback_mutex:
|
|
143
|
+
self._quotes_unsubscribe = func
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def quotes_session_id(self):
|
|
147
|
+
return self._quotes_session_id
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def api_client(self):
|
|
151
|
+
return self._api_client
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def on_quotes_message(self):
|
|
155
|
+
return self._on_quotes_message
|
|
156
|
+
|
|
157
|
+
@on_quotes_message.setter
|
|
158
|
+
def on_quotes_message(self, func):
|
|
159
|
+
with self._callback_mutex:
|
|
160
|
+
self._on_quotes_message = func
|
|
161
|
+
|
|
162
|
+
def register_payload_decoder(self, type, decoder):
|
|
163
|
+
with self._callback_mutex:
|
|
164
|
+
self._quotes_decoder.register_payload_decoder(type, decoder)
|
|
165
|
+
|
|
166
|
+
def _quotes_connect(self, host, port):
|
|
167
|
+
self.on_message = self._quotes_message
|
|
168
|
+
self.on_connect = self._quotes_on_connect
|
|
169
|
+
if not host:
|
|
170
|
+
endpoint_request = ResolveEndpointRequest(
|
|
171
|
+
self._region_id, api_type=api_type.QUOTES)
|
|
172
|
+
endpoint = self._endpoint_resolver.resolve(endpoint_request)
|
|
173
|
+
_host = endpoint
|
|
174
|
+
else:
|
|
175
|
+
_host = host
|
|
176
|
+
try:
|
|
177
|
+
return super().connect(_host, port)
|
|
178
|
+
except Exception as e:
|
|
179
|
+
self._easy_log(
|
|
180
|
+
LOG_ERR, 'Caught exception in connect: %s, host: %s, port: %s, ssl: %s', e, _host, port, self._ssl)
|
|
181
|
+
raise e
|
|
182
|
+
|
|
183
|
+
def connect_and_loop_forever(self, timeout=1, logger_enable=True, customer_logger=None):
|
|
184
|
+
self._init_logger(logger_enable, customer_logger)
|
|
185
|
+
self._init_client()
|
|
186
|
+
retry_policy_context = QuotesRetryPolicyContext(None, 0, None)
|
|
187
|
+
retries = 0
|
|
188
|
+
final_exception = None
|
|
189
|
+
while True:
|
|
190
|
+
if self._thread_terminate is True:
|
|
191
|
+
self._easy_log(LOG_WARNING,
|
|
192
|
+
'exited due to thread terminated')
|
|
193
|
+
self._sock_close()
|
|
194
|
+
return
|
|
195
|
+
try:
|
|
196
|
+
self._quotes_connect(self._mqtt_host, self._mqtt_port)
|
|
197
|
+
loop_ret = super().loop_forever(timeout)
|
|
198
|
+
# loop_ret != 0 means unexpected error returned from server, should be retry in future
|
|
199
|
+
if loop_ret != 0:
|
|
200
|
+
raise LoopException(loop_ret)
|
|
201
|
+
else:
|
|
202
|
+
self._easy_log(LOG_WARNING, 'exited normally')
|
|
203
|
+
return
|
|
204
|
+
except ConnectException as connect_exception:
|
|
205
|
+
final_exception = connect_exception
|
|
206
|
+
retry_policy_context = QuotesRetryPolicyContext(
|
|
207
|
+
None, retries, connect_exception.error_code)
|
|
208
|
+
self._easy_log(LOG_ERR,
|
|
209
|
+
'connect exception:%s', connect_exception)
|
|
210
|
+
except Exception as exception:
|
|
211
|
+
final_exception = exception
|
|
212
|
+
retry_policy_context = QuotesRetryPolicyContext(
|
|
213
|
+
exception, retries, None)
|
|
214
|
+
self._easy_log(LOG_ERR, 'exception:%s', exception)
|
|
215
|
+
retryable = self._retry_policy.should_retry(retry_policy_context)
|
|
216
|
+
if retryable & RetryCondition.NO_RETRY:
|
|
217
|
+
self._easy_log(
|
|
218
|
+
LOG_ERR, 'processing will stopped due to not be retryable, retry_context:%s', retry_policy_context)
|
|
219
|
+
break
|
|
220
|
+
retry_policy_context.retryable = retryable
|
|
221
|
+
time_to_sleep = self._retry_policy.compute_delay_before_next_retry(
|
|
222
|
+
retry_policy_context)
|
|
223
|
+
self._easy_log(LOG_INFO, "next retry will be started in %s ms, retry_context:%s",
|
|
224
|
+
time_to_sleep, retry_policy_context)
|
|
225
|
+
time.sleep(time_to_sleep / 1000.0)
|
|
226
|
+
retries += 1
|
|
227
|
+
retry_policy_context.retries_attempted = retries
|
|
228
|
+
self._sock_close()
|
|
229
|
+
if final_exception:
|
|
230
|
+
raise final_exception
|
|
231
|
+
|
|
232
|
+
def connect_and_loop_async(self, timeout=1, thread_daemon=False, logger_enable=True, customer_logger=None):
|
|
233
|
+
if self._thread is not None:
|
|
234
|
+
return mqttc.MQTT_ERR_INVAL
|
|
235
|
+
self._sockpairR, self._sockpairW = mqttc._socketpair_compat()
|
|
236
|
+
self._thread_terminate = False
|
|
237
|
+
self._thread = threading.Thread(
|
|
238
|
+
target=self.connect_and_loop_forever, name="Thread-Async-Quotes-Client", args=(timeout,))
|
|
239
|
+
self._thread.daemon = True
|
|
240
|
+
self._thread.daemon = thread_daemon
|
|
241
|
+
self._thread.start()
|
|
242
|
+
|
|
243
|
+
def connect_and_loop_start(self, timeout=1, logger_enable=True, customer_logger=None):
|
|
244
|
+
self.connect_and_loop_async(timeout, True, logger_enable, customer_logger)
|
|
245
|
+
|
|
246
|
+
def loop_wait(self):
|
|
247
|
+
if self._thread is None:
|
|
248
|
+
return mqttc.MQTT_ERR_INVAL
|
|
249
|
+
if threading.current_thread() != self._thread:
|
|
250
|
+
self._thread.join()
|
|
251
|
+
|
|
252
|
+
def loop_stop(self):
|
|
253
|
+
return super().loop_stop()
|
|
254
|
+
|
|
255
|
+
def set_stream_logger(self, log_level=logging.DEBUG, logger_name='webull.data', stream=None,
|
|
256
|
+
format_string=None):
|
|
257
|
+
if format_string is None:
|
|
258
|
+
format_string = self.LOG_FORMAT
|
|
259
|
+
|
|
260
|
+
# http
|
|
261
|
+
self.api_client.set_stream_logger(log_level, 'webull.core', stream, format_string)
|
|
262
|
+
|
|
263
|
+
# mqtt
|
|
264
|
+
log = logging.getLogger(logger_name)
|
|
265
|
+
log.setLevel(log_level)
|
|
266
|
+
ch = logging.StreamHandler(stream)
|
|
267
|
+
ch.setLevel(log_level)
|
|
268
|
+
formatter = logging.Formatter(format_string)
|
|
269
|
+
ch.setFormatter(formatter)
|
|
270
|
+
log.addHandler(ch)
|
|
271
|
+
|
|
272
|
+
def set_file_logger(self, path, log_level=logging.DEBUG, logger_name='webull.data', format_string=None, when='H', interval=1, backup_count=72):
|
|
273
|
+
if format_string is None:
|
|
274
|
+
format_string = self.LOG_FORMAT
|
|
275
|
+
|
|
276
|
+
# http
|
|
277
|
+
self.api_client.set_file_logger(path, log_level, 'webull.core', format_string, when, interval, backup_count)
|
|
278
|
+
|
|
279
|
+
# mqtt
|
|
280
|
+
log = logging.getLogger(logger_name)
|
|
281
|
+
log.setLevel(log_level)
|
|
282
|
+
handler = TimedRotatingFileHandler(
|
|
283
|
+
filename=path,
|
|
284
|
+
when=when,
|
|
285
|
+
interval=interval,
|
|
286
|
+
backupCount=backup_count,
|
|
287
|
+
encoding='utf-8'
|
|
288
|
+
)
|
|
289
|
+
formatter = logging.Formatter(format_string)
|
|
290
|
+
handler.setFormatter(formatter)
|
|
291
|
+
log.addHandler(handler)
|
|
292
|
+
|
|
293
|
+
def _init_logger(self, logger_enable=True, customer_logger=None):
|
|
294
|
+
|
|
295
|
+
if logger_enable is not True:
|
|
296
|
+
return
|
|
297
|
+
|
|
298
|
+
if customer_logger:
|
|
299
|
+
customer_api_logger = logging.getLogger("webull.core")
|
|
300
|
+
customer_api_logger.setLevel(customer_logger.level)
|
|
301
|
+
for handler in customer_logger.handlers:
|
|
302
|
+
customer_api_logger.addHandler(handler)
|
|
303
|
+
self.api_client.set_logger(customer_api_logger)
|
|
304
|
+
self.enable_logger(customer_logger)
|
|
305
|
+
else:
|
|
306
|
+
log_format = '%(thread)d %(asctime)s %(name)s %(levelname)s %(message)s'
|
|
307
|
+
log_file_path = 'webull_data_streaming_sdk.log'
|
|
308
|
+
self.set_stream_logger(stream=sys.stdout, logger_name='webull.data', log_level=logging.INFO, format_string=log_format)
|
|
309
|
+
self.set_file_logger(path=log_file_path, logger_name='webull.data', log_level=logging.INFO, format_string=log_format)
|
|
310
|
+
self.enable_logger(logging.getLogger('webull.data'))
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _init_client(self):
|
|
314
|
+
ClientInitializer.initializer(self.api_client)
|
|
@@ -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
|
+
# coding=utf-8
|
|
16
|
+
|
|
17
|
+
from webull.data.internal.quotes_payload_decoder import Utf8Decoder
|
|
18
|
+
|
|
19
|
+
class QuotesDecoder:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self._payload_decoders = {}
|
|
22
|
+
self._default_decoder = Utf8Decoder()
|
|
23
|
+
|
|
24
|
+
def register_payload_decoder(self, payload_type, decoder):
|
|
25
|
+
self._payload_decoders[payload_type] = decoder
|
|
26
|
+
|
|
27
|
+
def decode(self, message):
|
|
28
|
+
quotes_topic = message.topic
|
|
29
|
+
if quotes_topic:
|
|
30
|
+
payload = message.payload
|
|
31
|
+
decoded_payload = self.decode_payload(quotes_topic, payload)
|
|
32
|
+
return (quotes_topic, decoded_payload)
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
def decode_payload(self, quotes_topic, payload):
|
|
36
|
+
decoder = self._payload_decoders.get(quotes_topic)
|
|
37
|
+
if decoder:
|
|
38
|
+
return decoder.parse(payload)
|
|
39
|
+
else:
|
|
40
|
+
return self._default_decoder.parse(payload)
|
|
@@ -0,0 +1,35 @@
|
|
|
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 abc
|
|
17
|
+
from webull.core.vendored.six import add_metaclass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@add_metaclass(abc.ABCMeta)
|
|
21
|
+
class BaseQuotesPayloadDecoder(object):
|
|
22
|
+
def __init__(self):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@abc.abstractclassmethod
|
|
26
|
+
def parse(self, payload):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Utf8Decoder(BaseQuotesPayloadDecoder):
|
|
31
|
+
def __init__(self):
|
|
32
|
+
super().__init__()
|
|
33
|
+
|
|
34
|
+
def parse(self, payload):
|
|
35
|
+
return str(payload.decode("utf-8"))
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
class QuotesTopic(object):
|
|
18
|
+
def __init__(self, instrument_id, data_type, interval):
|
|
19
|
+
self.instrument_id = instrument_id
|
|
20
|
+
self.data_type = int(data_type)
|
|
21
|
+
self.interval = int(interval)
|
|
22
|
+
|
|
23
|
+
def get_instrument_id(self):
|
|
24
|
+
return self.instrument_id
|
|
25
|
+
|
|
26
|
+
def get_data_type(self):
|
|
27
|
+
return self.data_type
|
|
28
|
+
|
|
29
|
+
def get_interval(self):
|
|
30
|
+
return self.interval
|
|
31
|
+
|
|
32
|
+
def __repr__(self):
|
|
33
|
+
return "%s-%s-%s" % (self.instrument_id, self.data_type, self.interval)
|
|
34
|
+
|
|
35
|
+
def __str__(self):
|
|
36
|
+
return self.__repr__()
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright 2022 Webull
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from webull.data.request.get_instruments_request import GetInstrumentsRequest
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Instrument:
|
|
19
|
+
def __init__(self, api_client):
|
|
20
|
+
self.client = api_client
|
|
21
|
+
|
|
22
|
+
def get_instrument(self, symbols, category):
|
|
23
|
+
"""
|
|
24
|
+
Query the underlying information according to the security symbol list and security type.
|
|
25
|
+
|
|
26
|
+
:param symbols: Securities symbol, such as: 00700,00981.
|
|
27
|
+
:param category: Security type, enumeration.
|
|
28
|
+
"""
|
|
29
|
+
instruments_request = GetInstrumentsRequest()
|
|
30
|
+
instruments_request.set_symbols(symbols)
|
|
31
|
+
instruments_request.set_category(category)
|
|
32
|
+
response = self.client.get_response(instruments_request)
|
|
33
|
+
return response
|
|
@@ -0,0 +1,187 @@
|
|
|
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
|
+
from webull.data.request.get_batch_historical_bars_request import BatchHistoricalBarsRequest
|
|
15
|
+
from webull.data.request.get_corp_action_request import GetCorpActionRequest
|
|
16
|
+
from webull.data.request.get_eod_bars_request import GetEodBarsRequest
|
|
17
|
+
from webull.data.request.get_historical_bars_request import GetHistoricalBarsRequest
|
|
18
|
+
from webull.data.request.get_quotes_request import GetQuotesRequest
|
|
19
|
+
from webull.data.request.get_snapshot_request import GetSnapshotRequest
|
|
20
|
+
from webull.data.request.get_tick_request import GetTickRequest
|
|
21
|
+
from webull.data.request.subscribe_request import SubscribeRequest
|
|
22
|
+
from webull.data.request.unsubscribe_request import UnsubcribeRequest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MarketData:
|
|
26
|
+
def __init__(self, api_client):
|
|
27
|
+
self.client = api_client
|
|
28
|
+
|
|
29
|
+
def get_history_bar(self, symbol, category, timespan, count='200', real_time_required=None, trading_sessions=None):
|
|
30
|
+
"""
|
|
31
|
+
Returns to Instrument in the window aggregated data.
|
|
32
|
+
According to the last N K-lines of the stock code, it supports various granularity K-lines such as m1 and m5.
|
|
33
|
+
Currently, only the K-line with the previous weight is provided for the daily K-line and above,
|
|
34
|
+
and only the un-weighted K-line is provided for the minute K.
|
|
35
|
+
|
|
36
|
+
:param symbol: Securities code
|
|
37
|
+
:param category: Security type, enumeration.
|
|
38
|
+
:param timespan: K-line time granularity
|
|
39
|
+
:param count: The number of lines: the default is 200, and the maximum limit is 1200
|
|
40
|
+
:param real_time_required: Returns the latest trade quote data. By default, the most recent market data is returned.
|
|
41
|
+
:param trading_sessions: Specify trading session, multiple selections are allowed
|
|
42
|
+
By default, only intraday candlestick data is returned.
|
|
43
|
+
"""
|
|
44
|
+
history_bar_request = GetHistoricalBarsRequest()
|
|
45
|
+
history_bar_request.set_symbol(symbol)
|
|
46
|
+
history_bar_request.set_category(category)
|
|
47
|
+
history_bar_request.set_timespan(timespan)
|
|
48
|
+
history_bar_request.set_count(count)
|
|
49
|
+
history_bar_request.set_real_time_required(real_time_required)
|
|
50
|
+
history_bar_request.set_trading_sessions(trading_sessions)
|
|
51
|
+
response = self.client.get_response(history_bar_request)
|
|
52
|
+
return response
|
|
53
|
+
|
|
54
|
+
def get_batch_history_bar(self, symbols, category, timespan, count='200', real_time_required=None, trading_sessions=None):
|
|
55
|
+
"""
|
|
56
|
+
Batch query K-line data for multiple symbols, returning aggregated data within the window.
|
|
57
|
+
According to the last N K-lines of the stock code, it supports various granularity K-lines such as m1 and m5.
|
|
58
|
+
Currently, only the K-line with the previous weight is provided for the daily K-line and above,
|
|
59
|
+
and only the un-weighted K-line is provided for the minute K.
|
|
60
|
+
|
|
61
|
+
:param symbols: List of security codes
|
|
62
|
+
:param category: Security type, enumeration
|
|
63
|
+
:param timespan: K-line interval
|
|
64
|
+
:param count: Number of K-lines to return, default is 200, maximum is 1200
|
|
65
|
+
:param real_time_required: Returns the latest trade quote data. By default, the most recent market data is returned.
|
|
66
|
+
:param trading_sessions: Specify trading session, multiple selections are allowed
|
|
67
|
+
By default, only intraday candlestick data is returned.
|
|
68
|
+
"""
|
|
69
|
+
history_bar_request = BatchHistoricalBarsRequest()
|
|
70
|
+
history_bar_request.set_symbols(symbols)
|
|
71
|
+
history_bar_request.set_category(category)
|
|
72
|
+
history_bar_request.set_timespan(timespan)
|
|
73
|
+
history_bar_request.set_count(count)
|
|
74
|
+
history_bar_request.set_real_time_required(real_time_required)
|
|
75
|
+
history_bar_request.set_trading_sessions(trading_sessions)
|
|
76
|
+
response = self.client.get_response(history_bar_request)
|
|
77
|
+
return response
|
|
78
|
+
|
|
79
|
+
def get_snapshot(self, symbols, category, extend_hour_required=None, overnight_required=None):
|
|
80
|
+
"""
|
|
81
|
+
Query the latest stock market snapshots in batches according to the stock code list.
|
|
82
|
+
|
|
83
|
+
:param symbols: List of security codes; for example: single: 00700 multiple: 00700,00981;
|
|
84
|
+
For each request,up to 100 symbols can be subscribed; Under the authority of Hong Kong stock BMP,
|
|
85
|
+
a single request supports up to 20 symbols
|
|
86
|
+
:param category: Security type, enumeration.
|
|
87
|
+
:param extend_hour_required: Whether to include pre-market and after-hours sessions, the default is not included
|
|
88
|
+
:param overnight_required: Whether to include the night session, the default is not included
|
|
89
|
+
"""
|
|
90
|
+
snapshot_request = GetSnapshotRequest()
|
|
91
|
+
snapshot_request.set_symbols(symbols)
|
|
92
|
+
snapshot_request.set_category(category)
|
|
93
|
+
snapshot_request.set_extend_hour_required(extend_hour_required)
|
|
94
|
+
snapshot_request.set_overnight_required(overnight_required)
|
|
95
|
+
response = self.client.get_response(snapshot_request)
|
|
96
|
+
return response
|
|
97
|
+
|
|
98
|
+
def get_quotes(self, symbol, category, depth=None, overnight_required=None):
|
|
99
|
+
"""
|
|
100
|
+
Query the depth quote of securities according to the stock code list.
|
|
101
|
+
|
|
102
|
+
:param symbol: Securities code
|
|
103
|
+
:param category: Security type, enumeration.
|
|
104
|
+
:param depth: Retrieve bid/ask depth
|
|
105
|
+
Level 1 contains only the top 1 bid/ask level.
|
|
106
|
+
Level 2 becomes effective, with 10 levels by default. For U.S. stocks, Level 2 supports up to 50 levels.
|
|
107
|
+
:param overnight_required: Whether to include the night session, the default is not included
|
|
108
|
+
"""
|
|
109
|
+
quote_request = GetQuotesRequest()
|
|
110
|
+
quote_request.set_symbol(symbol)
|
|
111
|
+
quote_request.set_category(category)
|
|
112
|
+
quote_request.set_depth(depth)
|
|
113
|
+
quote_request.set_overnight_required(overnight_required)
|
|
114
|
+
response = self.client.get_response(quote_request)
|
|
115
|
+
return response
|
|
116
|
+
|
|
117
|
+
def get_tick(self, symbol, category, count='200', trading_sessions=None):
|
|
118
|
+
"""
|
|
119
|
+
Query tick-by-tick transaction of securities according to the stock code list.
|
|
120
|
+
|
|
121
|
+
:param symbol: Securities code
|
|
122
|
+
:param category: Security type, enumeration.
|
|
123
|
+
:param count: The number of lines: the default is 30, and the maximum limit is 1000
|
|
124
|
+
:param trading_sessions: Specify trading session, multiple selections are allowed
|
|
125
|
+
"""
|
|
126
|
+
tick_request = GetTickRequest()
|
|
127
|
+
tick_request.set_symbol(symbol)
|
|
128
|
+
tick_request.set_category(category)
|
|
129
|
+
tick_request.set_count(count)
|
|
130
|
+
tick_request.set_trading_sessions(trading_sessions)
|
|
131
|
+
response = self.client.get_response(tick_request)
|
|
132
|
+
return response
|
|
133
|
+
|
|
134
|
+
def get_eod_bar(self, instrument_ids, date=None, count='1'):
|
|
135
|
+
"""
|
|
136
|
+
Only for Webull JP
|
|
137
|
+
|
|
138
|
+
Query end-of-day market information according to instrument_id.
|
|
139
|
+
|
|
140
|
+
:param instrument_ids: Instrument id collection, such as: 913256135,913303964.
|
|
141
|
+
Multiple instrument_ids should be separated by ,.
|
|
142
|
+
A single query supports up to 200 instrument_id
|
|
143
|
+
:param date: UTC time. Time format: yyyy-MM-dd, and the default check is conducted on the latest date
|
|
144
|
+
:param count: With “date” as the deadline, the end-of-day market data of the last “count” trading days:
|
|
145
|
+
the default is 1, and the maximum limit is 800
|
|
146
|
+
"""
|
|
147
|
+
eod_bar_request = GetEodBarsRequest()
|
|
148
|
+
eod_bar_request.set_instrument_ids(instrument_ids)
|
|
149
|
+
if date is not None:
|
|
150
|
+
eod_bar_request.set_date(date)
|
|
151
|
+
eod_bar_request.set_count(count)
|
|
152
|
+
response = self.client.get_response(eod_bar_request)
|
|
153
|
+
return response
|
|
154
|
+
|
|
155
|
+
def get_corp_action(self, instrument_ids, event_types, start_date=None, end_date=None, page_number=None,
|
|
156
|
+
page_size=None, last_update_time=None):
|
|
157
|
+
"""
|
|
158
|
+
Only for Webull JP
|
|
159
|
+
Supports the query of the corporate events for stock splits and reverse stock split,
|
|
160
|
+
including past and upcoming events.
|
|
161
|
+
|
|
162
|
+
:param instrument_ids: Instrument id collection, such as: 913256135,913303964.
|
|
163
|
+
Multiple instrument_ids should be separated by ,.
|
|
164
|
+
A single query supports up to 100 instrument_id
|
|
165
|
+
:param event_types: Event type collection. Multiple event_types should be separated by ,
|
|
166
|
+
:param start_date: Event start date, UTC time.Time format: yyyy-MM-dd
|
|
167
|
+
:param end_date: Event end date, UTC time.Time format: yyyy-MM-dd
|
|
168
|
+
:param page_number: The initial value, if not passed, the first page will be searched by default
|
|
169
|
+
:param page_size: Number of entries per page: default value is 20, and maximum value is 200.
|
|
170
|
+
Integers can be filled
|
|
171
|
+
:param last_update_time: Incremental update time, UTC time. Time format: yyyy-MM-dd HH:mm:ss
|
|
172
|
+
"""
|
|
173
|
+
eod_corp_action_request = GetCorpActionRequest()
|
|
174
|
+
eod_corp_action_request.set_instrument_ids(instrument_ids)
|
|
175
|
+
eod_corp_action_request.set_event_types(event_types)
|
|
176
|
+
if start_date is not None:
|
|
177
|
+
eod_corp_action_request.set_start_date(start_date)
|
|
178
|
+
if end_date is not None:
|
|
179
|
+
eod_corp_action_request.set_end_date(end_date)
|
|
180
|
+
if page_number is not None:
|
|
181
|
+
eod_corp_action_request.set_page_number(page_number)
|
|
182
|
+
if page_size is not None:
|
|
183
|
+
eod_corp_action_request.set_page_size(page_size)
|
|
184
|
+
if last_update_time is not None:
|
|
185
|
+
eod_corp_action_request.set_last_update_time(last_update_time)
|
|
186
|
+
response = self.client.get_response(eod_corp_action_request)
|
|
187
|
+
return response
|