最近老是出现订单状态卡在提交中的情况,所以在process_order_event的时候把orderdata写到日志里了,结果发现这个处理的顺序有点问题。从代码来看,在报单的时候,首先会生成的是初始的OrderData,并调用一次on_order(),也就是这时process_order_event()收到的data状态为提交中,时间为None;之后收到的就是订单回报了,这部分处理的顺序没发现什么问题。
如题,由于目前有多个账户在运行,用的script_engine调用write_log()输出日志会输出到同一个文件中,混到一起查看日志会有些错乱,这个问题该怎么解决呢?
最近偶尔会碰到收不到订单回报的情况,我用的是脚本策略,下单之后用get_order(vt_orderid)查询订单,发现status一直是SUBMITTING,但实际已经成交了;如果重新登陆账号,又可以接收到这笔订单的回报,也可以查到ALLTRADED的状态。
我看了一下源码,发现orderdata对象在生成的时候默认的status就是SUBMITTING,后续可能没有收到回报,所以没有触发on_order()来更新内存里的数据,不知道是不是因为网络原因有丢包之类的情况?
目前用的是2.9版本的脚本策略,我想要在集合竞价的时候平掉股指期货的对锁单,但是出现了成交编号相同的情况,也就是成交编号为277的那一笔是自成交。用get_order可以看到两笔订单都是成交了4手的,但是用get_trade会发现自成交的那一手只记录在了其中一边的信息之中,这会导致我更新本地仓位时出现问题,应该怎么解决呢?
业务原因,需要同时操作融航和CTP的账户,自己搞了个多账户监控的程序,但是想要在一个程序里同时登录融航和CTP没法实现。从二者的gateway代码中没有看出来哪里会出现冲突,是因为二者的tdapi底层有不兼容的问题?
如题,目前有多个账户在交易,开多个快期客户端看起来很麻烦,目前是想找个办法对这些账户的资金、报单、成交等信息在一个面板上进行监控,不知道有没有比较方便的办法?目前知道的是快期专业版可以,但免费账户只能绑定两个实盘账号。
之前在用2.9版本的脚本策略scripttrader交易的时候,遇到了一个平今转换上的问题,即不会自动把Offset.CLOSE转换成Offset.CLOSETODAY或Offset.CLOSEYESTERDAY,最终导致被拒单。当然可以为每个策略自行做本地仓位记录,记录今仓和昨仓的量,然后在下单时直接指定平今平昨,但是更新起来会比较麻烦,不如调用vnpy\trader\converter.py来实现自动转换。一步一步排查之后,我发现了问题所在,斗胆在下面展示一下我的解决方案。
首先来看vnpy_scripttrader\engine.py里面的send_order()函数:
def send_order(
self,
vt_symbol: str,
price: float,
volume: float,
direction: Direction,
offset: Offset,
order_type: OrderType
) -> str:
""""""
contract: Optional[ContractData] = self.get_contract(vt_symbol)
if not contract:
return ""
req: OrderRequest = OrderRequest(
symbol=contract.symbol,
exchange=contract.exchange,
direction=direction,
type=order_type,
volume=volume,
price=price,
offset=offset,
reference=APP_NAME
)
vt_orderid: str = self.main_engine.send_order(req, contract.gateway_name)
return vt_orderid
这里在使用sell()或者cover()这两个平仓的函数时,他传入的Offset对象是Offset.CLOSE,这里没有对平今平昨进行转换,而传Offset.CLOSE默认是平昨,所以在没有昨仓时会被拒单。那么我们需要在这一步用trader\converter.py中的OffsetConverter.convert_order_request()进行转换,但是似乎脚本策略中初始化一个OffsetConverter对象后直接调用并不能做到平今转换,还需要对OffsetConverter以及PositionHolding进行修改。原始代码如下
class OffsetConverter:
""""""
def __init__(self, main_engine: MainEngine):
""""""
self.main_engine: MainEngine = main_engine
self.holdings: Dict[str, "PositionHolding"] = {}
def update_position(self, position: PositionData) -> None:
""""""
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) -> None:
""""""
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) -> None:
""""""
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, vt_orderid: str) -> None:
""""""
if not self.is_convert_required(req.vt_symbol):
return
holding = self.get_position_holding(req.vt_symbol)
holding.update_order_request(req, vt_orderid)
def get_position_holding(self, vt_symbol: str) -> "PositionHolding":
""""""
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,
net: bool = False
) -> List[OrderRequest]:
""""""
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 net:
return holding.convert_order_request_net(req)
elif req.exchange in [Exchange.SHFE, Exchange.INE]:
return holding.convert_order_request_shfe(req)
else:
return [req]
class PositionHolding:
""""""
def __init__(self, contract: ContractData):
""""""
self.vt_symbol: str = contract.vt_symbol
self.exchange: Exchange = contract.exchange
self.active_orders: Dict[str, OrderData] = {}
self.long_pos: float = 0
self.long_yd: float = 0
self.long_td: float = 0
self.short_pos: float = 0
self.short_yd: float = 0
self.short_td: float = 0
self.long_pos_frozen: float = 0
self.long_yd_frozen: float = 0
self.long_td_frozen: float = 0
self.short_pos_frozen: float = 0
self.short_yd_frozen: float = 0
self.short_td_frozen: float = 0
.......
def convert_order_request_shfe(self, req: OrderRequest) -> List[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 []
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
......
不知道是什么原因,这个类中的holdings字典并不会在有交易时进行更新,所以get_position_holding(req.vt_symbol)得到的是初始值,以上期所合约订单转换为例,在调用convert_order_request_shfe(req)后,pos_available=0,返回空列表[]。为了解决这个问题,我们需要在convert_order_request()中执行holding=self.get_position_holding(req.vt_symbol)之前手动update_position()更新一下holdings字典。我修改后的代码如下:
def convert_order_request(
self,
req: OrderRequest,
lock: bool,
net: bool = False
) -> List[OrderRequest]:
""""""
if not self.is_convert_required(req.vt_symbol):
return [req]
pos_long = self.main_engine.get_position(req.vt_symbol+'.多')
pos_short = self.main_engine.get_position(req.vt_symbol+'.空')
if pos_long:
self.update_position(pos_long)
if pos_short:
self.update_position(pos_short)
holding = self.get_position_holding(req.vt_symbol)
if lock:
return holding.convert_order_request_lock(req)
elif net:
return holding.convert_order_request_net(req)
elif req.exchange in [Exchange.SHFE, Exchange.INE]:
return holding.convert_order_request_shfe(req)
else:
return [req]
这样就可以正确执行convert_order_request_shfe(req)了,我们再回到vnpy_scripttrader\engine.py中来加入一步平今转换,我的做法是在send_order()中初始化一个converter,但似乎直接在engine初始化时就加入一个converter会更好(为了实盘稳定我没有尝试)。修改之后的send_order()如下:
def send_order(
self,
vt_symbol: str,
price: float,
volume: float,
direction: Direction,
offset: Offset,
order_type: OrderType
) -> str:
""""""
contract: Optional[ContractData] = self.get_contract(vt_symbol)
if not contract:
return ""
req: OrderRequest = OrderRequest(
symbol=contract.symbol,
exchange=contract.exchange,
direction=direction,
type=order_type,
volume=volume,
price=price,
offset=offset,
reference=APP_NAME
)
try:
converter=OffsetConverter(self.main_engine)
req=converter.convert_order_request(req,lock=False,net=False)[0]
except:
pass
vt_orderid: str = self.main_engine.send_order(req, contract.gateway_name)
return vt_orderid
这样改之后从我目前的使用情况来看可以稳定解决平今转换的问题,如果有用脚本策略的大神有更好的修改意见欢迎讨论!
最近出现了这样一个问题:集合竞价的订单没有成交,开盘后我想撤掉这个单子重新下,发出撤单指令后等待了1秒用get_order()查询订单状态得到的是Cancelled,但是在快期上看到的实际是已成交。这个就很迷惑,想知道这个撤单流程和查订单状态哪里出了问题。
最近我的实盘策略在开盘发FAK单进行仓位调整时,出现了好几次重复下单的情况,排查之后发现是get_order()查询订单信息显示Status.SUBMITTING且get_trades()没有成交回报,所以没有能更新存储在本地文件中的仓位信息,之后导致重复下单。
我执行的整体逻辑是这样的:
while trading:
if need_order:
orders_list.append(order(...))
sleep(1.5)
get_orders(order_list)
update_positions(...)
orders_list=[]
是否是因为中间延迟比较大,我等待的时间不够长?但模拟盘相同的程序且等待时间更短没有出现这种情况,实盘上交所的品种也是正常的,唯独最近大商所品种频繁出现。然后我又检查了一下订单时间等信息,发现用get_order()获取到的time与快期显示的有很大差异,比如下图中本地记录下来的是21:00:04,快期中则是21:00:10,所以想请问一下这种情况怎么处理呢?
如题,我用的是这个帖子里的脚本https://www.vnpy.com/forum/topic/3046-quan-shi-chang-lu-zhi-xing-qing-shu-ju
然后运行之后只有如图的结果,数据库里并没有更新数据。是因为更新版本之后有一些代码失效了吗?
最近在用脚本模式写策略的时候遇到了一个问题,两个不同的策略做的品种如果有交集,那么通过get_positions()接口获得的是两个策略的总仓位,可能会出现互相干扰的情况。有一种办法是在本地维护一个文件来记录当前各个策略的仓位信息,但其中涉及到报单的成交与否等各种情况,感觉写起来会比较复杂。所以想问各位有没有更好的办法?或者是有没有方法获取持仓明细?
最近交易所提高了好几个品种的平今手续费,所以想试试看锁仓,但是在脚本策略的文档里没看到?