[Mod]add offset converter function to cta strategy app
This commit is contained in:
parent
873a8080ab
commit
353a9c0d8d
297
vnpy/app/cta_strategy/converter.py
Normal file
297
vnpy/app/cta_strategy/converter.py
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
""""""
|
||||||
|
from copy import copy
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from vnpy.trader.engine import MainEngine
|
||||||
|
from vnpy.trader.object import (
|
||||||
|
ContractData,
|
||||||
|
OrderData,
|
||||||
|
TradeData,
|
||||||
|
PositionData,
|
||||||
|
OrderRequest
|
||||||
|
)
|
||||||
|
from vnpy.trader.constant import (Direction, Offset, Exchange)
|
||||||
|
|
||||||
|
|
||||||
|
class OffsetConverter:
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, main_engine: MainEngine):
|
||||||
|
""""""
|
||||||
|
self.main_engine = main_engine
|
||||||
|
self.holdings = {}
|
||||||
|
|
||||||
|
def update_position(self, position: PositionData):
|
||||||
|
""""""
|
||||||
|
if not self.is_convert_required(position.vt_symbol):
|
||||||
|
return
|
||||||
|
|
||||||
|
holding = self.get_position_holding(position.vt_symbol)
|
||||||
|
holding.update_position(position)
|
||||||
|
|
||||||
|
def update_trade(self, trade: TradeData):
|
||||||
|
""""""
|
||||||
|
if not self.is_convert_required(trade.vt_symbol):
|
||||||
|
return
|
||||||
|
|
||||||
|
holding = self.get_position_holding(trade.vt_symbol)
|
||||||
|
holding.update_trade(trade)
|
||||||
|
|
||||||
|
def update_order(self, order: OrderData):
|
||||||
|
""""""
|
||||||
|
if not self.is_convert_required(order.vt_symbol):
|
||||||
|
return
|
||||||
|
|
||||||
|
holding = self.get_position_holding(order.vt_symbol)
|
||||||
|
holding.update_order(order)
|
||||||
|
|
||||||
|
def update_order_request(self, req: OrderRequest):
|
||||||
|
""""""
|
||||||
|
if not self.is_convert_required(req.vt_symbol):
|
||||||
|
return
|
||||||
|
|
||||||
|
holding = self.get_position_holding(req.vt_symbol)
|
||||||
|
holding.update_order_request(req)
|
||||||
|
|
||||||
|
def get_position_holding(self, vt_symbol: str):
|
||||||
|
""""""
|
||||||
|
holding = self.holdings.get(vt_symbol, None)
|
||||||
|
if not holding:
|
||||||
|
contract = self.main_engine.get_contract(vt_symbol)
|
||||||
|
holding = PositionHolding(contract)
|
||||||
|
self.holdings[vt_symbol] = holding
|
||||||
|
return holding
|
||||||
|
|
||||||
|
def convert_order_request(self, req: OrderRequest, lock: bool):
|
||||||
|
""""""
|
||||||
|
if not self.is_convert_required(req.vt_symbol):
|
||||||
|
return [req]
|
||||||
|
|
||||||
|
holding = self.get_position_holding(req.vt_symbol)
|
||||||
|
|
||||||
|
if lock:
|
||||||
|
return holding.convert_order_request_lock(req)
|
||||||
|
elif req.exchange == Exchange.SHFE:
|
||||||
|
return holding.convert_order_request_shfe(req)
|
||||||
|
else:
|
||||||
|
return [req]
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
|
def is_convert_required(self, vt_symbol: str):
|
||||||
|
"""
|
||||||
|
Check if the contract needs offset convert.
|
||||||
|
"""
|
||||||
|
contract = self.main_engine.get(vt_symbol)
|
||||||
|
|
||||||
|
# Only contracts with long-short position mode requires convert
|
||||||
|
if not contract.net_position:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class PositionHolding:
|
||||||
|
""""""
|
||||||
|
|
||||||
|
def __init__(self, contract: ContractData):
|
||||||
|
""""""
|
||||||
|
self.vt_symbol = contract.vt_symbol
|
||||||
|
self.exchange = contract.exchange
|
||||||
|
|
||||||
|
self.active_orders = {}
|
||||||
|
|
||||||
|
self.long_pos = 0
|
||||||
|
self.long_yd = 0
|
||||||
|
self.long_td = 0
|
||||||
|
|
||||||
|
self.short_pos = 0
|
||||||
|
self.short_yd = 0
|
||||||
|
self.short_td = 0
|
||||||
|
|
||||||
|
self.long_pos_frozen = 0
|
||||||
|
self.long_yd_frozen = 0
|
||||||
|
self.long_td_frozen = 0
|
||||||
|
|
||||||
|
self.short_pos_frozen = 0
|
||||||
|
self.short_yd_frozen = 0
|
||||||
|
self.short_td_frozen = 0
|
||||||
|
|
||||||
|
def update_position(self, position: PositionData):
|
||||||
|
""""""
|
||||||
|
if position.direction == Direction.LONG:
|
||||||
|
self.long_pos = position.volume
|
||||||
|
self.long_yd = position.yd_volume
|
||||||
|
self.long_td = self.long_pos - self.long_yd
|
||||||
|
else:
|
||||||
|
self.short_pos = position.volume
|
||||||
|
self.short_yd = position.yd_volume
|
||||||
|
self.short_td = self.short_pos - self.short_yd
|
||||||
|
|
||||||
|
def update_order(self, order: OrderData):
|
||||||
|
""""""
|
||||||
|
if order.is_active():
|
||||||
|
self.active_orders[order.vt_orderid] = order
|
||||||
|
else:
|
||||||
|
if order.vt_orderid in self.active_orders:
|
||||||
|
self.active_orders.pop(order.vt_orderid)
|
||||||
|
|
||||||
|
self.calculate_frozen()
|
||||||
|
|
||||||
|
def update_order_request(self, req: OrderRequest, vt_orderid: str):
|
||||||
|
""""""
|
||||||
|
gateway_name, orderid = vt_orderid.split(".")
|
||||||
|
order = req.create_order_data(orderid, gateway_name)
|
||||||
|
self.update_order(order)
|
||||||
|
|
||||||
|
def update_trade(self, trade: TradeData):
|
||||||
|
""""""
|
||||||
|
if trade.direction == Direction.LONG:
|
||||||
|
if trade.offset == Offset.OPEN:
|
||||||
|
self.long_td += trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSETODAY:
|
||||||
|
self.short_td -= trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSEYESTERDAY:
|
||||||
|
self.short_yd -= trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSE:
|
||||||
|
if trade.exchange == Exchange.SHFE:
|
||||||
|
self.short_yd -= trade.volume
|
||||||
|
else:
|
||||||
|
self.short_td -= trade.volume
|
||||||
|
|
||||||
|
if self.short_td < 0:
|
||||||
|
self.short_yd += self.short_td
|
||||||
|
self.short_td = 0
|
||||||
|
else:
|
||||||
|
if trade.offset == Offset.OPEN:
|
||||||
|
self.short_td += trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSETODAY:
|
||||||
|
self.long_td -= trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSEYESTERDAY:
|
||||||
|
self.long_yd -= trade.volume
|
||||||
|
elif trade.offset == Offset.CLOSE:
|
||||||
|
if trade.exchange == Exchange.SHFE:
|
||||||
|
self.long_yd -= trade.volume
|
||||||
|
else:
|
||||||
|
self.long_td -= trade.volume
|
||||||
|
|
||||||
|
if self.long_td < 0:
|
||||||
|
self.long_yd += self.long_td
|
||||||
|
self.long_td = 0
|
||||||
|
|
||||||
|
self.long_pos = self.long_td + self.long_yd
|
||||||
|
self.short_pos = self.short_td + self.short_yd
|
||||||
|
|
||||||
|
def calculate_frozen(self):
|
||||||
|
""""""
|
||||||
|
self.long_pos_frozen = 0
|
||||||
|
self.long_yd_frozen = 0
|
||||||
|
self.long_td_frozen = 0
|
||||||
|
|
||||||
|
self.short_pos_frozen = 0
|
||||||
|
self.short_yd_frozen = 0
|
||||||
|
self.short_td_frozen = 0
|
||||||
|
|
||||||
|
for order in self.active_orders.values():
|
||||||
|
# Ignore position open orders
|
||||||
|
if order.offset == Offset.OPEN:
|
||||||
|
continue
|
||||||
|
|
||||||
|
frozen = order.volume - order.traded
|
||||||
|
|
||||||
|
if order.direction == Direction.LONG:
|
||||||
|
if order.offset == Offset.CLOSETODAY:
|
||||||
|
self.short_td_frozen += frozen
|
||||||
|
elif order.offset == Offset.CLOSEYESTERDAY:
|
||||||
|
self.short_yd_frozen += frozen
|
||||||
|
elif order.offset == Offset.CLOSE:
|
||||||
|
self.short_td_frozen += frozen
|
||||||
|
|
||||||
|
if self.short_td_frozen > self.short_td:
|
||||||
|
self.short_yd_frozen += (self.short_td_frozen
|
||||||
|
- self.short_td)
|
||||||
|
self.short_td_frozen = self.short_td
|
||||||
|
elif order.direction == Direction.SHORT:
|
||||||
|
if order.offset == Offset.CLOSETODAY:
|
||||||
|
self.long_td_frozen += frozen
|
||||||
|
elif order.offset == Offset.CLOSEYESTERDAY:
|
||||||
|
self.long_yd_frozen += frozen
|
||||||
|
elif order.offset == Offset.CLOSE:
|
||||||
|
self.long_td_frozen += frozen
|
||||||
|
|
||||||
|
if self.long_td_frozen > self.short_td:
|
||||||
|
self.long_yd_frozen += (self.long_td_frozen
|
||||||
|
- self.long_td)
|
||||||
|
self.long_td_frozen = self.long_td
|
||||||
|
|
||||||
|
self.long_pos_frozen = self.long_td_frozen + self.long_yd_frozen
|
||||||
|
self.short_pos_frozen = self.short_td_frozen + self.short_yd_frozen
|
||||||
|
|
||||||
|
def convert_order_request_shfe(self, req: OrderRequest):
|
||||||
|
""""""
|
||||||
|
if req.offset == Offset.OPEN:
|
||||||
|
return [req]
|
||||||
|
|
||||||
|
if req.direction == Direction.LONG:
|
||||||
|
pos_available = self.short_pos - self.short_pos_frozen
|
||||||
|
td_available = self.short_td - self.short_td_frozen
|
||||||
|
else:
|
||||||
|
pos_available = self.long_pos - self.long_pos_frozen
|
||||||
|
td_available = self.long_td - self.long_td_frozen
|
||||||
|
|
||||||
|
if req.volume > pos_available:
|
||||||
|
return [req]
|
||||||
|
elif req.volume <= td_available:
|
||||||
|
req_td = copy(req)
|
||||||
|
req_td.offset = Offset.CLOSETODAY
|
||||||
|
return [req_td]
|
||||||
|
else:
|
||||||
|
req_list = []
|
||||||
|
|
||||||
|
if td_available > 0:
|
||||||
|
req_td = copy(req)
|
||||||
|
req_td.offset = Offset.CLOSETODAY
|
||||||
|
req_td.volume = td_available
|
||||||
|
req_list.append(req_td)
|
||||||
|
|
||||||
|
req_yd = copy(req)
|
||||||
|
req_yd.offset = Offset.CLOSEYESTERDAY
|
||||||
|
req_yd.volume = req.volume - td_available
|
||||||
|
req_list.append(req_yd)
|
||||||
|
|
||||||
|
return req_list
|
||||||
|
|
||||||
|
def convert_order_request_lock(self, req: OrderRequest):
|
||||||
|
""""""
|
||||||
|
if req.direction == Direction.LONG:
|
||||||
|
td_volume = self.short_td
|
||||||
|
yd_available = self.short_yd - self.short_yd_frozen
|
||||||
|
else:
|
||||||
|
td_volume = self.long_td
|
||||||
|
yd_available = self.long_yd - self.long_yd_frozen
|
||||||
|
|
||||||
|
# If there is td_volume, we can only lock position
|
||||||
|
if td_volume:
|
||||||
|
req_open = copy(req)
|
||||||
|
req_open.offset = Offset.OPEN
|
||||||
|
return [req_open]
|
||||||
|
# If no td_volume, we close opposite yd position first
|
||||||
|
# then open new position
|
||||||
|
else:
|
||||||
|
open_volume = max(0, yd_available - req.volume)
|
||||||
|
req_list = []
|
||||||
|
|
||||||
|
if yd_available:
|
||||||
|
req_yd = copy(req)
|
||||||
|
if self.exchange == Exchange.SHFE:
|
||||||
|
req_yd.offset = Offset.CLOSEYESTERDAY
|
||||||
|
else:
|
||||||
|
req_yd.offset = Offset.CLOSE
|
||||||
|
req_list.append(req_yd)
|
||||||
|
|
||||||
|
if open_volume:
|
||||||
|
req_open = copy(req)
|
||||||
|
req_open.offset = Offset.OPEN
|
||||||
|
req_open.volume = open_volume
|
||||||
|
req_list.append(req_open)
|
||||||
|
|
||||||
|
return req_list
|
Loading…
Reference in New Issue
Block a user