[增强功能] 增加接口获取某一合约当前保证金(包括自定义套利合约)
This commit is contained in:
parent
8c6b926b82
commit
aafedafca3
@ -65,6 +65,7 @@ from vnpy.trader.util_logger import setup_logger
|
|||||||
from vnpy.data.mongo.mongo_data import MongoData
|
from vnpy.data.mongo.mongo_data import MongoData
|
||||||
from uuid import uuid1
|
from uuid import uuid1
|
||||||
|
|
||||||
|
|
||||||
class BackTestingEngine(object):
|
class BackTestingEngine(object):
|
||||||
"""
|
"""
|
||||||
CTA回测引擎
|
CTA回测引擎
|
||||||
@ -208,7 +209,7 @@ class BackTestingEngine(object):
|
|||||||
# 回测任务/回测结果,保存在数据库中
|
# 回测任务/回测结果,保存在数据库中
|
||||||
self.mongo_api = None
|
self.mongo_api = None
|
||||||
self.task_id = None
|
self.task_id = None
|
||||||
self.test_setting = None # 回测设置
|
self.test_setting = None # 回测设置
|
||||||
self.strategy_setting = None # 所有回测策略得设置
|
self.strategy_setting = None # 所有回测策略得设置
|
||||||
|
|
||||||
def create_fund_kline(self, name, use_renko=False):
|
def create_fund_kline(self, name, use_renko=False):
|
||||||
@ -302,6 +303,77 @@ class BackTestingEngine(object):
|
|||||||
def get_margin_rate(self, vt_symbol: str):
|
def get_margin_rate(self, vt_symbol: str):
|
||||||
return self.margin_rate.get(vt_symbol, 0.1)
|
return self.margin_rate.get(vt_symbol, 0.1)
|
||||||
|
|
||||||
|
def get_margin(self, vt_symbol: str):
|
||||||
|
"""
|
||||||
|
按照当前价格,计算1手合约需要得保证金
|
||||||
|
:param vt_symbol:
|
||||||
|
:return: 普通合约/期权 => 当前价格 * size * margin_rate
|
||||||
|
SP j2101&j2105.DCE => max( 当前价格 * size * margin_rate)
|
||||||
|
j2101-1-i2101-3-BJ.SPD => (主动腿价格*主动腿size * 主动腿margin_rate + 被动腿价格*被动腿size * 被动腿margin_rate
|
||||||
|
rb2101-1-rb2105-1-CJ.SPD => max(主动腿价格*主动腿size * 主动腿margin_rate , 被动腿价格*被动腿size * 被动腿margin_rate
|
||||||
|
"""
|
||||||
|
|
||||||
|
if '.SPD99' in vt_symbol:
|
||||||
|
vt_symbol = vt_symbol.replace('.SPD99', '.SPD')
|
||||||
|
|
||||||
|
if not vt_symbol.endswith('.SPD') and '&' not in vt_symbol:
|
||||||
|
cur_price = self.get_price(vt_symbol)
|
||||||
|
cur_size = self.get_size(vt_symbol)
|
||||||
|
cur_margin_rate = self.get_margin_rate(vt_symbol)
|
||||||
|
if cur_price and cur_size and cur_margin_rate:
|
||||||
|
return abs(cur_price * cur_size * cur_margin_rate)
|
||||||
|
else:
|
||||||
|
# 取不到价格,取不到size,或者取不到保证金比例
|
||||||
|
self.write_error(f'无法计算{vt_symbol}的保证金,价格:{cur_price}或size:{cur_size}或margin_rate:{cur_margin_rate}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# j2101-1-i2101-3-BJ.SPD rb2101-1-rb2105-1-CJ.SPD
|
||||||
|
if vt_symbol.endswith('.SPD'):
|
||||||
|
act_symbol, act_ratio, pas_symbol, pas_ratio, spd_type = vt_symbol.replace('.SPD', '').split('-')
|
||||||
|
act_vt_symbol = '{}.{}'.format(act_symbol, self.get_exchange(act_symbol).value)
|
||||||
|
pas_vt_symbol = '{}.{}'.format(pas_symbol, self.get_exchange(pas_symbol).value)
|
||||||
|
act_ratio = int(act_ratio)
|
||||||
|
pas_ratio = int(pas_ratio)
|
||||||
|
# SP j2101&j2105.DCE
|
||||||
|
elif '&' in vt_symbol:
|
||||||
|
symbol, exchange = extract_vt_symbol(vt_symbol)
|
||||||
|
symbol = symbol.split(' ')[-1]
|
||||||
|
act_symbol, pas_symbol = symbol.split('&')
|
||||||
|
act_vt_symbol = f'{act_symbol}.{exchange.value}'
|
||||||
|
pas_vt_symbol = f'{pas_symbol}.{exchange.value}'
|
||||||
|
act_ratio = 1
|
||||||
|
pas_ratio = 1
|
||||||
|
else:
|
||||||
|
self.write_error(f'无法计算{vt_symbol}的保证金:无法分解')
|
||||||
|
return None
|
||||||
|
|
||||||
|
act_cur_price = self.get_price(act_vt_symbol)
|
||||||
|
act_size = self.get_size(act_vt_symbol)
|
||||||
|
act_margin_rate = self.get_margin_rate(act_vt_symbol)
|
||||||
|
pas_cur_price = self.get_price(pas_vt_symbol)
|
||||||
|
pas_size = self.get_size(pas_vt_symbol)
|
||||||
|
pas_margin_rate = self.get_margin_rate(pas_vt_symbol)
|
||||||
|
|
||||||
|
if not all([act_cur_price, act_size, act_margin_rate]):
|
||||||
|
self.write_error(
|
||||||
|
f'无法计算{vt_symbol}的保证金,{act_vt_symbol}价格:{act_cur_price}或size:{act_size}或margin_rate:{act_margin_rate}')
|
||||||
|
return None
|
||||||
|
if not all([pas_cur_price, pas_size, pas_margin_rate]):
|
||||||
|
self.write_error(
|
||||||
|
f'无法计算{vt_symbol}的保证金,{pas_vt_symbol}价格:{pas_cur_price}或size:{pas_size}或margin_rate:{pas_margin_rate}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 跨期合约
|
||||||
|
if get_underlying_symbol(act_symbol) == get_underlying_symbol(pas_symbol):
|
||||||
|
spd_margin = max(act_cur_price * act_size * act_margin_rate * act_ratio,
|
||||||
|
pas_cur_price * pas_size * pas_margin_rate * pas_ratio)
|
||||||
|
|
||||||
|
# 跨品种合约,取最大值
|
||||||
|
else:
|
||||||
|
spd_margin = act_cur_price * act_size * act_margin_rate * act_ratio + pas_cur_price * pas_size * pas_margin_rate * pas_ratio
|
||||||
|
|
||||||
|
return spd_margin
|
||||||
|
|
||||||
def set_slippage(self, vt_symbol: str, slippage: float):
|
def set_slippage(self, vt_symbol: str, slippage: float):
|
||||||
"""设置滑点点数"""
|
"""设置滑点点数"""
|
||||||
self.slippage.update({vt_symbol: slippage})
|
self.slippage.update({vt_symbol: slippage})
|
||||||
@ -599,7 +671,8 @@ class BackTestingEngine(object):
|
|||||||
def new_bar(self, bar):
|
def new_bar(self, bar):
|
||||||
"""新的K线"""
|
"""新的K线"""
|
||||||
self.last_bar.update({bar.vt_symbol: bar})
|
self.last_bar.update({bar.vt_symbol: bar})
|
||||||
if self.last_dt is None or (bar.datetime and bar.datetime > self.last_dt - timedelta(seconds=self.bar_interval_seconds)):
|
if self.last_dt is None or (
|
||||||
|
bar.datetime and bar.datetime > self.last_dt - timedelta(seconds=self.bar_interval_seconds)):
|
||||||
self.last_dt = bar.datetime + timedelta(seconds=self.bar_interval_seconds)
|
self.last_dt = bar.datetime + timedelta(seconds=self.bar_interval_seconds)
|
||||||
self.set_price(bar.vt_symbol, bar.close_price)
|
self.set_price(bar.vt_symbol, bar.close_price)
|
||||||
self.cross_stop_order(bar=bar) # 撮合停止单
|
self.cross_stop_order(bar=bar) # 撮合停止单
|
||||||
@ -717,7 +790,7 @@ class BackTestingEngine(object):
|
|||||||
elif self.contract_type == 'future':
|
elif self.contract_type == 'future':
|
||||||
symbol = vt_symbol
|
symbol = vt_symbol
|
||||||
if self.using_99_contract:
|
if self.using_99_contract:
|
||||||
underly_symbol = get_underlying_symbol(symbol).upper() # WJ: 当需要回测A1701.DCE时,不能替换成99合约。
|
underly_symbol = get_underlying_symbol(symbol).upper() # WJ: 当需要回测A1701.DCE时,不能替换成99合约。
|
||||||
exchange = self.get_exchange(f'{underly_symbol}99')
|
exchange = self.get_exchange(f'{underly_symbol}99')
|
||||||
else:
|
else:
|
||||||
exchange = self.get_exchange(symbol)
|
exchange = self.get_exchange(symbol)
|
||||||
@ -798,7 +871,7 @@ class BackTestingEngine(object):
|
|||||||
"""保存策略数据"""
|
"""保存策略数据"""
|
||||||
for strategy in self.strategies.values():
|
for strategy in self.strategies.values():
|
||||||
self.write_log(u'save strategy data')
|
self.write_log(u'save strategy data')
|
||||||
if hasattr(strategy,'save_data'):
|
if hasattr(strategy, 'save_data'):
|
||||||
strategy.save_data()
|
strategy.save_data()
|
||||||
|
|
||||||
def send_order(self,
|
def send_order(self,
|
||||||
@ -1441,7 +1514,8 @@ class BackTestingEngine(object):
|
|||||||
# raise Exception(u'异常!没有空单持仓,不能cover')
|
# raise Exception(u'异常!没有空单持仓,不能cover')
|
||||||
return
|
return
|
||||||
|
|
||||||
cur_short_pos_list = [s_pos.volume for s_pos in self.short_position_list if s_pos.vt_symbol == trade.vt_symbol]
|
cur_short_pos_list = [s_pos.volume for s_pos in self.short_position_list if
|
||||||
|
s_pos.vt_symbol == trade.vt_symbol]
|
||||||
|
|
||||||
self.write_log(u'{}当前空单:{}'.format(trade.vt_symbol, cur_short_pos_list))
|
self.write_log(u'{}当前空单:{}'.format(trade.vt_symbol, cur_short_pos_list))
|
||||||
|
|
||||||
@ -1454,7 +1528,8 @@ class BackTestingEngine(object):
|
|||||||
self.write_error(f'没有{trade.strategy_name}对应的symbol:{trade.vt_symbol}的空单持仓, 继续')
|
self.write_error(f'没有{trade.strategy_name}对应的symbol:{trade.vt_symbol}的空单持仓, 继续')
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self.write_error(u'异常,{}没有对应symbol:{}的空单持仓, 终止'.format(trade.strategy_name, trade.vt_symbol))
|
self.write_error(
|
||||||
|
u'异常,{}没有对应symbol:{}的空单持仓, 终止'.format(trade.strategy_name, trade.vt_symbol))
|
||||||
# raise Exception(u'realtimeCalculate2() Exception,没有对应symbol:{0}的空单持仓'.format(trade.vt_symbol))
|
# raise Exception(u'realtimeCalculate2() Exception,没有对应symbol:{0}的空单持仓'.format(trade.vt_symbol))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -1587,7 +1662,6 @@ class BackTestingEngine(object):
|
|||||||
|
|
||||||
cover_volume = 0
|
cover_volume = 0
|
||||||
|
|
||||||
|
|
||||||
if g_result is not None:
|
if g_result is not None:
|
||||||
# 更新组合的数据
|
# 更新组合的数据
|
||||||
g_result.turnover = g_result.turnover + result.turnover
|
g_result.turnover = g_result.turnover + result.turnover
|
||||||
@ -1629,7 +1703,8 @@ class BackTestingEngine(object):
|
|||||||
# raise RuntimeError(f'realtimeCalculate2() Exception,没有对应的symbol:{trade.vt_symbol}多单数据,')
|
# raise RuntimeError(f'realtimeCalculate2() Exception,没有对应的symbol:{trade.vt_symbol}多单数据,')
|
||||||
return
|
return
|
||||||
|
|
||||||
cur_long_pos_list = [s_pos.volume for s_pos in self.long_position_list if s_pos.vt_symbol == trade.vt_symbol]
|
cur_long_pos_list = [s_pos.volume for s_pos in self.long_position_list if
|
||||||
|
s_pos.vt_symbol == trade.vt_symbol]
|
||||||
|
|
||||||
self.write_log(u'{}当前多单:{}'.format(trade.vt_symbol, cur_long_pos_list))
|
self.write_log(u'{}当前多单:{}'.format(trade.vt_symbol, cur_long_pos_list))
|
||||||
|
|
||||||
@ -2206,12 +2281,12 @@ class BackTestingEngine(object):
|
|||||||
self.mongo_api = MongoData(host=save_mongo.get('host', 'localhost'), port=save_mongo.get('port', 27017))
|
self.mongo_api = MongoData(host=save_mongo.get('host', 'localhost'), port=save_mongo.get('port', 27017))
|
||||||
|
|
||||||
d = {
|
d = {
|
||||||
'task_id': self.task_id, # 单实例回测任务id
|
'task_id': self.task_id, # 单实例回测任务id
|
||||||
'name': self.test_name, # 回测实例名称, 策略名+参数+时间
|
'name': self.test_name, # 回测实例名称, 策略名+参数+时间
|
||||||
'group_id': self.test_setting.get('group_id', datetime.now().strftime('%y-%m-%d')), # 回测组合id
|
'group_id': self.test_setting.get('group_id', datetime.now().strftime('%y-%m-%d')), # 回测组合id
|
||||||
'status': 'start',
|
'status': 'start',
|
||||||
'task_start_time': datetime.now(), # 任务开始执行时间
|
'task_start_time': datetime.now(), # 任务开始执行时间
|
||||||
'run_host': socket.gethostname(), # 任务运行得host主机
|
'run_host': socket.gethostname(), # 任务运行得host主机
|
||||||
'test_setting': self.test_setting, # 回测参数
|
'test_setting': self.test_setting, # 回测参数
|
||||||
'strategy_setting': self.strategy_setting, # 策略参数
|
'strategy_setting': self.strategy_setting, # 策略参数
|
||||||
}
|
}
|
||||||
@ -2274,11 +2349,11 @@ class BackTestingEngine(object):
|
|||||||
flt=flt)
|
flt=flt)
|
||||||
|
|
||||||
if d:
|
if d:
|
||||||
d.update({'status': 'finish'}) # 更新状态未完成
|
d.update({'status': 'finish'}) # 更新状态未完成
|
||||||
d.update(result_info) # 补充回测结果
|
d.update(result_info) # 补充回测结果
|
||||||
d.update({'task_finish_time': datetime.now()}) # 更新回测完成时间
|
d.update({'task_finish_time': datetime.now()}) # 更新回测完成时间
|
||||||
d.update({'trade_list': binary.Binary(zlib.compress(pickle.dumps(self.trade_pnl_list)))}) # 更新交易记录
|
d.update({'trade_list': binary.Binary(zlib.compress(pickle.dumps(self.trade_pnl_list)))}) # 更新交易记录
|
||||||
d.update({'daily_list': binary.Binary(zlib.compress(pickle.dumps(self.daily_list)))}) # 更新每日净值记录
|
d.update({'daily_list': binary.Binary(zlib.compress(pickle.dumps(self.daily_list)))}) # 更新每日净值记录
|
||||||
|
|
||||||
self.write_log(u'更新回测结果至数据库')
|
self.write_log(u'更新回测结果至数据库')
|
||||||
|
|
||||||
|
@ -850,6 +850,78 @@ class CtaEngine(BaseEngine):
|
|||||||
|
|
||||||
return contract.min_volume
|
return contract.min_volume
|
||||||
|
|
||||||
|
def get_margin(self, vt_symbol: str):
|
||||||
|
"""
|
||||||
|
按照当前价格,计算1手合约需要得保证金
|
||||||
|
:param vt_symbol:
|
||||||
|
:return: 普通合约/期权 => 当前价格 * size * margin_rate
|
||||||
|
SP j2101&j2105.DCE => max( 当前价格 * size * margin_rate)
|
||||||
|
j2101-1-i2101-3-BJ.SPD => (主动腿价格*主动腿size * 主动腿margin_rate + 被动腿价格*被动腿size * 被动腿margin_rate
|
||||||
|
rb2101-1-rb2105-1-CJ.SPD => max(主动腿价格*主动腿size * 主动腿margin_rate , 被动腿价格*被动腿size * 被动腿margin_rate
|
||||||
|
"""
|
||||||
|
if not vt_symbol.endswith('.SPD') and '&' not in vt_symbol:
|
||||||
|
cur_price = self.get_price(vt_symbol)
|
||||||
|
cur_size = self.get_size(vt_symbol)
|
||||||
|
cur_margin_rate = self.get_margin_rate(vt_symbol)
|
||||||
|
if cur_price and cur_size and cur_margin_rate:
|
||||||
|
return abs(cur_price * cur_size * cur_margin_rate)
|
||||||
|
else:
|
||||||
|
# 取不到价格,取不到size,或者取不到保证金比例
|
||||||
|
self.write_error(f'无法计算{vt_symbol}的保证金,价格:{cur_price}或size:{cur_size}或margin_rate:{cur_margin_rate}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# j2101-1-i2101-3-BJ.SPD rb2101-1-rb2105-1-CJ.SPD
|
||||||
|
if vt_symbol.endswith('.SPD'):
|
||||||
|
contract_conf = self.get_custom_contract(vt_symbol)
|
||||||
|
if contract_conf is None:
|
||||||
|
self.write_error(f'无法计算{vt_symbol}保证金,获取不到自定义合约配置')
|
||||||
|
return None
|
||||||
|
act_symbol = contract_conf.get('leg1_symbol')
|
||||||
|
pas_symbol = contract_conf.get('leg2_symbol')
|
||||||
|
act_vt_symbol = '{}.{}'.format(act_symbol, contract_conf.get('leg1_exchange'))
|
||||||
|
pas_vt_symbol = '{}.{}'.format(pas_symbol, contract_conf.get('leg2_exchange'))
|
||||||
|
act_ratio = int(contract_conf.get('leg1_ratio'))
|
||||||
|
pas_ratio = int(contract_conf.get('leg2_ratio'))
|
||||||
|
# SP j2101&j2105.DCE
|
||||||
|
elif '&' in vt_symbol:
|
||||||
|
symbol, exchange = extract_vt_symbol(vt_symbol)
|
||||||
|
symbol = symbol.split(' ')[-1]
|
||||||
|
act_symbol, pas_symbol = symbol.split('&')
|
||||||
|
act_vt_symbol = f'{act_symbol}.{exchange.value}'
|
||||||
|
pas_vt_symbol = f'{pas_symbol}.{exchange.value}'
|
||||||
|
act_ratio = 1
|
||||||
|
pas_ratio = 1
|
||||||
|
else:
|
||||||
|
self.write_error(f'无法计算{vt_symbol}的保证金:无法分解')
|
||||||
|
return None
|
||||||
|
|
||||||
|
act_cur_price = self.get_price(act_vt_symbol)
|
||||||
|
act_size = self.get_size(act_vt_symbol)
|
||||||
|
act_margin_rate = self.get_margin_rate(act_vt_symbol)
|
||||||
|
pas_cur_price = self.get_price(pas_vt_symbol)
|
||||||
|
pas_size = self.get_size(pas_vt_symbol)
|
||||||
|
pas_margin_rate = self.get_margin_rate(pas_vt_symbol)
|
||||||
|
|
||||||
|
if not all([act_cur_price, act_size, act_margin_rate]):
|
||||||
|
self.write_error(
|
||||||
|
f'无法计算{vt_symbol}的保证金,{act_vt_symbol}价格:{act_cur_price}或size:{act_size}或margin_rate:{act_margin_rate}')
|
||||||
|
return None
|
||||||
|
if not all([pas_cur_price, pas_size, pas_margin_rate]):
|
||||||
|
self.write_error(
|
||||||
|
f'无法计算{vt_symbol}的保证金,{pas_vt_symbol}价格:{pas_cur_price}或size:{pas_size}或margin_rate:{pas_margin_rate}')
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 跨期合约
|
||||||
|
if get_underlying_symbol(act_symbol) == get_underlying_symbol(pas_symbol):
|
||||||
|
spd_margin = max(act_cur_price * act_size * act_margin_rate * act_ratio,
|
||||||
|
pas_cur_price * pas_size * pas_margin_rate * pas_ratio)
|
||||||
|
|
||||||
|
# 跨品种合约,取最大值
|
||||||
|
else:
|
||||||
|
spd_margin = act_cur_price * act_size * act_margin_rate * act_ratio + pas_cur_price * pas_size * pas_margin_rate * pas_ratio
|
||||||
|
|
||||||
|
return spd_margin
|
||||||
|
|
||||||
def get_tick(self, vt_symbol: str):
|
def get_tick(self, vt_symbol: str):
|
||||||
"""获取合约得最新tick"""
|
"""获取合约得最新tick"""
|
||||||
return self.main_engine.get_tick(vt_symbol)
|
return self.main_engine.get_tick(vt_symbol)
|
||||||
@ -873,6 +945,24 @@ class CtaEngine(BaseEngine):
|
|||||||
return self.main_engine.get_contract(vt_symbol)
|
return self.main_engine.get_contract(vt_symbol)
|
||||||
|
|
||||||
def get_custom_contract(self, vt_symbol):
|
def get_custom_contract(self, vt_symbol):
|
||||||
|
"""
|
||||||
|
获取自定义合约的设置
|
||||||
|
:param symbol: "pb2012-1-pb2101-1-CJ"
|
||||||
|
:return: {
|
||||||
|
"name": "pb跨期价差",
|
||||||
|
"exchange": "SPD",
|
||||||
|
"leg1_symbol": "pb2012",
|
||||||
|
"leg1_exchange": "SHFE",
|
||||||
|
"leg1_ratio": 1,
|
||||||
|
"leg2_symbol": "pb2101",
|
||||||
|
"leg2_exchange": "SHFE",
|
||||||
|
"leg2_ratio": 1,
|
||||||
|
"is_spread": true,
|
||||||
|
"size": 1,
|
||||||
|
"margin_rate": 0.1,
|
||||||
|
"price_tick": 5
|
||||||
|
}
|
||||||
|
"""
|
||||||
return self.main_engine.get_custom_contract(vt_symbol.split('.')[0])
|
return self.main_engine.get_custom_contract(vt_symbol.split('.')[0])
|
||||||
|
|
||||||
def get_all_contracts(self):
|
def get_all_contracts(self):
|
||||||
|
@ -288,6 +288,9 @@ class SpreadTestingEngine(BackTestingEngine):
|
|||||||
|
|
||||||
self.write_log(u'开始套利组合回测')
|
self.write_log(u'开始套利组合回测')
|
||||||
|
|
||||||
|
# 保存回测脚本到数据库
|
||||||
|
self.save_setting_to_mongo()
|
||||||
|
|
||||||
for strategy_name, strategy_setting in strategy_settings.items():
|
for strategy_name, strategy_setting in strategy_settings.items():
|
||||||
# 策略得启动日期
|
# 策略得启动日期
|
||||||
if 'start_date' in strategy_setting:
|
if 'start_date' in strategy_setting:
|
||||||
|
@ -771,8 +771,8 @@ class CtaProTemplate(CtaTemplate):
|
|||||||
|
|
||||||
short_symbol = sg.snapshot.get('mi_symbol', self.vt_symbol)
|
short_symbol = sg.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
pos_symbols.add(short_symbol)
|
pos_symbols.add(short_symbol)
|
||||||
self.write_log(u'加载持仓空单[{},价格:{}],[指数:{},价格:{}],数量:{}手'
|
self.write_log(u'加载持仓空单[ID:{},vt_symbol:{},价格:{}],[指数:{},价格:{}],数量:{}手'
|
||||||
.format(short_symbol, sg.snapshot.get('open_price'),
|
.format(sg.id, short_symbol, sg.snapshot.get('open_price'),
|
||||||
self.idx_symbol, sg.open_price, sg.volume))
|
self.idx_symbol, sg.open_price, sg.volume))
|
||||||
self.position.short_pos -= sg.volume
|
self.position.short_pos -= sg.volume
|
||||||
|
|
||||||
@ -797,8 +797,8 @@ class CtaProTemplate(CtaTemplate):
|
|||||||
long_symbol = lg.snapshot.get('mi_symbol', self.vt_symbol)
|
long_symbol = lg.snapshot.get('mi_symbol', self.vt_symbol)
|
||||||
pos_symbols.add(long_symbol)
|
pos_symbols.add(long_symbol)
|
||||||
|
|
||||||
self.write_log(u'加载持仓多单[{},价格:{}],[指数{},价格:{}],数量:{}手'
|
self.write_log(u'加载持仓多单[ID:{},vt_symbol:{},价格:{}],[指数{},价格:{}],数量:{}手'
|
||||||
.format(lg.snapshot.get('miSymbol'), lg.snapshot.get('open_price'),
|
.format(lg.id, long_symbol, lg.snapshot.get('open_price'),
|
||||||
self.idx_symbol, lg.open_price, lg.volume))
|
self.idx_symbol, lg.open_price, lg.volume))
|
||||||
self.position.long_pos += lg.volume
|
self.position.long_pos += lg.volume
|
||||||
|
|
||||||
@ -899,24 +899,29 @@ class CtaProTemplate(CtaTemplate):
|
|||||||
|
|
||||||
none_mi_grid = None
|
none_mi_grid = None
|
||||||
none_mi_symbol = None
|
none_mi_symbol = None
|
||||||
|
self.write_log(f'持仓换月=>启动.')
|
||||||
# 找出非主力合约的持仓网格
|
# 找出非主力合约的持仓网格
|
||||||
for g in self.gt.get_opened_grids(direction=Direction.LONG):
|
for g in self.gt.get_opened_grids(direction=Direction.LONG):
|
||||||
none_mi_symbol = g.snapshot.get('mi_symbol')
|
none_mi_symbol = g.snapshot.get('mi_symbol', g.vt_symbol)
|
||||||
|
# 如果持仓的合约,跟策略配置的vt_symbol一致,则不处理
|
||||||
if none_mi_symbol is None or none_mi_symbol == self.vt_symbol:
|
if none_mi_symbol is None or none_mi_symbol == self.vt_symbol:
|
||||||
# 如果持仓的合约,跟策略配置的vt_symbol一致,则不处理
|
self.write_log(f'none_mi_symbol:{none_mi_symbol}, vt_symbol:{self.vt_symbol} 一致,不处理')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# 如果未开仓,或者处于委托状态,或者已交易完毕,不处理
|
||||||
if not g.open_status or g.order_status or g.volume - g.traded_volume <= 0:
|
if not g.open_status or g.order_status or g.volume - g.traded_volume <= 0:
|
||||||
|
self.write_log(f'开仓状态:{g.open_status}, 委托状态:{g.order_status},网格持仓:{g.volume} ,已交易数量:{g.traded_volume}, 不处理')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
none_mi_grid = g
|
none_mi_grid = g
|
||||||
if g.traded_volume > 0 and g.volume - g.traded_volume > 0:
|
if g.traded_volume > 0 and g.volume - g.traded_volume > 0:
|
||||||
|
|
||||||
g.volume -= g.traded_volume
|
g.volume -= g.traded_volume
|
||||||
g.traded_volume = 0
|
g.traded_volume = 0
|
||||||
break
|
break
|
||||||
if none_mi_grid is None:
|
if none_mi_grid is None:
|
||||||
return
|
return
|
||||||
|
self.write_log(f'持仓换月=>找到多单持仓:{none_mi_symbol},持仓数量:{none_mi_grid.volume}')
|
||||||
# 找到行情中非主力合约/主力合约的最新价
|
# 找到行情中非主力合约/主力合约的最新价
|
||||||
none_mi_tick = self.tick_dict.get(none_mi_symbol)
|
none_mi_tick = self.tick_dict.get(none_mi_symbol)
|
||||||
mi_tick = self.tick_dict.get(self.vt_symbol, None)
|
mi_tick = self.tick_dict.get(self.vt_symbol, None)
|
||||||
@ -925,24 +930,28 @@ class CtaProTemplate(CtaTemplate):
|
|||||||
|
|
||||||
# 如果涨停价,不做卖出
|
# 如果涨停价,不做卖出
|
||||||
if self.is_upper_limit(none_mi_symbol) or self.is_upper_limit(self.vt_symbol):
|
if self.is_upper_limit(none_mi_symbol) or self.is_upper_limit(self.vt_symbol):
|
||||||
|
self.write_log(f'{none_mi_symbol} 或 {self.vt_symbol} 为涨停价,不做换仓')
|
||||||
return
|
return
|
||||||
none_mi_price = max(none_mi_tick.last_price, none_mi_tick.bid_price_1)
|
none_mi_price = max(none_mi_tick.last_price, none_mi_tick.bid_price_1)
|
||||||
|
|
||||||
grid = deepcopy(none_mi_grid)
|
grid = deepcopy(none_mi_grid)
|
||||||
grid.id = str(uuid.uuid1())
|
grid.id = str(uuid.uuid1())
|
||||||
|
grid.open_status = False
|
||||||
|
self.write_log(f'持仓换月=>复制持仓信息{none_mi_symbol},ID:{none_mi_grid.id} => {self.vt_symbol},ID:{grid.id}')
|
||||||
|
|
||||||
# 委托卖出非主力合约
|
# 委托卖出非主力合约
|
||||||
|
|
||||||
vt_orderids = self.sell(price=none_mi_price,
|
vt_orderids = self.sell(price=none_mi_price,
|
||||||
volume=none_mi_grid.volume,
|
volume=none_mi_grid.volume,
|
||||||
vt_symbol=none_mi_symbol,
|
vt_symbol=none_mi_symbol,
|
||||||
order_type=self.order_type,
|
order_type=self.order_type,
|
||||||
grid=none_mi_grid)
|
grid=none_mi_grid)
|
||||||
if len(vt_orderids) > 0:
|
if len(vt_orderids) > 0:
|
||||||
self.write_log(f'切换合约,委托卖出非主力合约{none_mi_symbol}持仓:{none_mi_grid.volume}')
|
self.write_log(f'持仓换月=>委托卖出非主力合约{none_mi_symbol}持仓:{none_mi_grid.volume}')
|
||||||
|
|
||||||
# 已经发生过换月的,不执行买入新合约
|
# 已经发生过换月的,不执行买入新合约
|
||||||
if none_mi_grid.snapshot.get("switched", False):
|
if none_mi_grid.snapshot.get("switched", False):
|
||||||
self.write_log(f'已经执行过换月,不再创建新的买入操作')
|
self.write_log(f'持仓换月=>已经执行过换月,不再创建新的买入操作')
|
||||||
return
|
return
|
||||||
|
|
||||||
none_mi_grid.snapshot.update({'switched': True})
|
none_mi_grid.snapshot.update({'switched': True})
|
||||||
@ -956,13 +965,13 @@ class CtaProTemplate(CtaTemplate):
|
|||||||
order_type=self.order_type,
|
order_type=self.order_type,
|
||||||
grid=grid)
|
grid=grid)
|
||||||
if len(vt_orderids) > 0:
|
if len(vt_orderids) > 0:
|
||||||
self.write_log(u'切换合约,委托买入主力合约:{},价格:{},数量:{}'
|
self.write_log(u'持仓换月=>委托买入主力合约:{},价格:{},数量:{}'
|
||||||
.format(self.vt_symbol, self.cur_mi_price, grid.volume))
|
.format(self.vt_symbol, self.cur_mi_price, grid.volume))
|
||||||
else:
|
else:
|
||||||
self.write_error(f'委托买入主力合约:{self.vt_symbol}失败')
|
self.write_error(f'持仓换月=>委托买入主力合约:{self.vt_symbol}失败')
|
||||||
self.gt.save()
|
self.gt.save()
|
||||||
else:
|
else:
|
||||||
self.write_error(f'委托卖出非主力合约:{none_mi_symbol}失败')
|
self.write_error(f'持仓换月=>委托卖出非主力合约:{none_mi_symbol}失败')
|
||||||
|
|
||||||
def tns_switch_short_pos(self):
|
def tns_switch_short_pos(self):
|
||||||
"""切换合约,从持仓的非主力合约,切换至主力合约"""
|
"""切换合约,从持仓的非主力合约,切换至主力合约"""
|
||||||
|
@ -1070,6 +1070,8 @@ class CtpTdApi(TdApi):
|
|||||||
future_contract.update({'margin_rate': mi_margin_rate})
|
future_contract.update({'margin_rate': mi_margin_rate})
|
||||||
future_contract.update({'symbol_size': idx_contract.size})
|
future_contract.update({'symbol_size': idx_contract.size})
|
||||||
future_contract.update({'price_tick': idx_contract.pricetick})
|
future_contract.update({'price_tick': idx_contract.pricetick})
|
||||||
|
if 'exchange' not in future_contract:
|
||||||
|
future_contract.update({'exchange': contract.exchange.value})
|
||||||
future_contracts.update({underlying_symbol: future_contract})
|
future_contracts.update({underlying_symbol: future_contract})
|
||||||
self.future_contract_changed = True
|
self.future_contract_changed = True
|
||||||
index_contracts.update({underlying_symbol: idx_contract})
|
index_contracts.update({underlying_symbol: idx_contract})
|
||||||
@ -2090,4 +2092,3 @@ class TqMdApi():
|
|||||||
self.update_thread.join()
|
self.update_thread.join()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.gateway.write_log('退出天勤行情api异常:{}'.format(str(e)))
|
self.gateway.write_log('退出天勤行情api异常:{}'.format(str(e)))
|
||||||
|
|
||||||
|
@ -452,14 +452,13 @@ class OmsEngine(BaseEngine):
|
|||||||
import bz2
|
import bz2
|
||||||
import pickle
|
import pickle
|
||||||
contract_file_name = 'vn_contract.pkb2'
|
contract_file_name = 'vn_contract.pkb2'
|
||||||
if not os.path.exists(contract_file_name):
|
if os.path.exists(contract_file_name):
|
||||||
return
|
try:
|
||||||
try:
|
with bz2.BZ2File(contract_file_name, 'rb') as f:
|
||||||
with bz2.BZ2File(contract_file_name, 'rb') as f:
|
self.contracts = pickle.load(f)
|
||||||
self.contracts = pickle.load(f)
|
self.write_log(f'加载缓存合约字典:{contract_file_name}')
|
||||||
self.write_log(f'加载缓存合约字典:{contract_file_name}')
|
except Exception as ex:
|
||||||
except Exception as ex:
|
self.write_log(f'加载缓存合约异常:{str(ex)}')
|
||||||
self.write_log(f'加载缓存合约异常:{str(ex)}')
|
|
||||||
|
|
||||||
# 更新自定义合约
|
# 更新自定义合约
|
||||||
custom_contracts = self.get_all_custom_contracts()
|
custom_contracts = self.get_all_custom_contracts()
|
||||||
|
Loading…
Reference in New Issue
Block a user