diff --git a/vnpy/api/xtp/vnxtp.pyd b/vnpy/api/xtp/vnxtp.pyd index 099ec46e..7648bfdb 100644 Binary files a/vnpy/api/xtp/vnxtp.pyd and b/vnpy/api/xtp/vnxtp.pyd differ diff --git a/vnpy/api/xtp/vnxtp.pyi b/vnpy/api/xtp/vnxtp.pyi index faca3608..40e0641d 100644 --- a/vnpy/api/xtp/vnxtp.pyi +++ b/vnpy/api/xtp/vnxtp.pyi @@ -7,19 +7,34 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[AsyncDispatchException], None]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + from . import vnxtp_XTP as XTP class XTPRspInfoStruct(): error_id: int - error_msg: Sequence[int] + error_msg: str class XTPSpecificTickerStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str class XTPMarketDataStockExData(): @@ -71,7 +86,7 @@ class XTPMarketDataStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float pre_close_price: float open_price: float @@ -95,7 +110,7 @@ class XTPMarketDataStruct(): bid_qty: Sequence[int] ask_qty: Sequence[int] trades_count: int - ticker_status: Sequence[int] + ticker_status: str stk: XTPMarketDataStockExData opt: XTPMarketDataOptionExData data_type: XTP_MARKETDATA_TYPE @@ -106,8 +121,8 @@ class XTPQuoteStaticInfo(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str ticker_type: XTP_TICKER_TYPE pre_close_price: float upper_limit_price: float @@ -121,7 +136,7 @@ class OrderBookStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float qty: int turnover: float @@ -161,7 +176,7 @@ class XTPTickByTickStruct(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str seq: int data_time: int type: XTP_TBT_TYPE @@ -173,7 +188,7 @@ class XTPTickerPriceInfo(): exchange_id: XTP_EXCHANGE_TYPE - ticker: Sequence[int] + ticker: str last_price: float @@ -182,7 +197,7 @@ class XTPOrderInsertInfo(): order_xtp_id: int order_client_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE price: float stop_price: float @@ -210,7 +225,7 @@ class XTPOrderInfo(): order_client_id: int order_cancel_client_id: int order_cancel_xtp_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE price: float quantity: int @@ -227,7 +242,7 @@ class XTPOrderInfo(): update_time: int cancel_time: int trade_amount: float - order_local_id: Sequence[int] + order_local_id: str order_status: XTP_ORDER_STATUS_TYPE order_submit_status: XTP_ORDER_SUBMIT_STATUS_TYPE order_type: int @@ -238,16 +253,16 @@ class XTPTradeReport(): order_xtp_id: int order_client_id: int - ticker: Sequence[int] + ticker: str market: XTP_MARKET_TYPE local_order_id: int - exec_id: Sequence[int] + exec_id: str price: float quantity: int trade_time: int trade_amount: float report_index: int - order_exch_id: Sequence[int] + order_exch_id: str trade_type: int u32: int side: int @@ -255,13 +270,13 @@ class XTPTradeReport(): reserved1: int reserved2: int business_type: XTP_BUSINESS_TYPE - branch_pbu: Sequence[int] + branch_pbu: str class XTPQueryOrderReq(): - ticker: Sequence[int] + ticker: str begin_time: int end_time: int @@ -270,13 +285,13 @@ class XTPQueryReportByExecIdReq(): order_xtp_id: int - exec_id: Sequence[int] + exec_id: str class XTPQueryTraderReq(): - ticker: Sequence[int] + ticker: str begin_time: int end_time: int @@ -311,8 +326,8 @@ class XTPQueryAssetRsp(): class XTPQueryStkPositionRsp(): - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str market: XTP_MARKET_TYPE total_qty: int sellable_qty: int @@ -350,17 +365,17 @@ class XTPQueryStructuredFundInfoReq(): exchange_id: XTP_EXCHANGE_TYPE - sf_ticker: Sequence[int] + sf_ticker: str class XTPStructuredFundInfo(): exchange_id: XTP_EXCHANGE_TYPE - sf_ticker: Sequence[int] - sf_ticker_name: Sequence[int] - ticker: Sequence[int] - ticker_name: Sequence[int] + sf_ticker: str + sf_ticker_name: str + ticker: str + ticker_name: str split_merge_status: XTP_SPLIT_MERGE_STATUS ratio: int min_split_qty: int @@ -372,15 +387,15 @@ class XTPQueryETFBaseReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryETFBaseRsp(): market: XTP_MARKET_TYPE - etf: Sequence[int] - subscribe_redemption_ticker: Sequence[int] + etf: str + subscribe_redemption_ticker: str unit: int subscribe_status: int redemption_status: int @@ -395,16 +410,16 @@ class XTPQueryETFComponentReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryETFComponentRsp(): market: XTP_MARKET_TYPE - ticker: Sequence[int] - component_ticker: Sequence[int] - component_name: Sequence[int] + ticker: str + component_ticker: str + component_name: str quantity: int component_market: XTP_MARKET_TYPE replace_type: ETF_REPLACE_TYPE @@ -416,8 +431,8 @@ class XTPQueryIPOTickerRsp(): market: XTP_MARKET_TYPE - ticker: Sequence[int] - ticker_name: Sequence[int] + ticker: str + ticker_name: str price: float unit: int qty_upper_limit: int @@ -434,17 +449,17 @@ class XTPQueryOptionAuctionInfoReq(): market: XTP_MARKET_TYPE - ticker: Sequence[int] + ticker: str class XTPQueryOptionAuctionInfoRsp(): - ticker: Sequence[int] + ticker: str security_id_source: XTP_MARKET_TYPE - symbol: Sequence[int] - contract_id: Sequence[int] - underlying_security_id: Sequence[int] + symbol: str + contract_id: str + underlying_security_id: str underlying_security_id_source: XTP_MARKET_TYPE list_date: int last_trade_date: int @@ -483,8 +498,8 @@ class XTPFundTransferReq(): serial_id: int - fund_account: Sequence[int] - password: Sequence[int] + fund_account: str + password: str amount: float transfer_type: XTP_FUND_TRANSFER_TYPE @@ -610,7 +625,7 @@ class XTP_POSITION_DIRECTION_TYPE(Enum): class XTP_MARKETDATA_TYPE(Enum): XTP_MARKETDATA_ACTUAL: XTP_MARKETDATA_TYPE XTP_MARKETDATA_OPTION: XTP_MARKETDATA_TYPE -XTPVersionType = Sequence[int] +XTPVersionType = str XTP_LOG_LEVEL = XTP_LOG_LEVEL XTP_PROTOCOL_TYPE = XTP_PROTOCOL_TYPE XTP_EXCHANGE_TYPE = XTP_EXCHANGE_TYPE diff --git a/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp b/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp index 14130770..6933bd46 100644 --- a/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp +++ b/vnpy/api/xtp/vnxtp/generated_files/generated_functions_1.cpp @@ -277,9 +277,14 @@ void generate_class_XTP_API_QuoteApi(pybind11::object & parent) PyQuoteApi > c(parent, "QuoteApi"); c.def_static("CreateQuoteApi", + autocxxpy::apply_function_transform< + autocxxpy::function_constant< &XTP::API::QuoteApi::CreateQuoteApi - //, - //pybind11::call_guard() + >, + brigand::list< + > + >::value, + pybind11::call_guard() ); c.def("Release", autocxxpy::apply_function_transform< diff --git a/vnpy/api/xtp/vnxtp/generated_files/module.cpp b/vnpy/api/xtp/vnxtp/generated_files/module.cpp index 617f88e8..7c62af9f 100644 --- a/vnpy/api/xtp/vnxtp/generated_files/module.cpp +++ b/vnpy/api/xtp/vnxtp/generated_files/module.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "module.hpp" @@ -20,6 +21,12 @@ void additional_init(pybind11::module &m) void init_dispatcher(pybind11::module &m) { + m.def("set_async_callback_exception_handler", &autocxxpy::async_callback_exception_handler::set_handler); + pybind11::class_ c(m, "AsyncDispatchException"); + c.def_property("what", &autocxxpy::async_dispatch_exception::what, nullptr); + c.def_readonly("instance", &autocxxpy::async_dispatch_exception::instance); + c.def_readonly("function_name", &autocxxpy::async_dispatch_exception::function_name); + autocxxpy::dispatcher::instance().start(); } diff --git a/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp b/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp index 6100625a..2b9e523c 100644 --- a/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp +++ b/vnpy/api/xtp/vnxtp/include/autocxxpy/callback_wrapper.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "brigand.hpp" @@ -66,7 +67,7 @@ namespace autocxxpy template constexpr callback_type callback_type_of_v = callback_type_of::value; -#ifdef PYBIND11_OVERLOAD_NAME +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 template struct pybind11_static_caster { static pybind11::detail::overload_caster_t caster; @@ -75,18 +76,59 @@ namespace autocxxpy template AUTOCXXPY_SELECT_ANY pybind11::detail::overload_caster_t pybind11_static_caster::caster; + struct async_dispatch_exception : public std::exception + { + async_dispatch_exception(const char *what, const pybind11::object &instance, std::string function_name) + : std::exception(what), instance(instance), function_name(function_name) + {} + pybind11::object instance; + std::string function_name; + inline const char* what() noexcept + { + return std::exception::what(); + } + }; + + struct async_callback_exception_handler + { + using handler_type = std::function; + static handler_type custom_handler; + + inline static void handle_excepiton(const async_dispatch_exception&e) + { + if (custom_handler) + { + custom_handler(e); + } + } + + inline static void set_handler(const handler_type& handler) + { + custom_handler = handler; + } + }; + + AUTOCXXPY_SELECT_ANY async_callback_exception_handler::handler_type async_callback_exception_handler::custom_handler; #endif namespace arg_helper { + ////////////////////////////////////////////////////////////////////////// + // stores + ////////////////////////////////////////////////////////////////////////// + // # todo: char8, char16, char32, wchar_t, etc... // # todo: shall i copy only const type, treating non-const type as output pointer? - inline auto save(const char *val) + inline std::optional save(const char* val) { // match const char * + if (nullptr == val) AUTOCXXPY_UNLIKELY + return std::nullopt; // maybe empty string is also a choice? return std::string(val); } - inline auto save(char *val) + inline std::optional save(char* val) { // match char * + if (nullptr == val) AUTOCXXPY_UNLIKELY + return std::nullopt; // maybe empty string is also a choice? return std::string(val); } template @@ -101,29 +143,41 @@ namespace autocxxpy } template - inline T &save(T *val) + inline std::optional save(T * val) { // match pointer + if (nullptr == val) AUTOCXXPY_UNLIKELY + { + return std::nullopt; + } return *val; } template - inline T &save(const T *val) + inline std::optional& save(const T * val) { // match const pointer + if (nullptr == val) AUTOCXXPY_UNLIKELY + { + return std::nullopt; + } return const_cast(*val); } template - inline T &save(const T &val) + inline T& save(const T & val) { // match everything else : just use original type return const_cast(val); } + ////////////////////////////////////////////////////////////////////////// + // loads + ////////////////////////////////////////////////////////////////////////// + template struct loader { // match default(everyting besides pointer) template - inline to_type operator ()(src_type &val) + inline to_type operator ()(src_type& val) { return val; } @@ -132,57 +186,57 @@ namespace autocxxpy template struct loader> { // match const char [] - using to_type = const char *; - inline to_type operator ()(const std::string &val) + using to_type = const char*; + inline to_type operator ()(const std::string& val) { - return const_cast(val.data()); + return const_cast(val.data()); } }; template struct loader> { // match char [] - using to_type = char *; - inline to_type operator ()(const std::string &val) + using to_type = char*; + inline to_type operator ()(const std::string& val) { - return const_cast(val.data()); + return const_cast(val.data()); } }; template <> - struct loader + struct loader { // match const char * - using to_type = const char *; - inline to_type operator ()(const std::string &val) + using to_type = const char*; + inline to_type operator ()(const std::optional& val) { - return const_cast(val.data()); + if (val) AUTOCXXPY_LIKELY + return const_cast(val->data()); + return nullptr; } }; template <> - struct loader + struct loader { // match char * - using to_type = char *; - inline to_type operator ()(const std::string &val) + using to_type = char*; + inline to_type operator ()(const std::optional& val) { - return const_cast(val.data()); + if (val) AUTOCXXPY_LIKELY + return const_cast(val->data()); + return nullptr; } }; template - struct loader + struct loader { // match pointer template - inline to_type *operator ()(src_type &val) + inline to_type* operator ()(const std::optional& val) { // val to poiner - return const_cast(&val); + if (val) AUTOCXXPY_LIKELY + return const_cast(&(*val)); + return nullptr; } - - //template - //inline to_type *operator ()(src_type *val) - //{ // pointer to pointer - // return val; - //} }; }; @@ -195,7 +249,7 @@ namespace autocxxpy using class_type = class_of_member_method_t; public: template - inline static ret_type call(class_type *instance, const char *py_func_name, arg_types ... args) + inline static ret_type call(class_type* instance, const char* py_func_name, arg_types ... args) { if constexpr (callback_type_of_v == callback_type::Direct) { @@ -211,47 +265,70 @@ namespace autocxxpy } template - inline static void async(class_type *instance, const char *py_func_name, arg_types ... args) + inline static void async(class_type* instance, const char* py_func_name, arg_types ... args) { return async_impl(instance, py_func_name, std::index_sequence_for{}, args ...); } template - inline static ret_type sync(class_type *instance, const char * py_func_name, arg_types ... args) + inline static ret_type sync(class_type * instance, const char* py_func_name, arg_types ... args) { // if this code is under test environment, we don't need pybind11 // since header of pybind11 use #pragma once, no macros is defined, we use a public macro to check if pybind11 is included or not #ifdef PYBIND11_OVERLOAD_NAME pybind11::gil_scoped_acquire gil; - pybind11::function overload = pybind11::get_overload(static_cast(instance), py_func_name); - if (overload) { - auto o = overload(args ...); - if (pybind11::detail::cast_is_temporary_value_reference::value) { - auto & caster = pybind11_static_caster::caster; - return pybind11::detail::cast_ref(std::move(o), caster); + pybind11::function overload = pybind11::get_overload(static_cast(instance), py_func_name); + if (overload) AUTOCXXPY_LIKELY{ + try + { + auto result = overload(args ...); + if (pybind11::detail::cast_is_temporary_value_reference::value) + { + auto& caster = pybind11_static_caster::caster; + return pybind11::detail::cast_ref(std::move(result), caster); + } + else + { + return pybind11::detail::cast_safe(std::move(result)); + } + } + catch (const pybind11::error_already_set & e) + { + // todo: option to not to throw when sync is called directly + throw async_dispatch_exception(e.what(), pybind11::cast(instance), py_func_name); } - else return pybind11::detail::cast_safe(std::move(o)); } #endif return (instance->*method)(args ...); } private: template - inline static void async_impl(class_type *instance, const char *py_func_name, std::index_sequence, arg_types ... args) + inline static void async_impl(class_type * instance, const char* py_func_name, std::index_sequence, arg_types ... args) { // wrap for ctp like function calls: // all the pointer might be unavailable after this call, so copy its value into a tuple auto arg_tuple = std::make_tuple(arg_helper::save(args) ...); auto task = [instance, py_func_name, arg_tuple = std::move(arg_tuple)]() { - // resolve all value: - // if it was originally a pointer, then use pointer type. - // if it was originally a value, just keep a reference to that value. - sync( - instance, py_func_name, - arg_helper::loader, brigand::integral_constant > >{} - (std::get(arg_tuple)) ... - ); +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 + try + { +#endif + // resolve all value: + // if it was originally a pointer, then use pointer type. + // if it was originally a value, just keep a reference to that value. + sync( + instance, py_func_name, + arg_helper::loader, brigand::integral_constant > >{} + (std::get(arg_tuple)) ... + ); +#ifdef AUTOCXXPY_INCLUDED_PYBIND11 + } + catch (const async_dispatch_exception &e) + { + async_callback_exception_handler::handle_excepiton(e); + } +#endif }; dispatcher::instance().add(std::move(task)); } diff --git a/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp b/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp index 79301824..f0e28a91 100644 --- a/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp +++ b/vnpy/api/xtp/vnxtp/include/autocxxpy/config/config.hpp @@ -8,12 +8,30 @@ #ifndef AUTOCXXPY_UNUSED -#define AUTOCXXPY_UNUSED(x) (void)(x) +# define AUTOCXXPY_UNUSED(x) (void)(x) #endif #ifdef _MSC_VER -#define AUTOCXXPY_SELECT_ANY __declspec(selectany) +# define AUTOCXXPY_SELECT_ANY __declspec(selectany) #else -#define AUTOCXXPY_SELECT_ANY __attribute__ ((selectany)) -#endif \ No newline at end of file +# define AUTOCXXPY_SELECT_ANY __attribute__ ((selectany)) +#endif + + +#ifdef __has_cpp_attribute +# if __has_cpp_attribute(likely) +# define AUTOCXXPY_LIKELY [[likely]] +# endif +# if __has_cpp_attribute(unlikely) +# define AUTOCXXPY_UNLIKELY [[unlikely]] +# endif +#endif + +#ifndef AUTOCXXPY_LIKELY +#define AUTOCXXPY_LIKELY +#endif + +#ifndef AUTOCXXPY_UNLIKELY +#define AUTOCXXPY_UNLIKELY +#endif diff --git a/vnpy/api/xtp/vnxtp/vnxtp.vcxproj b/vnpy/api/xtp/vnxtp/vnxtp.vcxproj index 9811582a..e711c1af 100644 --- a/vnpy/api/xtp/vnxtp/vnxtp.vcxproj +++ b/vnpy/api/xtp/vnxtp/vnxtp.vcxproj @@ -119,6 +119,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -138,6 +139,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -160,6 +162,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console @@ -184,6 +187,7 @@ stdcpp17 4819 /bigobj %(AdditionalOptions) + true Console diff --git a/vnpy/api/xtp/vnxtp_XTP.pyi b/vnpy/api/xtp/vnxtp_XTP.pyi index 63aa7d94..f5ac8cb9 100644 --- a/vnpy/api/xtp/vnxtp_XTP.pyi +++ b/vnpy/api/xtp/vnxtp_XTP.pyi @@ -7,5 +7,20 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[Exception, object, str], bool]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + from . import vnxtp_XTP_API as API diff --git a/vnpy/api/xtp/vnxtp_XTP_API.pyi b/vnpy/api/xtp/vnxtp_XTP_API.pyi index 11706ffc..e3f98ddd 100644 --- a/vnpy/api/xtp/vnxtp_XTP_API.pyi +++ b/vnpy/api/xtp/vnxtp_XTP_API.pyi @@ -7,6 +7,21 @@ if typing.TYPE_CHECKING: from .vnxtp import * +def set_async_callback_exception_handler(handler: Callable[[Exception, object, str], bool]): + """ + set a customize exception handler for async callback in this module(pyd) + \a handler should return True if it handles that exception, + If the return value of \a handler is not True, exception will be re-thrown. + """ + ... + + +class AsyncDispatchException: + what: str + instance: object + function_name: str + + class TraderSpi(): diff --git a/vnpy/gateway/xtp/xtp_gateway.py b/vnpy/gateway/xtp/xtp_gateway.py index c3d8ac85..57f0b1de 100644 --- a/vnpy/gateway/xtp/xtp_gateway.py +++ b/vnpy/gateway/xtp/xtp_gateway.py @@ -1,3 +1,4 @@ +import sys from typing import Any, Sequence from vnpy.api.xtp.vnxtp import ( @@ -12,21 +13,21 @@ from vnpy.api.xtp.vnxtp import ( XTP_EXCHANGE_TYPE, XTP_LOG_LEVEL, XTP_PROTOCOL_TYPE, - XTP_TICKER_TYPE_STOCK, - XTP_TICKER_TYPE_INDEX, - XTP_TICKER_TYPE_FUND, - XTP_TICKER_TYPE_BOND, - XTP_TICKER_TYPE_OPTION, - XTP_PROTOCOL_TCP, - XTP_PROTOCOL_UDP + set_async_callback_exception_handler, + AsyncDispatchException, + XTP_TICKER_TYPE, ) from vnpy.event import EventEngine from vnpy.trader.constant import Exchange, Product from vnpy.trader.gateway import BaseGateway -from vnpy.trader.object import (CancelRequest, OrderRequest, SubscribeRequest, - TickData, ContractData, OrderData, TradeData, - PositionData, AccountData) +from vnpy.trader.object import ( + CancelRequest, + OrderRequest, + SubscribeRequest, + TickData, + ContractData, +) from vnpy.trader.utility import get_folder_path API = XTP.API @@ -38,19 +39,17 @@ EXCHANGE_XTP2VT = { EXCHANGE_VT2XTP = {v: k for k, v in EXCHANGE_XTP2VT.items()} PRODUCT_XTP2VT = { - XTP_TICKER_TYPE_STOCK: Product.EQUITY, - XTP_TICKER_TYPE_INDEX: Product.INDEX, - XTP_TICKER_TYPE_FUND: Product.FUND, - XTP_TICKER_TYPE_BOND: Product.BOND, - XTP_TICKER_TYPE_OPTION: Product.OPTION + XTP_TICKER_TYPE.XTP_TICKER_TYPE_STOCK: Product.EQUITY, + XTP_TICKER_TYPE.XTP_TICKER_TYPE_INDEX: Product.INDEX, + XTP_TICKER_TYPE.XTP_TICKER_TYPE_FUND: Product.FUND, + XTP_TICKER_TYPE.XTP_TICKER_TYPE_BOND: Product.BOND, + XTP_TICKER_TYPE.XTP_TICKER_TYPE_OPTION: Product.OPTION, } - symbol_name_map = {} class XtpGateway(BaseGateway): - default_setting = { "账号": "", "密码": "", @@ -59,7 +58,7 @@ class XtpGateway(BaseGateway): "行情端口": 0, "交易地址": "", "交易端口": 0, - "行情协议": ["TCP", "UDP"] + "行情协议": ["TCP", "UDP"], } def __init__(self, event_engine: EventEngine): @@ -68,19 +67,23 @@ class XtpGateway(BaseGateway): self.quote_api = XtpQuoteApi(self) + set_async_callback_exception_handler(self._async_callback_exception_handler) + pass + def connect(self, setting: dict): """""" - userid = setting['账号'] - password = setting['密码'] - client_id = setting['客户号'] - quote_ip = setting['行情地址'] - quote_port = setting['行情端口'] - trade_ip = setting['交易地址'] - trade_port = setting['交易端口'] + userid = setting["账号"] + password = setting["密码"] + client_id = int(setting["客户号"]) + quote_ip = setting["行情地址"] + quote_port = int(setting["行情端口"]) + trade_ip = setting["交易地址"] + trade_port = setting["交易端口"] quote_protocol = setting["行情协议"] - self.quote_api.connect(userid, password, client_id, - quote_ip, quote_port, quote_protocol) + self.quote_api.connect( + userid, password, client_id, quote_ip, quote_port, quote_protocol + ) def close(self): """""" @@ -102,11 +105,17 @@ class XtpGateway(BaseGateway): def query_position(self): pass + def _async_callback_exception_handler(self, e: AsyncDispatchException): + error_str = f"发生内部错误:\n" f"位置:{e.instance}.{e.function_name}" f"详细信息:{e.what}" + print(error_str, file=sys.stderr) + + self.write_log(error_str) # write_error function? + class XtpQuoteApi(API.QuoteSpi): - def __init__(self, gateway: BaseGateway): """""" + super(XtpQuoteApi, self).__init__() self.gateway = gateway self.gateway_name = gateway.gateway_name @@ -123,10 +132,10 @@ class XtpQuoteApi(API.QuoteSpi): self, userid: str, password: str, - client_id: str, + client_id: int, server_ip: str, - server_port: str, - quote_protocol: str + server_port: int, + quote_protocol: str, ): """""" if self.api: @@ -139,17 +148,15 @@ class XtpQuoteApi(API.QuoteSpi): self.server_port = server_port if quote_protocol == "CTP": - self.quote_protocol = XTP_PROTOCOL_TCP + self.quote_protocol = XTP_PROTOCOL_TYPE.XTP_PROTOCOL_TCP else: - self.quote_protocol = XTP_PROTOCOL_UDP + self.quote_protocol = XTP_PROTOCOL_TYPE.XTP_PROTOCOL_UDP # Create API object path = str(get_folder_path(self.gateway_name.lower())) self.api = API.QuoteApi.CreateQuoteApi( - self.client_id, - path, - XTP_LOG_LEVEL.XTP_LOG_LEVEL_TRACE + self.client_id, path, XTP_LOG_LEVEL.XTP_LOG_LEVEL_TRACE ) self.api.RegisterSpi(self) @@ -161,7 +168,7 @@ class XtpQuoteApi(API.QuoteSpi): self.server_port, self.userid, self.password, - self.quote_protocol + self.quote_protocol, ) if not ret: @@ -180,11 +187,13 @@ class XtpQuoteApi(API.QuoteSpi): def query_contract(self): """""" for exchange_id in EXCHANGE_XTP2VT.keys(): - self.api.QueryAllTickers(exchange_id) + ret = self.api.QueryAllTickers(exchange_id) + if ret != 0: + self.gateway.write_log("订阅合约失败") def check_error(self, func_name: str, error_info: XTPRspInfoStruct): """""" - if error_info.error_id: + if error_info and error_info.error_id: msg = f"{func_name}发生错误, 代码:{error_info.error_id},信息:{error_info.error_msg}" self.gateway.write_log(msg) return True @@ -199,21 +208,37 @@ class XtpQuoteApi(API.QuoteSpi): """""" self.check_error("行情接口", error_info) - def OnSubMarketData(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnSubMarketData( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" self.check_error("订阅行情", error_info) + return super().OnSubMarketData(ticker, error_info, is_last) - def OnUnSubMarketData(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnUnSubMarketData( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass - def OnDepthMarketData(self, market_data: XTPMarketDataStruct, bid1_qty: Sequence[int], - bid1_count: int, max_bid1_count: int, ask1_qty: Sequence[int], - ask1_count: int, max_ask1_count: int) -> Any: + def OnDepthMarketData( + self, + market_data: XTPMarketDataStruct, + bid1_qty: Sequence[int], + bid1_count: int, + max_bid1_count: int, + ask1_qty: Sequence[int], + ask1_count: int, + max_ask1_count: int, + ) -> Any: """""" - timestamp = market_data.date_time + timestamp = market_data.data_time tick = TickData( symbol=market_data.ticker, @@ -247,19 +272,27 @@ class XtpQuoteApi(API.QuoteSpi): ask_volume_3=market_data.ask_qty[2], ask_volume_4=market_data.ask_qty[3], ask_volume_5=market_data.ask_qty[4], - gateway_name=self.gateway_name + gateway_name=self.gateway_name, ) tick.name = symbol_name_map.get(tick.vt_symbol, tick.symbol) self.gateway.on_tick(tick) - def OnSubOrderBook(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnSubOrderBook( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass - def OnUnSubOrderBook(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnUnSubOrderBook( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass @@ -267,13 +300,21 @@ class XtpQuoteApi(API.QuoteSpi): """""" pass - def OnSubTickByTick(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnSubTickByTick( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass - def OnUnSubTickByTick(self, ticker: XTPSpecificTickerStruct, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnUnSubTickByTick( + self, + ticker: XTPSpecificTickerStruct, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass @@ -281,88 +322,106 @@ class XtpQuoteApi(API.QuoteSpi): """""" pass - def OnSubscribeAllMarketData(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllMarketData( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllMarketData(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllMarketData( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnSubscribeAllOrderBook(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllOrderBook( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllOrderBook(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllOrderBook( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnSubscribeAllTickByTick(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllTickByTick( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllTickByTick(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllTickByTick( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnQueryAllTickers(self, ticker_info: XTPQuoteStaticInfo, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: - """""" - return - # if self.check_error("查询合约", error_info): - # return + def OnQueryAllTickers( + self, + ticker_info: XTPQuoteStaticInfo, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: + if self.check_error("查询合约", error_info): + return - # contract = ContractData( - # symbol=ticker_info.ticker, - # exchange=EXCHANGE_XTP2VT[ticker_info.exchange_id], - # name=ticker_info.ticker_name, - # product=PRODUCT_XTP2VT[ticker_info.ticker_type], - # size=1, - # pricetick=ticker_info.pricetick, - # min_volume=ticker_info.buy_qty_unit, - # gateway_name=self.gateway_name - # ) - # self.gateway.on_contract(contract) + contract = ContractData( + symbol=ticker_info.ticker, + exchange=EXCHANGE_XTP2VT[ticker_info.exchange_id], + name=ticker_info.ticker_name, + product=PRODUCT_XTP2VT[ticker_info.ticker_type], + size=1, + pricetick=ticker_info.pricetick, + min_volume=ticker_info.buy_qty_unit, + gateway_name=self.gateway_name, + ) + self.gateway.on_contract(contract) - # symbol_name_map[contract.vt_symbol] = contract.name + symbol_name_map[contract.vt_symbol] = contract.name - def OnQueryTickersPriceInfo(self, ticker_info: XTPTickerPriceInfo, error_info: XTPRspInfoStruct, - is_last: bool) -> Any: + def OnQueryTickersPriceInfo( + self, + ticker_info: XTPTickerPriceInfo, + error_info: XTPRspInfoStruct, + is_last: bool, + ) -> Any: """""" pass - def OnSubscribeAllOptionMarketData(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllOptionMarketData( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllOptionMarketData(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllOptionMarketData( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnSubscribeAllOptionOrderBook(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllOptionOrderBook( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllOptionOrderBook(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllOptionOrderBook( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnSubscribeAllOptionTickByTick(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnSubscribeAllOptionTickByTick( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass - def OnUnSubscribeAllOptionTickByTick(self, exchange_id: XTP_EXCHANGE_TYPE, - error_info: XTPRspInfoStruct) -> Any: + def OnUnSubscribeAllOptionTickByTick( + self, exchange_id: XTP_EXCHANGE_TYPE, error_info: XTPRspInfoStruct + ) -> Any: """""" pass