[增强] 账号/策略仓位比对,市价单委托

This commit is contained in:
msincenselee 2020-04-04 00:49:21 +08:00
parent 91823b8c71
commit a01bb7e735
4 changed files with 90 additions and 108 deletions

View File

@ -670,24 +670,17 @@ class CtaEngine(BaseEngine):
price=price,
volume=volume,
gateway_name=gateway_name)
if order_type == OrderType.FAK:
return self.send_fak_order(
return self.send_server_order(
strategy=strategy,
contract=contract,
direction=direction,
offset=offset,
price=price,
volume=volume,
gateway_name=gateway_name)
else:
return self.send_limit_order(
strategy=strategy,
contract=contract,
direction=direction,
offset=offset,
price=price,
volume=volume,
gateway_name=gateway_name)
type=order_type,
gateway_name=gateway_name
)
def cancel_order(self, strategy: CtaTemplate, vt_orderid: str):
"""
@ -919,6 +912,7 @@ class CtaEngine(BaseEngine):
self.write_log(msg=msg,
strategy_name=strategy.strategy_name,
level=logging.CRITICAL)
self.send_wechat(msg)
def add_strategy(
self, class_name: str,
@ -1490,8 +1484,7 @@ class CtaEngine(BaseEngine):
compare_pos[vt_symbol] = OrderedDict(
{
"账号空单": abs(position.volume) if position.volume < 0 else 0,
'账号多单': position.volume if position.volume > 0 else 0,
"账号净仓": position.volume,
'策略空单': 0,
'策略多单': 0,
'空单策略': [],
@ -1511,8 +1504,7 @@ class CtaEngine(BaseEngine):
self.write_log(u'账号持仓信息获取不到{},创建一个'.format(vt_symbol))
symbol_pos = OrderedDict(
{
"账号空单": 0,
'账号多单': 0,
"账号净仓": 0,
'策略空单': 0,
'策略多单': 0,
'空单策略': [],
@ -1521,12 +1513,12 @@ class CtaEngine(BaseEngine):
)
if pos.get('direction') == 'short':
symbol_pos.update({'策略空单': symbol_pos.get('策略空单', 0) + abs(pos.get('volume', 0))})
symbol_pos.update({'策略空单': round(symbol_pos.get('策略空单', 0) + abs(pos.get('volume', 0)), 7)})
symbol_pos['空单策略'].append(
u'{}({})'.format(strategy_pos['strategy_name'], abs(pos.get('volume', 0))))
self.write_log(u'更新{}策略持空仓=>{}'.format(vt_symbol, symbol_pos.get('策略空单', 0)))
if pos.get('direction') == 'long':
symbol_pos.update({'策略多单': symbol_pos.get('策略多单', 0) + abs(pos.get('volume', 0))})
symbol_pos.update({'策略多单': round(symbol_pos.get('策略多单', 0) + abs(pos.get('volume', 0)),7)})
symbol_pos['多单策略'].append(
u'{}({})'.format(strategy_pos['strategy_name'], abs(pos.get('volume', 0))))
self.write_log(u'更新{}策略持多仓=>{}'.format(vt_symbol, symbol_pos.get('策略多单', 0)))
@ -1537,47 +1529,21 @@ class CtaEngine(BaseEngine):
for vt_symbol in sorted(vt_symbols):
# 发送不一致得结果
symbol_pos = compare_pos.pop(vt_symbol)
d_long = {
'account_id': self.engine_config.get('account_id', '-'),
'vt_symbol': vt_symbol,
'direction': Direction.LONG.value,
'strategy_list': symbol_pos.get('多单策略', [])}
d_short = {
'account_id': self.engine_config.get('account_id', '-'),
'vt_symbol': vt_symbol,
'direction': Direction.SHORT.value,
'strategy_list': symbol_pos.get('空单策略', [])}
net_symbol_pos = round(round(symbol_pos['策略多单'], 7) - round(symbol_pos['策略空单'], 7),7)
# 多空都一致
if round(symbol_pos['账号空单'], 7) == round(symbol_pos['策略空单'], 7) and \
round(symbol_pos['账号多单'], 7) == round(symbol_pos['策略多单'], 7):
if round(symbol_pos['账号净仓'], 7) == net_symbol_pos:
msg = u'{}多空都一致.{}\n'.format(vt_symbol, json.dumps(symbol_pos, indent=2, ensure_ascii=False))
self.write_log(msg)
compare_info += msg
else:
pos_compare_result += '\n{}: '.format(vt_symbol)
# 多单不一致
if round(symbol_pos['策略多单'], 7) != round(symbol_pos['账号多单'], 7):
msg = '{}多单[账号({}), 策略{},共({})], ' \
.format(vt_symbol,
symbol_pos['账号多单'],
symbol_pos['多单策略'],
symbol_pos['策略多单'])
msg = f"{vt_symbol} [{symbol_pos}]"
pos_compare_result += msg
self.write_error(u'{}不一致:{}'.format(vt_symbol, msg))
compare_info += u'{}不一致:{}\n'.format(vt_symbol, msg)
# 空单不一致
if round(symbol_pos['策略空单'], 7) != round(symbol_pos['账号空单'], 7):
msg = '{}空单[账号({}), 策略{},共({})], ' \
.format(vt_symbol,
symbol_pos['账号空单'],
symbol_pos['空单策略'],
symbol_pos['策略空单'])
pos_compare_result += msg
self.write_error(u'{}不一致:{}'.format(vt_symbol, msg))
compare_info += u'{}不一致:{}\n'.format(vt_symbol, msg)
self.write_error(u'{}不一致:{}'.format(vt_symbol, json.dumps(symbol_pos, indent=2, ensure_ascii=False)))
compare_info += u'{}不一致:{}\n'.format(vt_symbol, json.dumps(symbol_pos, indent=2, ensure_ascii=False))
# 不匹配输入到stdErr通道
if pos_compare_result != '':

View File

@ -427,6 +427,7 @@ class CtaFutureTemplate(CtaTemplate):
# 委托类型
order_type = OrderType.LIMIT
cancel_seconds = 120 # 撤单时间(秒)
activate_market = False
# 资金相关
max_invest_rate = 0.1 # 最大仓位(0~1)
@ -454,6 +455,7 @@ class CtaFutureTemplate(CtaTemplate):
self.account_pos = None # 当前账号vt_symbol持仓信息
self.last_minute = None # 最后的分钟,用于on_tick内每分钟处理的逻辑
self.display_bars = True
super().__init__(
cta_engine, strategy_name, vt_symbol, setting
@ -481,6 +483,20 @@ class CtaFutureTemplate(CtaTemplate):
self.margin_rate = self.cta_engine.get_margin_rate(self.vt_symbol)
self.volumn_tick = self.cta_engine.get_volume_tick(self.vt_symbol)
if self.activate_market:
self.write_log(f'{self.strategy_name}使用市价单委托方式')
self.order_type = OrderType.MARKET
def sync_data(self):
"""同步更新数据"""
if not self.backtesting:
self.write_log(u'保存k线缓存数据')
self.save_klines_to_cache()
if self.inited and self.trading:
self.write_log(u'保存policy数据')
self.policy.save()
def save_klines_to_cache(self, kline_names: list = []):
"""
保存K线数据到缓存
@ -578,6 +594,7 @@ class CtaFutureTemplate(CtaTemplate):
:return:
"""
self.write_log(u'init_position(),初始化持仓')
changed = False
if len(self.gt.up_grids) <= 0:
self.position.short_pos = 0
# 加载已开仓的空单数据网格JSON
@ -592,12 +609,14 @@ class CtaFutureTemplate(CtaTemplate):
if len(sg.order_ids) > 0 or sg.order_status:
self.write_log(f'重置委托状态:{sg.order_status},清除委托单:{sg.order_ids}')
sg.order_status = False
[self.cancel_order(vt_orderid) for vt_orderid in sg.order_ids]
sg.order_ids = []
changed = True
self.write_log(u'加载持仓空单[{},价格:{},数量:{}手,开仓时间:{}'
.format(self.vt_symbol, sg.open_price,
sg.volume, sg.open_time))
self.position.short_pos -= sg.volume
self.position.short_pos = round(self.position.short_pos - sg.volume, 7)
self.write_log(u'持久化空单,共持仓:{}'.format(abs(self.position.short_pos)))
@ -615,15 +634,17 @@ class CtaFutureTemplate(CtaTemplate):
if len(lg.order_ids) > 0 or lg.order_status:
self.write_log(f'重置委托状态:{lg.order_status},清除委托单:{lg.order_ids}')
lg.order_status = False
[self.cancel_order(vt_orderid) for vt_orderid in lg.order_ids]
lg.order_ids = []
changed = True
self.write_log(u'加载持仓多单[{},价格:{},数量:{}手, 开仓时间:{}'
.format(self.vt_symbol, lg.open_price, lg.volume, lg.open_time))
self.position.long_pos += lg.volume
self.position.long_pos = round(self.position.long_pos + lg.volume, 7)
self.write_log(f'持久化多单,共持仓:{self.position.long_pos}')
self.position.pos = self.position.long_pos + self.position.short_pos
self.position.pos = round(self.position.long_pos + self.position.short_pos, 7)
self.write_log(u'{}加载持久化数据完成,多单:{},空单:{},共:{}'
.format(self.strategy_name,
@ -631,6 +652,7 @@ class CtaFutureTemplate(CtaTemplate):
abs(self.position.short_pos),
self.position.pos))
self.pos = self.position.pos
if changed:
self.gt.save()
self.display_grids()
@ -791,23 +813,17 @@ class CtaFutureTemplate(CtaTemplate):
self.gt.save()
# 在策略得活动订单中,移除
self.write_log(f'委托单{order.vt_orderid}完成,从活动订单中移除')
self.active_orders.pop(order.vt_orderid, None)
def on_order_open_canceled(self, order: OrderData):
"""
委托开仓单撤销
如果是FAK模式重新修改价格再提交
FAK用于实盘需要增加涨跌停判断
:param order:
:return:
"""
self.write_log(u'委托开仓单撤销:{}'.format(order.__dict__))
if not self.trading:
if not self.backtesting:
self.write_error(u'当前不允许交易')
return
if order.vt_orderid not in self.active_orders:
self.write_error(u'{}不在未完成的委托单中{}'.format(order.vt_orderid, self.active_orders))
return
@ -816,28 +832,19 @@ class CtaFutureTemplate(CtaTemplate):
old_order = self.active_orders[order.vt_orderid]
self.write_log(u'{} 委托信息:{}'.format(order.vt_orderid, old_order))
old_order['traded'] = order.traded
order_vt_symbol = copy(old_order['vt_symbol'])
order_volume = round(old_order['volume'] - old_order['traded'], 7)
if order_volume <= 0:
msg = u'{} {}{}需重新开仓数量为{},不再开仓' \
.format(self.strategy_name,
order.vt_orderid,
order_vt_symbol,
order_volume)
self.write_error(msg)
self.write_log(u'移除:{}'.format(order.vt_orderid))
self.active_orders.pop(order.vt_orderid, None)
return
grid = old_order.get('grid', None)
pre_status = old_order.get('status', Status.NOTTRADED)
old_order.update({'status': Status.CANCELLED})
self.write_log(u'委托单状态:{}=>{}'.format(pre_status, old_order.get('status')))
if grid:
if order.vt_orderid in grid.order_ids:
grid.order_ids.remove(order.vt_orderid)
if order.traded > 0:
pre_traded_volume = grid.traded_volume
grid.traded_volume = round(grid.traded_volume + order.traded,7)
self.write_log(f'撤单中部分成交:{order.traded} + 原已成交:{pre_traded_volume} => {grid.traded_volume}')
if not grid.order_ids:
grid.order_status = False
@ -854,25 +861,10 @@ class CtaFutureTemplate(CtaTemplate):
self.write_error(u'{}不在未完成的委托单中:{}'.format(order.vt_orderid, self.active_orders))
return
if not self.trading:
self.write_error(u'当前不允许交易')
return
# 直接更新“未完成委托单”更新volume,Retry次数
old_order = self.active_orders[order.vt_orderid]
self.write_log(u'{} 订单信息:{}'.format(order.vt_orderid, old_order))
old_order['traded'] = order.traded
# order_time = old_order['order_time']
order_symbol = copy(old_order['vt_symbol'])
order_volume = old_order['volume'] - old_order['traded']
if order_volume <= 0:
msg = u'{} {}{}重新平仓数量为{},不再平仓' \
.format(self.strategy_name, order.vt_orderid, order_symbol, order_volume)
self.write_error(msg)
self.send_wechat(msg)
self.write_log(u'活动订单移除:{}'.format(order.vt_orderid))
self.active_orders.pop(order.vt_orderid, None)
return
grid = old_order.get('grid', None)
pre_status = old_order.get('status', Status.NOTTRADED)
@ -881,6 +873,10 @@ class CtaFutureTemplate(CtaTemplate):
if grid:
if order.vt_orderid in grid.order_ids:
grid.order_ids.remove(order.vt_orderid)
if order.traded > 0:
pre_traded_volume = grid.traded_volume
grid.traded_volume = round(grid.traded_volume + order.traded, 7)
self.write_log(f'撤单中部分成交:{order.traded} + 原已成交:{pre_traded_volume} => {grid.traded_volume}')
if len(grid.order_ids) == 0:
grid.order_status = False
self.gt.save()
@ -942,6 +938,7 @@ class CtaFutureTemplate(CtaTemplate):
vt_orderids = self.buy(vt_symbol=self.vt_symbol,
price=self.cur_price,
volume=grid.volume,
order_type=self.order_type,
order_time=self.cur_datetime,
grid=grid)
if len(vt_orderids) > 0:
@ -964,6 +961,7 @@ class CtaFutureTemplate(CtaTemplate):
vt_orderids = self.short(vt_symbol=self.vt_symbol,
price=self.cur_price,
volume=grid.volume,
order_type=self.order_type,
order_time=self.cur_datetime,
grid=grid)
if len(vt_orderids) > 0:
@ -986,7 +984,7 @@ class CtaFutureTemplate(CtaTemplate):
:return:
"""
self.write_log(u'执行事务平多仓位:{}'.format(grid.to_json()))
"""
self.account_pos = self.cta_engine.get_position(
vt_symbol=self.vt_symbol,
direction=Direction.NET)
@ -994,7 +992,7 @@ class CtaFutureTemplate(CtaTemplate):
if self.account_pos is None:
self.write_error(u'无法获取{}得持仓信息'.format(self.vt_symbol))
return False
"""
# 发出委托卖出单
if self.backtesting:
sell_price = self.cur_price - self.price_tick
@ -1007,6 +1005,7 @@ class CtaFutureTemplate(CtaTemplate):
grid.volume = round(grid.volume, 7)
grid.traded_volume = 0
"""
if self.account_pos.volume <= 0:
self.write_error(u'当前{}的净持仓:{},不能平多单'
.format(self.vt_symbol,
@ -1019,11 +1018,12 @@ class CtaFutureTemplate(CtaTemplate):
grid.volume))
grid.volume = self.account_pos.volume
"""
vt_orderids = self.sell(
vt_symbol=self.vt_symbol,
price=sell_price,
volume=grid.volume,
order_type=self.order_type,
order_time=self.cur_datetime,
grid=grid)
if len(vt_orderids) == 0:
@ -1045,14 +1045,14 @@ class CtaFutureTemplate(CtaTemplate):
:return:
"""
self.write_log(u'执行事务平空仓位:{}'.format(grid.to_json()))
"""
self.account_pos = self.cta_engine.get_position(
vt_symbol=self.vt_symbol,
direction=Direction.NET)
if self.account_pos is None:
self.write_error(u'无法获取{}得持仓信息'.format(self.vt_symbol))
return False
"""
# 发出委托单
if self.backtesting:
cover_price = self.cur_price + self.price_tick
@ -1065,6 +1065,7 @@ class CtaFutureTemplate(CtaTemplate):
grid.volume = round(grid.volume, 7)
grid.traded_volume = 0
"""
if self.account_pos.volume >= 0:
self.write_error(u'当前{}的净持仓:{},不能平空单'
.format(self.vt_symbol,
@ -1077,13 +1078,15 @@ class CtaFutureTemplate(CtaTemplate):
grid.volume))
grid.volume = abs(self.account_pos.volume)
"""
vt_orderids = self.cover(
price=cover_price,
vt_symbol=self.vt_symbol,
volume=grid.volume,
order_type=self.order_type,
order_time=self.cur_datetime,
grid=grid)
if len(vt_orderids) == 0:
if self.backtesting:
self.write_error(u'空单平仓委托失败')
@ -1115,9 +1118,6 @@ class CtaFutureTemplate(CtaTemplate):
order_vt_symbol = order_info.get('vt_symbol', self.vt_symbol)
order_time = order_info['order_time']
order_volume = order_info['volume'] - order_info['traded']
# order_price = order_info['price']
# order_direction = order_info['direction']
# order_offset = order_info['offset']
order_grid = order_info['grid']
order_status = order_info.get('status', Status.NOTTRADED)
order_type = order_info.get('order_type', OrderType.LIMIT)
@ -1240,7 +1240,7 @@ class CtaFutureTemplate(CtaTemplate):
# 删除撤单的订单
for vt_orderid in canceled_ids:
self.write_log(u'删除orderID:{0}'.format(vt_orderid))
self.write_log(f'活动订单撤单成功,移除{vt_orderid}')
self.active_orders.pop(vt_orderid, None)
if len(self.active_orders) == 0:
@ -1250,7 +1250,9 @@ class CtaFutureTemplate(CtaTemplate):
"""更新网格显示信息"""
if not self.inited:
return
self.account_pos = self.cta_engine.get_position(vt_symbol=self.vt_symbol, direction=Direction.NET)
if self.account_pos:
self.write_log(f'账号{self.vt_symbol}持仓:{self.account_pos.volume}, 冻结:{self.account_pos.frozen}, 盈亏:{self.account_pos.pnl}')
up_grids_info = self.gt.to_str(direction=Direction.SHORT)
if len(self.gt.up_grids) > 0:
self.write_log(up_grids_info)

View File

@ -634,6 +634,17 @@ class CtaProTemplate(CtaTemplate):
self.symbol_size = self.cta_engine.get_size(self.vt_symbol)
self.margin_rate = self.cta_engine.get_margin_rate(self.vt_symbol)
def sync_data(self):
"""同步更新数据"""
if not self.backtesting:
self.write_log(u'保存k线缓存数据')
self.save_klines_to_cache()
if self.inited and self.trading:
self.write_log(u'保存policy数据')
self.policy.save()
def save_klines_to_cache(self, kline_names: list = []):
"""
保存K线数据到缓存

View File

@ -413,6 +413,9 @@ class BinancefRestApi(RestClient):
"newClientOrderId": orderid,
"newOrderRespType": "ACK"
}
if req.type == OrderType.MARKET:
params.pop('timeInForce', None)
params.pop('price', None)
self.add_request(
method="POST",