[Add] XtpGateway support margin trading
This commit is contained in:
parent
9e5258b8df
commit
b56d0ce160
@ -35,20 +35,20 @@ def main():
|
|||||||
|
|
||||||
main_engine = MainEngine(event_engine)
|
main_engine = MainEngine(event_engine)
|
||||||
main_engine.add_gateway(XtpGateway)
|
main_engine.add_gateway(XtpGateway)
|
||||||
main_engine.add_gateway(CtpGateway)
|
# main_engine.add_gateway(CtpGateway)
|
||||||
# main_engine.add_gateway(CtptestGateway)
|
# main_engine.add_gateway(CtptestGateway)
|
||||||
main_engine.add_gateway(FemasGateway)
|
# main_engine.add_gateway(FemasGateway)
|
||||||
main_engine.add_gateway(IbGateway)
|
# main_engine.add_gateway(IbGateway)
|
||||||
main_engine.add_gateway(FutuGateway)
|
# main_engine.add_gateway(FutuGateway)
|
||||||
main_engine.add_gateway(BitmexGateway)
|
# main_engine.add_gateway(BitmexGateway)
|
||||||
main_engine.add_gateway(TigerGateway)
|
# main_engine.add_gateway(TigerGateway)
|
||||||
main_engine.add_gateway(OesGateway)
|
# main_engine.add_gateway(OesGateway)
|
||||||
main_engine.add_gateway(OkexGateway)
|
# main_engine.add_gateway(OkexGateway)
|
||||||
main_engine.add_gateway(HuobiGateway)
|
# main_engine.add_gateway(HuobiGateway)
|
||||||
main_engine.add_gateway(BitfinexGateway)
|
# main_engine.add_gateway(BitfinexGateway)
|
||||||
main_engine.add_gateway(OnetokenGateway)
|
# main_engine.add_gateway(OnetokenGateway)
|
||||||
main_engine.add_gateway(OkexfGateway)
|
# main_engine.add_gateway(OkexfGateway)
|
||||||
main_engine.add_gateway(HbdmGateway)
|
# main_engine.add_gateway(HbdmGateway)
|
||||||
|
|
||||||
main_engine.add_app(CtaStrategyApp)
|
main_engine.add_app(CtaStrategyApp)
|
||||||
main_engine.add_app(CtaBacktesterApp)
|
main_engine.add_app(CtaBacktesterApp)
|
||||||
|
@ -17,6 +17,7 @@ from vnpy.api.xtp.vnxtp import (
|
|||||||
XTPOrderInfo,
|
XTPOrderInfo,
|
||||||
XTPTradeReport,
|
XTPTradeReport,
|
||||||
XTPOrderCancelInfo,
|
XTPOrderCancelInfo,
|
||||||
|
XTPCrdDebtInfo,
|
||||||
XTPQueryStkPositionRsp,
|
XTPQueryStkPositionRsp,
|
||||||
XTPQueryAssetRsp,
|
XTPQueryAssetRsp,
|
||||||
XTPStructuredFundInfo,
|
XTPStructuredFundInfo,
|
||||||
@ -32,6 +33,11 @@ from vnpy.api.xtp.vnxtp import (
|
|||||||
XTP_TE_RESUME_TYPE,
|
XTP_TE_RESUME_TYPE,
|
||||||
XTP_SIDE_BUY,
|
XTP_SIDE_BUY,
|
||||||
XTP_SIDE_SELL,
|
XTP_SIDE_SELL,
|
||||||
|
XTP_SIDE_MARGIN_TRADE,
|
||||||
|
XTP_SIDE_SHORT_SELL,
|
||||||
|
XTP_SIDE_REPAY_MARGIN,
|
||||||
|
XTP_SIDE_REPAY_STOCK,
|
||||||
|
XTP_ACCOUNT_TYPE,
|
||||||
XTP_BUSINESS_TYPE,
|
XTP_BUSINESS_TYPE,
|
||||||
XTP_TICKER_TYPE,
|
XTP_TICKER_TYPE,
|
||||||
XTP_MARKET_TYPE,
|
XTP_MARKET_TYPE,
|
||||||
@ -40,7 +46,7 @@ from vnpy.api.xtp.vnxtp import (
|
|||||||
)
|
)
|
||||||
from vnpy.event import EventEngine
|
from vnpy.event import EventEngine
|
||||||
from vnpy.trader.event import EVENT_TIMER
|
from vnpy.trader.event import EVENT_TIMER
|
||||||
from vnpy.trader.constant import Exchange, Product, Direction, OrderType, Status
|
from vnpy.trader.constant import Exchange, Product, Direction, OrderType, Status, Offset
|
||||||
from vnpy.trader.gateway import BaseGateway
|
from vnpy.trader.gateway import BaseGateway
|
||||||
from vnpy.trader.object import (CancelRequest, OrderRequest, SubscribeRequest,
|
from vnpy.trader.object import (CancelRequest, OrderRequest, SubscribeRequest,
|
||||||
TickData, ContractData, OrderData, TradeData,
|
TickData, ContractData, OrderData, TradeData,
|
||||||
@ -71,9 +77,17 @@ PRODUCT_XTP2VT = {
|
|||||||
XTP_TICKER_TYPE.XTP_TICKER_TYPE_OPTION: Product.OPTION
|
XTP_TICKER_TYPE.XTP_TICKER_TYPE_OPTION: Product.OPTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# DIRECTION_VT2XTP = {
|
||||||
|
# Direction.LONG: XTP_SIDE_BUY,
|
||||||
|
# Direction.SHORT: XTP_SIDE_SELL
|
||||||
|
# }
|
||||||
DIRECTION_VT2XTP = {
|
DIRECTION_VT2XTP = {
|
||||||
Direction.LONG: XTP_SIDE_BUY,
|
(Direction.LONG, Offset.OPEN): XTP_SIDE_MARGIN_TRADE,
|
||||||
Direction.SHORT: XTP_SIDE_SELL
|
(Direction.SHORT, Offset.CLOSE): XTP_SIDE_REPAY_MARGIN,
|
||||||
|
(Direction.SHORT, Offset.OPEN): XTP_SIDE_SHORT_SELL,
|
||||||
|
(Direction.LONG, Offset.CLOSE): XTP_SIDE_REPAY_STOCK,
|
||||||
|
(Direction.SHORT, Offset.NONE): XTP_SIDE_BUY,
|
||||||
|
(Direction.LONG, Offset.NONE): XTP_SIDE_SELL,
|
||||||
}
|
}
|
||||||
DIRECTION_XTP2VT = {v: k for k, v in DIRECTION_VT2XTP.items()}
|
DIRECTION_XTP2VT = {v: k for k, v in DIRECTION_VT2XTP.items()}
|
||||||
|
|
||||||
@ -95,6 +109,7 @@ STATUS_XTP2VT = {
|
|||||||
|
|
||||||
|
|
||||||
symbol_name_map = {}
|
symbol_name_map = {}
|
||||||
|
symbol_exchange_map = {}
|
||||||
|
|
||||||
|
|
||||||
class XtpGateway(BaseGateway):
|
class XtpGateway(BaseGateway):
|
||||||
@ -428,6 +443,9 @@ class XtpQuoteApi(API.QuoteSpi):
|
|||||||
|
|
||||||
symbol_name_map[contract.vt_symbol] = contract.name
|
symbol_name_map[contract.vt_symbol] = contract.name
|
||||||
|
|
||||||
|
if contract.product != Product.INDEX:
|
||||||
|
symbol_exchange_map[contract.symbol] = contract.exchange
|
||||||
|
|
||||||
if is_last:
|
if is_last:
|
||||||
self.gateway.write_log(f"{contract.exchange.value}合约信息查询成功")
|
self.gateway.write_log(f"{contract.exchange.value}合约信息查询成功")
|
||||||
|
|
||||||
@ -487,6 +505,13 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
self.session_id = 0
|
self.session_id = 0
|
||||||
self.reqid = 0
|
self.reqid = 0
|
||||||
|
|
||||||
|
# Whether current account supports margin or option
|
||||||
|
self.margin_trading = False
|
||||||
|
self.option_trading = False
|
||||||
|
|
||||||
|
#
|
||||||
|
self.short_positions = {}
|
||||||
|
|
||||||
def connect(
|
def connect(
|
||||||
self,
|
self,
|
||||||
userid: str,
|
userid: str,
|
||||||
@ -564,9 +589,13 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
xtp_req.market = MARKET_VT2XTP[req.exchange]
|
xtp_req.market = MARKET_VT2XTP[req.exchange]
|
||||||
xtp_req.price = req.price
|
xtp_req.price = req.price
|
||||||
xtp_req.quantity = int(req.volume)
|
xtp_req.quantity = int(req.volume)
|
||||||
xtp_req.side = DIRECTION_VT2XTP[req.direction]
|
xtp_req.side = DIRECTION_VT2XTP.get((req.direction, req.offset), "")
|
||||||
xtp_req.price_type = ORDERTYPE_VT2XTP[req.type]
|
xtp_req.price_type = ORDERTYPE_VT2XTP[req.type]
|
||||||
xtp_req.business_type = XTP_BUSINESS_TYPE.XTP_BUSINESS_TYPE_CASH
|
|
||||||
|
if req.offset == Offset.NONE:
|
||||||
|
xtp_req.business_type = XTP_BUSINESS_TYPE.XTP_BUSINESS_TYPE_CASH
|
||||||
|
else:
|
||||||
|
xtp_req.business_type = XTP_BUSINESS_TYPE.XTP_BUSINESS_TYPE_MARGIN
|
||||||
|
|
||||||
orderid = self.api.InsertOrder(xtp_req, self.session_id)
|
orderid = self.api.InsertOrder(xtp_req, self.session_id)
|
||||||
|
|
||||||
@ -595,6 +624,10 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
self.reqid += 1
|
self.reqid += 1
|
||||||
self.api.QueryPosition("", self.session_id, self.reqid)
|
self.api.QueryPosition("", self.session_id, self.reqid)
|
||||||
|
|
||||||
|
if self.margin_trading:
|
||||||
|
self.reqid += 1
|
||||||
|
self.api.QueryCreditDebtInfo(self.session_id, self.reqid)
|
||||||
|
|
||||||
def check_error(self, func_name: str, error_info: XTPRspInfoStruct):
|
def check_error(self, func_name: str, error_info: XTPRspInfoStruct):
|
||||||
""""""
|
""""""
|
||||||
if error_info and error_info.error_id:
|
if error_info and error_info.error_id:
|
||||||
@ -617,12 +650,15 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
""""""
|
""""""
|
||||||
self.check_error("委托下单", error_info)
|
self.check_error("委托下单", error_info)
|
||||||
|
|
||||||
|
direction, offset = DIRECTION_XTP2VT[ order_info.side]
|
||||||
|
|
||||||
order = OrderData(
|
order = OrderData(
|
||||||
symbol=order_info.ticker,
|
symbol=order_info.ticker,
|
||||||
exchange=MARKET_XTP2VT[order_info.market],
|
exchange=MARKET_XTP2VT[order_info.market],
|
||||||
orderid=str(order_info.order_xtp_id),
|
orderid=str(order_info.order_xtp_id),
|
||||||
type=ORDERTYPE_XTP2VT[order_info.price_type],
|
type=ORDERTYPE_XTP2VT[order_info.price_type],
|
||||||
direction=DIRECTION_XTP2VT[order_info.side],
|
direction=direction,
|
||||||
|
offset=offset,
|
||||||
price=order_info.price,
|
price=order_info.price,
|
||||||
volume=order_info.quantity,
|
volume=order_info.quantity,
|
||||||
traded=order_info.qty_traded,
|
traded=order_info.qty_traded,
|
||||||
@ -635,12 +671,15 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
|
|
||||||
def OnTradeEvent(self, trade_info: XTPTradeReport, session_id: int) -> Any:
|
def OnTradeEvent(self, trade_info: XTPTradeReport, session_id: int) -> Any:
|
||||||
""""""
|
""""""
|
||||||
|
direction, offset = DIRECTION_XTP2VT[trade_info.side]
|
||||||
|
|
||||||
trade = TradeData(
|
trade = TradeData(
|
||||||
symbol=trade_info.ticker,
|
symbol=trade_info.ticker,
|
||||||
exchange=MARKET_XTP2VT[trade_info.market],
|
exchange=MARKET_XTP2VT[trade_info.market],
|
||||||
orderid=str(trade_info.order_xtp_id),
|
orderid=str(trade_info.order_xtp_id),
|
||||||
tradeid=str(trade_info.exec_id),
|
tradeid=str(trade_info.exec_id),
|
||||||
direction=DIRECTION_XTP2VT[trade_info.side],
|
direction=direction,
|
||||||
|
offset=offset,
|
||||||
price=trade_info.price,
|
price=trade_info.price,
|
||||||
volume=trade_info.quantity,
|
volume=trade_info.quantity,
|
||||||
time=trade_info.trade_time,
|
time=trade_info.trade_time,
|
||||||
@ -682,7 +721,7 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
position = PositionData(
|
position = PositionData(
|
||||||
symbol=xtp_position.ticker,
|
symbol=xtp_position.ticker,
|
||||||
exchange=MARKET_XTP2VT[xtp_position.market],
|
exchange=MARKET_XTP2VT[xtp_position.market],
|
||||||
direction=Direction.NET,
|
direction=Direction.LONG,
|
||||||
volume=xtp_position.total_qty,
|
volume=xtp_position.total_qty,
|
||||||
frozen=xtp_position.locked_position,
|
frozen=xtp_position.locked_position,
|
||||||
price=xtp_position.avg_price,
|
price=xtp_position.avg_price,
|
||||||
@ -703,6 +742,11 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
)
|
)
|
||||||
self.gateway.on_account(account)
|
self.gateway.on_account(account)
|
||||||
|
|
||||||
|
if asset.account_type == XTP_ACCOUNT_TYPE.XTP_ACCOUNT_CREDIT:
|
||||||
|
self.margin_trading = True
|
||||||
|
elif asset.account_type == XTP_ACCOUNT_TYPE.XTP_ACCOUNT_DERIVE:
|
||||||
|
self.option_trading = True
|
||||||
|
|
||||||
def OnQueryStructuredFund(self, fund_info: XTPStructuredFundInfo, error_info: XTPRspInfoStruct,
|
def OnQueryStructuredFund(self, fund_info: XTPStructuredFundInfo, error_info: XTPRspInfoStruct,
|
||||||
is_last: bool, session_id: int) -> Any:
|
is_last: bool, session_id: int) -> Any:
|
||||||
""""""
|
""""""
|
||||||
@ -741,3 +785,29 @@ class XtpTraderApi(API.TraderSpi):
|
|||||||
is_last: bool, session_id: int) -> Any:
|
is_last: bool, session_id: int) -> Any:
|
||||||
""""""
|
""""""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def OnQueryCreditDebtInfo(self, debt_info: XTPCrdDebtInfo, error_info: XTPRspInfoStruct,
|
||||||
|
request_id: int, is_last: bool, session_id: int) -> Any:
|
||||||
|
""""""
|
||||||
|
if debt_info.debt_type == 1:
|
||||||
|
symbol = debt_info.ticker
|
||||||
|
exchange = MARKET_XTP2VT[debt_info.market]
|
||||||
|
|
||||||
|
position = self.short_positions.get(symbol, None)
|
||||||
|
if not position:
|
||||||
|
position = PositionData(
|
||||||
|
symbol=symbol,
|
||||||
|
exchange=exchange,
|
||||||
|
direction=Direction.SHORT,
|
||||||
|
gateway_name=self.gateway_name
|
||||||
|
)
|
||||||
|
self.short_positions[symbol] = position
|
||||||
|
|
||||||
|
position.volume += debt_info.remain_qty
|
||||||
|
|
||||||
|
if is_last:
|
||||||
|
for position in self.short_positions.values():
|
||||||
|
self.gateway.on_position(position)
|
||||||
|
|
||||||
|
self.short_positions.clear()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user