[增强] 期货CtaPro模板, 组合回测引擎(支持tick级别组合回测)

This commit is contained in:
msincenselee 2020-02-24 16:48:17 +08:00
parent c5f71910d5
commit ee2fd126f4
30 changed files with 3863 additions and 2199 deletions

View File

@ -37,8 +37,11 @@
- 提供单独重启某一策略实例功能,可在线更新策略源码后,重启某一策略实例,不影响其他运行实例。 - 提供单独重启某一策略实例功能,可在线更新策略源码后,重启某一策略实例,不影响其他运行实例。
- 支持单策略多合约行情订阅,支持指数合约行情订阅 - 支持单策略多合约行情订阅,支持指数合约行情订阅
- 提供组合回测引擎能够直接加载cta_strategy_pro_setting.json文件进行组合回测。 - 提供组合回测引擎能够直接加载cta_strategy_pro_setting.json文件进行组合回测。
- 拆分组合回测引擎和回测引擎组合回测引擎支持bar/tick级别的组合回测
- 增加定时器推动策略on_timer - 增加定时器推动策略on_timer
- 增加定时推送策略持仓event - 增加定时推送策略持仓event
- 增加CtaPro模板支持精细化策略持久模板
- 增加CtaPro期货模板支持FAK委托自动换月等
8、增强主引擎包括 8、增强主引擎包括

View File

@ -5,8 +5,6 @@ qdarkstyle
requests requests
websocket-client websocket-client
peewee peewee
pymysql
psycopg2
mongoengine mongoengine
numpy numpy
pandas>=0.24.2 pandas>=0.24.2
@ -21,3 +19,5 @@ deap
pyzmq pyzmq
wmi wmi
QScintilla QScintilla
pytdx
pykalman

View File

@ -12,7 +12,7 @@ from vnpy.data.renko.rebuild_future import *
# Mongo数据库得地址renko数据库名tick文件缓存目录 # Mongo数据库得地址renko数据库名tick文件缓存目录
setting = { setting = {
"host": "192.168.0.207", "host": "127.0.0.1",
"db_name": FUTURE_RENKO_DB_NAME, "db_name": FUTURE_RENKO_DB_NAME,
"cache_folder": os.path.join(vnpy_root, 'tick_data', 'tdx', 'future') "cache_folder": os.path.join(vnpy_root, 'tick_data', 'tdx', 'future')
} }
@ -20,8 +20,12 @@ builder = FutureRenkoRebuilder(setting)
# 生成单个 # 生成单个
# builder.start(symbol='RB99',min_diff=1, height=10, start_date='2019-04-01', end_date='2019-09-10') # builder.start(symbol='RB99',min_diff=1, height=10, start_date='2019-04-01', end_date='2019-09-10')
# 生成多个 # 生成多个
builder.start(symbol='J99', price_tick=0.5, height=[10], start_date='2016-01-01', end_date='2016-02-16') #builder.start(symbol='J99', price_tick=0.5, height=[10, 'K3'], start_date='2016-01-01', end_date='2016-02-16')
# 在数据库最新renko基础上开始追加数据
builder.start(symbol='J99', price_tick=0.5, height=[10, 'K3', 'K5'], start_date='2016-01-01', refill=True)
# 导出csv # 导出csv
# builder.export(symbol='RB99',height=10, start_date='2019-04-01', end_date='2019-09-10') # builder.export(symbol='RB99',height=10, start_date='2019-04-01', end_date='2019-09-10')

View File

@ -14,21 +14,31 @@
''' '''
import json
import os
import sys import sys
import copy import copy
import traceback import traceback
import csv
from datetime import datetime, timedelta from datetime import datetime, timedelta
from queue import Queue from queue import Queue
from threading import Thread from threading import Thread
from time import time from time import time
from concurrent.futures import ThreadPoolExecutor
from vnpy.event import Event, EventEngine from vnpy.event import Event, EventEngine
from vnpy.trader.event import * from vnpy.trader.event import (
from vnpy.trader.constant import Direction EVENT_TIMER,
EVENT_ACCOUNT,
EVENT_ORDER,
EVENT_TRADE,
EVENT_POSITION,
EVENT_HISTORY_TRADE,
EVENT_HISTORY_ORDER,
EVENT_FUNDS_FLOW,
EVENT_STRATEGY_POS,
EVENT_ERROR,
EVENT_WARNING,
EVENT_CRITICAL,
)
# from vnpy.trader.constant import Direction
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.utility import get_trading_date, load_json, save_json from vnpy.trader.utility import get_trading_date, load_json, save_json
from vnpy.data.mongo.mongo_data import MongoData from vnpy.data.mongo.mongo_data import MongoData
@ -246,7 +256,7 @@ class AccountRecorder(BaseEngine):
self.save_setting() self.save_setting()
except Exception as ex: except Exception as ex:
self.main_engine.writeError(u'更新数据日期异常:{}'.format(str(ex))) self.main_engine.write_error(u'更新数据日期异常:{}'.format(str(ex)))
self.write_log(traceback.format_exc()) self.write_log(traceback.format_exc())
def get_begin_day(self, gw_name: str, data_type: str): def get_begin_day(self, gw_name: str, data_type: str):
@ -393,7 +403,7 @@ class AccountRecorder(BaseEngine):
price = self.main_engine.get_price(pos.vt_symbol) price = self.main_engine.get_price(pos.vt_symbol)
if price: if price:
data.update({'cur_price': price}) data.update({'cur_price': price})
except: except: # noqa
pass pass
self.update_data(db_name=ACCOUNT_DB_NAME, col_name=TODAY_POSITION_COL, fld=fld, data=data) self.update_data(db_name=ACCOUNT_DB_NAME, col_name=TODAY_POSITION_COL, fld=fld, data=data)
@ -561,7 +571,7 @@ class AccountRecorder(BaseEngine):
self.write_log(u'运行 {}.{} 更新 耗时:{}ms >200ms,数据:{}' self.write_log(u'运行 {}.{} 更新 耗时:{}ms >200ms,数据:{}'
.format(db_name, col_name, execute_ms, d)) .format(db_name, col_name, execute_ms, d))
except Exception as ex: except Exception as ex: # noqa
pass pass
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------

View File

@ -4,7 +4,6 @@ import traceback
from datetime import datetime from datetime import datetime
from threading import Thread from threading import Thread
from pathlib import Path from pathlib import Path
from inspect import getfile
from vnpy.event import Event, EventEngine from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine

View File

@ -1,17 +1,18 @@
from pathlib import Path from pathlib import Path
from vnpy.trader.app import BaseApp from vnpy.trader.app import BaseApp
from vnpy.trader.constant import Direction,Offset,Status from vnpy.trader.constant import Direction,Offset,Status,Color
from vnpy.trader.object import TickData, BarData, TradeData, OrderData from vnpy.trader.object import TickData, BarData, TradeData, OrderData
from vnpy.trader.utility import BarGenerator, ArrayManager from vnpy.trader.utility import BarGenerator, ArrayManager
from .cta_position import CtaPosition from .cta_position import CtaPosition
from .cta_line_bar import CtaLineBar, CtaMinuteBar, CtaHourBar, CtaDayBar, CtaWeekBar from .cta_line_bar import CtaLineBar, CtaMinuteBar, CtaHourBar, CtaDayBar, CtaWeekBar
from .base import APP_NAME, StopOrder, CtaComponent
from .cta_policy import CtaPolicy from .cta_policy import CtaPolicy
from .cta_grid_trade import CtaGrid, CtaGridTrade from .cta_grid_trade import CtaGrid, CtaGridTrade
from .base import APP_NAME, StopOrder
from .engine import CtaEngine from .engine import CtaEngine
from .template import CtaTemplate, CtaSignal, TargetPosTemplate, CtaProTemplate from .template import CtaTemplate, CtaSignal, TargetPosTemplate, CtaProTemplate, CtaProFutureTemplate
class CtaStrategyProApp(BaseApp): class CtaStrategyProApp(BaseApp):
"""""" """"""

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
""" """
Defines constants and objects used in CtaStrategyPro App. Defines constants and objects used in CtaStrategyPro App.
""" """
import sys
from abc import ABC from abc import ABC
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum from enum import Enum
@ -93,6 +94,7 @@ INTERVAL_DELTA_MAP = {
Interval.DAILY: timedelta(days=1), Interval.DAILY: timedelta(days=1),
} }
class CtaComponent(ABC): class CtaComponent(ABC):
""" CTA策略基础组件""" """ CTA策略基础组件"""
@ -103,7 +105,6 @@ class CtaComponent(ABC):
""" """
self.strategy = strategy self.strategy = strategy
# ----------------------------------------------------------------------
def write_log(self, content: str): def write_log(self, content: str):
"""记录日志""" """记录日志"""
if self.strategy: if self.strategy:
@ -111,11 +112,9 @@ class CtaComponent(ABC):
else: else:
print(content) print(content)
# ----------------------------------------------------------------------
def write_error(self, content: str, level: int = ERROR): def write_error(self, content: str, level: int = ERROR):
"""记录错误日志""" """记录错误日志"""
if self.strategy: if self.strategy:
self.strategy.write_log(msg=content, level=level) self.strategy.write_log(msg=content, level=level)
else: else:
print(content, file=sys.stderr) print(content, file=sys.stderr)

View File

@ -9,8 +9,6 @@ import traceback
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
from dataclasses import dataclass, field
from typing import List
from vnpy.trader.utility import get_folder_path from vnpy.trader.utility import get_folder_path
from vnpy.app.cta_strategy_pro.base import Direction, CtaComponent from vnpy.app.cta_strategy_pro.base import Direction, CtaComponent

View File

@ -12,6 +12,7 @@ import sys
import traceback import traceback
import talib as ta import talib as ta
import numpy as np import numpy as np
import csv
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -27,7 +28,7 @@ from vnpy.app.cta_strategy_pro.base import (
MARKET_ZJ) MARKET_ZJ)
from vnpy.app.cta_strategy_pro.cta_period import CtaPeriod, Period from vnpy.app.cta_strategy_pro.cta_period import CtaPeriod, Period
from vnpy.trader.object import BarData, TickData from vnpy.trader.object import BarData, TickData
from vnpy.trader.constant import Interval, Color, Exchange from vnpy.trader.constant import Interval, Color
from vnpy.trader.utility import round_to, get_trading_date, get_underlying_symbol from vnpy.trader.utility import round_to, get_trading_date, get_underlying_symbol
@ -1062,7 +1063,6 @@ class CtaLineBar(object):
self.line_bar.append(self.cur_bar) # 推入到lineBar队列 self.line_bar.append(self.cur_bar) # 推入到lineBar队列
# ----------------------------------------------------------------------
def generate_bar(self, tick: TickData): def generate_bar(self, tick: TickData):
"""生成 line Bar """ """生成 line Bar """
@ -1184,7 +1184,6 @@ class CtaLineBar(object):
if not endtick: if not endtick:
self.last_tick = tick self.last_tick = tick
# ----------------------------------------------------------------------
def __count_pre_high_low(self): def __count_pre_high_low(self):
"""计算 K线的前周期最高和最低""" """计算 K线的前周期最高和最低"""
@ -1206,7 +1205,6 @@ class CtaLineBar(object):
del self.line_pre_low[0] del self.line_pre_low[0]
self.line_pre_low.append(preLow) self.line_pre_low.append(preLow)
# ----------------------------------------------------------------------
def __count_sar(self): def __count_sar(self):
"""计算K线的SAR""" """计算K线的SAR"""
@ -1331,7 +1329,6 @@ class CtaLineBar(object):
if len(self.line_sar) > self.max_hold_bars: if len(self.line_sar) > self.max_hold_bars:
del self.line_sar[0] del self.line_sar[0]
# ----------------------------------------------------------------------
def __count_ma(self): def __count_ma(self):
"""计算K线的MA1 和MA2""" """计算K线的MA1 和MA2"""
@ -1486,7 +1483,8 @@ class CtaLineBar(object):
if self.para_ma1_len > 0: if self.para_ma1_len > 0:
count_len = min(self.bar_len, self.para_ma1_len) count_len = min(self.bar_len, self.para_ma1_len)
if count_len > 0: if count_len > 0:
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len) close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]),
count_len)
self._rt_ma1 = round(float(close_ma_array[-1]), self.round_n) self._rt_ma1 = round(float(close_ma_array[-1]), self.round_n)
# 计算斜率 # 计算斜率
@ -1497,7 +1495,8 @@ class CtaLineBar(object):
if self.para_ma2_len > 0: if self.para_ma2_len > 0:
count_len = min(self.bar_len, self.para_ma2_len) count_len = min(self.bar_len, self.para_ma2_len)
if count_len > 0: if count_len > 0:
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len) close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]),
count_len)
self._rt_ma2 = round(float(close_ma_array[-1]), self.round_n) self._rt_ma2 = round(float(close_ma_array[-1]), self.round_n)
# 计算斜率 # 计算斜率
@ -1508,7 +1507,8 @@ class CtaLineBar(object):
if self.para_ma3_len > 0: if self.para_ma3_len > 0:
count_len = min(self.bar_len, self.para_ma3_len) count_len = min(self.bar_len, self.para_ma3_len)
if count_len > 0: if count_len > 0:
close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]), count_len) close_ma_array = ta.MA(np.append(self.close_array[-count_len:], [self.line_bar[-1].close_price]),
count_len)
self._rt_ma3 = round(float(close_ma_array[-1]), self.round_n) self._rt_ma3 = round(float(close_ma_array[-1]), self.round_n)
# 计算斜率 # 计算斜率
@ -1558,7 +1558,6 @@ class CtaLineBar(object):
return self.line_ma3_atan[-1] return self.line_ma3_atan[-1]
return self._rt_ma3_atan return self._rt_ma3_atan
# ----------------------------------------------------------------------
def __count_ema(self): def __count_ema(self):
"""计算K线的EMA1 和EMA2""" """计算K线的EMA1 和EMA2"""
@ -1613,7 +1612,6 @@ class CtaLineBar(object):
del self.line_ema3[0] del self.line_ema3[0]
self.line_ema3.append(barEma3) self.line_ema3.append(barEma3)
# ----------------------------------------------------------------------
def rt_count_ema(self): def rt_count_ema(self):
"""计算K线的EMA1 和EMA2""" """计算K线的EMA1 和EMA2"""
@ -1827,7 +1825,7 @@ class CtaLineBar(object):
if self.para_atr1_len > 0: if self.para_atr1_len > 0:
count_len = min(self.bar_len, self.para_atr1_len) count_len = min(self.bar_len, self.para_atr1_len)
cur_atr1 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:], cur_atr1 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:],
self.close_array[-count_len * 2:], count_len) self.close_array[-count_len * 2:], count_len)
self.cur_atr1 = round(cur_atr1[-1], self.round_n) self.cur_atr1 = round(cur_atr1[-1], self.round_n)
if len(self.line_atr1) > self.max_hold_bars: if len(self.line_atr1) > self.max_hold_bars:
del self.line_atr1[0] del self.line_atr1[0]
@ -1836,7 +1834,7 @@ class CtaLineBar(object):
if self.para_atr2_len > 0: if self.para_atr2_len > 0:
count_len = min(self.bar_len, self.para_atr2_len) count_len = min(self.bar_len, self.para_atr2_len)
cur_atr2 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:], cur_atr2 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:],
self.close_array[-count_len * 2:], count_len) self.close_array[-count_len * 2:], count_len)
self.cur_atr2 = round(cur_atr2[-1], self.round_n) self.cur_atr2 = round(cur_atr2[-1], self.round_n)
if len(self.line_atr2) > self.max_hold_bars: if len(self.line_atr2) > self.max_hold_bars:
del self.line_atr2[0] del self.line_atr2[0]
@ -1844,8 +1842,8 @@ class CtaLineBar(object):
if self.para_atr3_len > 0: if self.para_atr3_len > 0:
count_len = min(self.bar_len, self.para_atr3_len) count_len = min(self.bar_len, self.para_atr3_len)
cur_atr3 = ta.ATR(self.high_array[-count_len * 2 :], self.low_array[-count_len * 2:], cur_atr3 = ta.ATR(self.high_array[-count_len * 2:], self.low_array[-count_len * 2:],
self.close_array[-count_len * 2:], count_len) self.close_array[-count_len * 2:], count_len)
self.cur_atr3 = round(cur_atr3[-1], self.round_n) self.cur_atr3 = round(cur_atr3[-1], self.round_n)
if len(self.line_atr3) > self.max_hold_bars: if len(self.line_atr3) > self.max_hold_bars:
@ -1853,7 +1851,6 @@ class CtaLineBar(object):
self.line_atr3.append(self.cur_atr3) self.line_atr3.append(self.cur_atr3)
# ----------------------------------------------------------------------
def __count_vol_ma(self): def __count_vol_ma(self):
"""计算平均成交量""" """计算平均成交量"""
@ -1869,7 +1866,6 @@ class CtaLineBar(object):
del self.line_vol_ma[0] del self.line_vol_ma[0]
self.line_vol_ma.append(avgVol) self.line_vol_ma.append(avgVol)
# ----------------------------------------------------------------------
def __count_rsi(self): def __count_rsi(self):
"""计算K线的RSI""" """计算K线的RSI"""
if self.para_rsi1_len <= 0 and self.para_rsi2_len <= 0: if self.para_rsi1_len <= 0 and self.para_rsi2_len <= 0:
@ -3943,7 +3939,6 @@ class CtaLineBar(object):
return self.line_bias3[-1] return self.line_bias3[-1]
return self._rt_bias3 return self._rt_bias3
# ----------------------------------------------------------------------
def write_log(self, content): def write_log(self, content):
"""记录CTA日志""" """记录CTA日志"""
self.strategy.write_log(u'[' + self.name + u']' + content) self.strategy.write_log(u'[' + self.name + u']' + content)
@ -4586,7 +4581,6 @@ class CtaMinuteBar(CtaLineBar):
# 实时计算 # 实时计算
self.rt_executed = False self.rt_executed = False
# ----------------------------------------------------------------------
def generate_bar(self, tick): def generate_bar(self, tick):
""" """
生成 line Bar 生成 line Bar
@ -4821,8 +4815,6 @@ class CtaHourBar(CtaLineBar):
# 实时计算 # 实时计算
self.rt_executed = False self.rt_executed = False
# ----------------------------------------------------------------------
def generate_bar(self, tick): def generate_bar(self, tick):
""" """
生成 line Bar 生成 line Bar
@ -5047,7 +5039,6 @@ class CtaDayBar(CtaLineBar):
# 实时计算 # 实时计算
self.rt_executed = False self.rt_executed = False
# ----------------------------------------------------------------------
def generate_bar(self, tick): def generate_bar(self, tick):
""" """
生成 line Bar 生成 line Bar
@ -5280,7 +5271,6 @@ class CtaWeekBar(CtaLineBar):
'%Y-%m-%d %H:%M:%S') '%Y-%m-%d %H:%M:%S')
return friday_night_dt return friday_night_dt
# ----------------------------------------------------------------------
def generate_bar(self, tick): def generate_bar(self, tick):
""" """
生成 line Bar 生成 line Bar
@ -5338,4 +5328,3 @@ class CtaWeekBar(CtaLineBar):
self.rt_executed = False self.rt_executed = False
self.last_tick = tick self.last_tick = tick

View File

@ -7,6 +7,11 @@ from collections import OrderedDict
from vnpy.app.cta_strategy_pro.base import CtaComponent from vnpy.app.cta_strategy_pro.base import CtaComponent
from vnpy.trader.utility import get_folder_path from vnpy.trader.utility import get_folder_path
TNS_STATUS_OBSERVATE = 'observate'
TNS_STATUS_ORDERING = 'ordering'
TNS_STATUS_OPENED = 'opened'
TNS_STATUS_CLOSED = 'closed'
class CtaPolicy(CtaComponent): class CtaPolicy(CtaComponent):
""" """
@ -18,7 +23,7 @@ class CtaPolicy(CtaComponent):
构造 构造
:param strategy: :param strategy:
""" """
super(CtaPolicy,self).__init__(strategy=strategy, kwargs=kwargs) super().__init__(strategy=strategy, kwargs=kwargs)
self.create_time = None self.create_time = None
self.save_time = None self.save_time = None
@ -67,7 +72,8 @@ class CtaPolicy(CtaComponent):
从持久化文件中获取 从持久化文件中获取
:return: :return:
""" """
json_file = os.path.abspath(os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.strategy_name))) json_file = os.path.abspath(
os.path.join(get_folder_path('data'), u'{}_Policy.json'.format(self.strategy.strategy_name)))
json_data = {} json_data = {}
if os.path.exists(json_file): if os.path.exists(json_file):

View File

@ -15,16 +15,16 @@ class CtaPosition(CtaComponent):
def __init__(self, strategy, **kwargs): def __init__(self, strategy, **kwargs):
super(CtaPosition, self).__init__(strategy=strategy, kwargs=kwargs) super(CtaPosition, self).__init__(strategy=strategy, kwargs=kwargs)
self.long_pos = 0 # 多仓持仓(正数) self.long_pos = 0 # 多仓持仓(正数)
self.short_pos = 0 # 空仓持仓(负数) self.short_pos = 0 # 空仓持仓(负数)
self.pos = 0 # 持仓状态 0:空仓/对空平等; >=1 净多仓 <=-1 净空仓 self.pos = 0 # 持仓状态 0:空仓/对空平等; >=1 净多仓 <=-1 净空仓
self.maxPos = sys.maxsize # 最大持仓量(多仓+空仓总量) self.maxPos = sys.maxsize # 最大持仓量(多仓+空仓总量)
def open_pos(self, direction: Direction, volume: float): def open_pos(self, direction: Direction, volume: float):
"""开、加仓""" """开、加仓"""
# volume: 正整数 # volume: 正整数
if direction == Direction.LONG: # 加多仓 if direction == Direction.LONG: # 加多仓
if (max(self.pos, self.long_pos) + volume) > self.maxPos: if (max(self.pos, self.long_pos) + volume) > self.maxPos:
self.write_error(content=f'开仓异常,净:{self.pos},多:{self.long_pos},加多:{volume},超过:{self.maxPos}') self.write_error(content=f'开仓异常,净:{self.pos},多:{self.long_pos},加多:{volume},超过:{self.maxPos}')
@ -34,7 +34,7 @@ class CtaPosition(CtaComponent):
self.long_pos += volume self.long_pos += volume
self.pos += volume self.pos += volume
if direction == Direction.SHORT: # 加空仓 if direction == Direction.SHORT: # 加空仓
if (min(self.pos, self.short_pos) - volume) < (0 - self.maxPos): if (min(self.pos, self.short_pos) - volume) < (0 - self.maxPos):
self.write_error(content=f'开仓异常,净:{self.pos},空:{self.short_pos},加空:{volume},超过:{self.maxPos}') self.write_error(content=f'开仓异常,净:{self.pos},空:{self.short_pos},加空:{volume},超过:{self.maxPos}')
@ -45,11 +45,11 @@ class CtaPosition(CtaComponent):
return True return True
def close_pos(self, direction: Direction, volume:float): def close_pos(self, direction: Direction, volume: float):
"""平、减仓""" """平、减仓"""
# vol: 正整数 # vol: 正整数
if direction == Direction.LONG: # 平空仓 Cover if direction == Direction.LONG: # 平空仓 Cover
if self.short_pos + volume > 0: if self.short_pos + volume > 0:
self.write_error(u'平仓异常,超出仓位。净:{0},空:{1},平仓:{2}'.format(self.pos, self.short_pos, volume)) self.write_error(u'平仓异常,超出仓位。净:{0},空:{1},平仓:{2}'.format(self.pos, self.short_pos, volume))
@ -61,7 +61,7 @@ class CtaPosition(CtaComponent):
# 更新上层策略的pos。该方法不推荐使用 # 更新上层策略的pos。该方法不推荐使用
self.strategy.pos = self.pos self.strategy.pos = self.pos
if direction == Direction.SHORT: # 平多仓 if direction == Direction.SHORT: # 平多仓
if self.long_pos - volume < 0: if self.long_pos - volume < 0:
self.write_error(u'平仓异常,超出仓位。净:{0},多:{1},平仓:{2}'.format(self.pos, self.long_pos, volume)) self.write_error(u'平仓异常,超出仓位。净:{0},多:{1},平仓:{2}'.format(self.pos, self.long_pos, volume))

View File

@ -1,14 +1,13 @@
"""""" """"""
import importlib import importlib
import csv
import os import os
import sys import sys
import traceback import traceback
from collections import defaultdict from collections import defaultdict
from pathlib import Path from pathlib import Path
from typing import Any, Callable from typing import Any, Callable
from datetime import datetime, timedelta from datetime import datetime
from collections import OrderedDict from collections import OrderedDict
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from copy import copy from copy import copy
@ -19,10 +18,8 @@ from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.object import ( from vnpy.trader.object import (
OrderRequest, OrderRequest,
SubscribeRequest, SubscribeRequest,
HistoryRequest,
LogData, LogData,
TickData, TickData,
BarData,
ContractData ContractData
) )
from vnpy.trader.event import ( from vnpy.trader.event import (
@ -37,8 +34,6 @@ from vnpy.trader.event import (
from vnpy.trader.constant import ( from vnpy.trader.constant import (
Direction, Direction,
OrderType, OrderType,
Interval,
Exchange,
Offset, Offset,
Status Status
) )
@ -359,12 +354,8 @@ class CtaEngine(BaseEngine):
if stop_order.vt_symbol != tick.vt_symbol: if stop_order.vt_symbol != tick.vt_symbol:
continue continue
long_triggered = ( long_triggered = stop_order.direction == Direction.LONG and tick.last_price >= stop_order.price
stop_order.direction == Direction.LONG and tick.last_price >= stop_order.price short_triggered = stop_order.direction == Direction.SHORT and tick.last_price <= stop_order.price
)
short_triggered = (
stop_order.direction == Direction.SHORT and tick.last_price <= stop_order.price
)
if long_triggered or short_triggered: if long_triggered or short_triggered:
strategy = self.strategies[stop_order.strategy_name] strategy = self.strategies[stop_order.strategy_name]
@ -1340,7 +1331,7 @@ class CtaEngine(BaseEngine):
pos_list.append(pos) pos_list.append(pos)
except Exception as ex: except Exception as ex:
self.write_error(u'分解SPD失败') self.write_error(f'分解SPD失败:{str(ex)}')
# update local pos dict # update local pos dict
self.strategy_pos_dict.update({name: pos_list}) self.strategy_pos_dict.update({name: pos_list})
@ -1439,16 +1430,21 @@ class CtaEngine(BaseEngine):
Update setting file. Update setting file.
""" """
strategy = self.strategies[strategy_name] strategy = self.strategies[strategy_name]
# 原配置
strategy_config = self.strategy_setting.get('strategy_name', {}) old_config = self.strategy_setting.get('strategy_name', {})
new_config = {
self.strategy_setting[strategy_name] = {
"class_name": strategy.__class__.__name__, "class_name": strategy.__class__.__name__,
"vt_symbol": strategy.vt_symbol, "vt_symbol": strategy.vt_symbol,
"auto_init": auto_init, "auto_init": auto_init,
"auto_start": auto_start, "auto_start": auto_start,
"setting": setting "setting": setting
} }
if old_config:
self.write_log(f'{strategy_name} 配置变更:\n{old_config} \n=> \n{new_config}')
self.strategy_setting[strategy_name] = new_config
save_json(self.setting_filename, self.strategy_setting) save_json(self.setting_filename, self.strategy_setting)
def remove_strategy_setting(self, strategy_name: str): def remove_strategy_setting(self, strategy_name: str):

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@ from vnpy.app.cta_strategy_pro import (
CtaTemplate, CtaTemplate,
StopOrder, StopOrder,
Direction, Direction,
Offset,
TickData, TickData,
BarData, BarData,
TradeData, TradeData,
@ -112,7 +111,7 @@ class TurtleSignalStrategy(CtaTemplate):
self.send_short_orders(self.entry_down) self.send_short_orders(self.entry_down)
cover_price = min(self.short_stop, self.exit_up) cover_price = min(self.short_stop, self.exit_up)
ret = self.cover(cover_price, abs(self.pos), True) self.cover(cover_price, abs(self.pos), True)
self.put_event() self.put_event()
@ -149,7 +148,7 @@ class TurtleSignalStrategy(CtaTemplate):
if self.pos >= 4: if self.pos >= 4:
return return
if self.cur_mi_price <= price - self.atr_value/2: if self.cur_mi_price <= price - self.atr_value / 2:
return return
t = self.pos / self.fixed_size t = self.pos / self.fixed_size

View File

@ -2,7 +2,6 @@ from vnpy.app.cta_strategy_pro import (
CtaTemplate, CtaTemplate,
StopOrder, StopOrder,
Direction, Direction,
Offset,
TickData, TickData,
BarData, BarData,
TradeData, TradeData,
@ -10,7 +9,6 @@ from vnpy.app.cta_strategy_pro import (
BarGenerator, BarGenerator,
ArrayManager, ArrayManager,
) )
from vnpy.trader.utility import round_to from vnpy.trader.utility import round_to
@ -189,7 +187,7 @@ class TurtleSignalStrategy_v2(CtaTemplate):
self.write_log(f'买入委托编号:{refs}') self.write_log(f'买入委托编号:{refs}')
if t == 1 and self.cur_mi_price > price: if t == 1 and self.cur_mi_price > price:
buy_price = round_to(price + self.atr_value * 0.5 , self.symbol_price_tick) buy_price = round_to(price + self.atr_value * 0.5, self.symbol_price_tick)
self.write_log(u'发出做多停止单,触发价格为: {}'.format(buy_price)) self.write_log(u'发出做多停止单,触发价格为: {}'.format(buy_price))
refs = self.buy(buy_price, self.invest_pos, True) refs = self.buy(buy_price, self.invest_pos, True)
if len(refs) > 0: if len(refs) > 0:

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
"""""" """"""
import logging
from copy import copy from copy import copy
from collections import defaultdict from collections import defaultdict
from datetime import datetime from datetime import datetime
@ -182,20 +182,24 @@ class RiskManagerEngine(BaseEngine):
if vt_accountid: if vt_accountid:
account = self.account_dict.get(vt_accountid, None) account = self.account_dict.get(vt_accountid, None)
if account: if account:
return account.balance, \ return (
account.available, \ account.balance,
round(account.frozen * 100 / (account.balance + 0.01), 2), \ account.available,
self.percent_limit round(account.frozen * 100 / (account.balance + 0.01), 2),
self.percent_limit
)
if len(self.account_dict.values()) > 0: if len(self.account_dict.values()) > 0:
account = list(self.account_dict.values())[0] account = list(self.account_dict.values())[0]
return account.balance, \ return (
account.available, \ account.balance,
round(account.frozen * 100 / (account.balance + 0.01), 2), \ account.available,
self.percent_limit round(account.frozen * 100 / (account.balance + 0.01), 2),
self.percent_limit
)
else: else:
return 0, 0, 0, 0 return 0, 0, 0, 0
def write_log(self, msg: str): def write_log(self, msg: str, source: str = "", level: int = logging.DEBUG):
"""""" """"""
log = LogData(msg=msg, gateway_name="RiskManager") log = LogData(msg=msg, gateway_name="RiskManager")
event = Event(type=EVENT_LOG, data=log) event = Event(type=EVENT_LOG, data=log)

View File

@ -1,2 +1,2 @@
from .widget import ChartWidget from .widget import ChartWidget, KlineWidget
from .item import CandleItem, VolumeItem from .item import CandleItem, VolumeItem

View File

@ -1,5 +1,5 @@
from typing import List, Dict, Type from typing import List, Dict, Type
from collections import deque
import pyqtgraph as pg import pyqtgraph as pg
from vnpy.trader.ui import QtGui, QtWidgets, QtCore from vnpy.trader.ui import QtGui, QtWidgets, QtCore
@ -11,8 +11,7 @@ from .base import (
to_int, NORMAL_FONT to_int, NORMAL_FONT
) )
from .axis import DatetimeAxis from .axis import DatetimeAxis
from .item import ChartItem from .item import ChartItem, CandleItem, VolumeItem
pg.setConfigOptions(antialias=True) pg.setConfigOptions(antialias=True)
@ -21,10 +20,10 @@ class ChartWidget(pg.PlotWidget):
"""""" """"""
MIN_BAR_COUNT = 100 MIN_BAR_COUNT = 100
def __init__(self, parent: QtWidgets.QWidget = None): def __init__(self, parent: QtWidgets.QWidget = None, title: str = "ChartWidget of vn.py"):
"""""" """"""
super().__init__(parent) super().__init__(parent)
self.title = title
self._manager: BarManager = BarManager() self._manager: BarManager = BarManager()
self._plots: Dict[str, pg.PlotItem] = {} self._plots: Dict[str, pg.PlotItem] = {}
@ -34,14 +33,14 @@ class ChartWidget(pg.PlotWidget):
self._first_plot: pg.PlotItem = None self._first_plot: pg.PlotItem = None
self._cursor: ChartCursor = None self._cursor: ChartCursor = None
self._right_ix: int = 0 # Index of most right data self._right_ix: int = 0 # Index of most right data
self._bar_count: int = self.MIN_BAR_COUNT # Total bar visible in chart self._bar_count: int = self.MIN_BAR_COUNT # Total bar visible in chart
self._init_ui() self._init_ui()
def _init_ui(self) -> None: def _init_ui(self) -> None:
"""""" """"""
self.setWindowTitle("ChartWidget of vn.py") self.setWindowTitle(self.title)
self._layout = pg.GraphicsLayout() self._layout = pg.GraphicsLayout()
self._layout.setContentsMargins(10, 10, 10, 10) self._layout.setContentsMargins(10, 10, 10, 10)
@ -59,11 +58,11 @@ class ChartWidget(pg.PlotWidget):
self, self._manager, self._plots, self._item_plot_map) self, self._manager, self._plots, self._item_plot_map)
def add_plot( def add_plot(
self, self,
plot_name: str, plot_name: str,
minimum_height: int = 80, minimum_height: int = 80,
maximum_height: int = None, maximum_height: int = None,
hide_x_axis: bool = False hide_x_axis: bool = False
) -> None: ) -> None:
""" """
Add plot area. Add plot area.
@ -111,20 +110,23 @@ class ChartWidget(pg.PlotWidget):
self._layout.addItem(plot) self._layout.addItem(plot)
def add_item( def add_item(
self, self,
item_class: Type[ChartItem], item_class: Type[ChartItem],
item_name: str, item_name: str,
plot_name: str plot_name: str
): ):
""" """
Add chart item. Add chart item.
""" """
# 创建显示的对象蜡烛图bar图散点线等
item = item_class(self._manager) item = item_class(self._manager)
self._items[item_name] = item self._items[item_name] = item
# 获取设置的显示区域,例如主图/volume/附图等
plot = self._plots.get(plot_name) plot = self._plots.get(plot_name)
plot.addItem(item) plot.addItem(item)
# 绑定显示对象与显示区域关系
self._item_plot_map[item] = plot self._item_plot_map[item] = plot
def get_plot(self, plot_name: str) -> pg.PlotItem: def get_plot(self, plot_name: str) -> pg.PlotItem:
@ -173,6 +175,7 @@ class ChartWidget(pg.PlotWidget):
for item in self._items.values(): for item in self._items.values():
item.update_bar(bar) item.update_bar(bar)
# 刷新显示区域的最高/最低值
self._update_plot_limits() self._update_plot_limits()
if self._right_ix >= (self._manager.get_count() - self._bar_count / 2): if self._right_ix >= (self._manager.get_count() - self._bar_count / 2):
@ -306,11 +309,11 @@ class ChartCursor(QtCore.QObject):
"""""" """"""
def __init__( def __init__(
self, self,
widget: ChartWidget, widget: ChartWidget,
manager: BarManager, manager: BarManager,
plots: Dict[str, pg.GraphicsObject], plots: Dict[str, pg.GraphicsObject],
item_plot_map: Dict[ChartItem, pg.GraphicsObject] item_plot_map: Dict[ChartItem, pg.GraphicsObject]
): ):
"""""" """"""
super().__init__() super().__init__()
@ -480,7 +483,9 @@ class ChartCursor(QtCore.QObject):
buf[plot] += ("\n\n" + item_info_text) buf[plot] += ("\n\n" + item_info_text)
for plot_name, plot in self._plots.items(): for plot_name, plot in self._plots.items():
plot_info_text = buf[plot] plot_info_text = buf.get(plot, None)
if not plot_info_text:
continue
info = self._infos[plot_name] info = self._infos[plot_name]
info.setText(plot_info_text) info.setText(plot_info_text)
info.show() info.show()
@ -532,3 +537,124 @@ class ChartCursor(QtCore.QObject):
for label in list(self._y_labels.values()) + [self._x_label]: for label in list(self._y_labels.values()) + [self._x_label]:
label.hide() label.hide()
class KlineWidget(ChartWidget):
""" k线widget支持多widget主图/volume/附图"""
clsId = 0
def __init__(self, parent: QtWidgets.QWidget = None,
title: str = "kline",
display_volume: bool = False,
display_sub: bool = False):
super().__init__(parent, title)
KlineWidget.clsId += 1
self.windowId = str(KlineWidget.clsId)
# 所有K线上指标
self.main_color_pool = deque(['red', 'green', 'yellow', 'white'])
self.main_indicator_data = {} # 主图指标数据字典key是指标value是list
self.main_indicator_colors = {} # 主图指标颜色字典key是指标value是list
self.main_indicator_plots = {} # 主图指标的所有画布字典key是指标value是plot)
self.display_volume = display_volume
self.display_sub = display_sub
# 所有副图上指标
self.sub_color_pool = deque(['red', 'green', 'yellow', 'white'])
self.sub_indicator_data = {}
self.sub_indicator_colors = {}
self.sub_indicator_plots = {}
self.main_plot_name = f'{self.windowId}_main'
self.volume_plot_name = f'{self.windowId}_volume'
self.sub_plot_name = f'{self.windowId}_sub'
self.main_plot = None
self.volume_plot = None
self.sub_plot = None
if self.display_volume or self.display_sub:
self.add_plot(self.main_plot_name, hide_x_axis=True) # 主图
self.add_item(CandleItem, "candle", self.main_plot_name) # 往主图区域,加入
if self.display_volume:
self.add_plot(self.volume_plot_name, maximum_height=60) # volume 附图
self.add_item(VolumeItem, "volume", self.volume_plot_name)
self.volume_plot = self.get_plot(self.volume_plot_name)
if self.display_sub:
self.add_plot(self.sub_plot_name, maximum_height=180) # 附图
self.sub_plot = self.get_plot(self.sub_plot_name)
else:
self.add_plot(self.main_plot_name, hide_x_axis=False) # 主图
self.add_item(CandleItem, "candle", self.main_plot_name) # 往主图区域,加入
self.add_cursor()
self.main_plot = self.get_plot(self.main_plot_name)
def add_indicator(self, indicator: str, is_main: bool = True):
"""
新增指标信号图
:param indicator: 指标/信号的名称如ma10
:param is_main: 是否为主图
:return:
"""
if is_main:
if indicator in self.main_indicator_plots:
self.main_plot.removeItem(self.main_indicator_plots[indicator]) # 存在该指标/信号,先移除原有画布
self.main_indicator_plots[indicator] = self.main_plot.plot() # 为该指标/信号,创建新的主图画布,登记字典
self.main_indicator_colors[indicator] = self.main_color_pool[0] # 登记该指标/信号使用的颜色
self.main_color_pool.append(self.main_color_pool.popleft()) # 调整剩余颜色
if indicator not in self.main_indicator_data:
self.main_indicator_data[indicator] = []
else:
if indicator in self.sub_indicator_plots:
self.sub_plot.removeItem(self.sub_indicator_plots[indicator]) # 若存在该指标/信号,先移除原有的附图画布
self.sub_indicator_plots[indicator] = self.sub_plot.plot() # 为该指标/信号,创建新的主图画布,登记字典
self.sub_indicator_colors[indicator] = self.sub_color_pool[0] # 登记该指标/信号使用的颜色
self.sub_color_pool.append(self.sub_color_pool.popleft()) # 调整剩余颜色
if indicator not in self.sub_indicator_data:
self.sub_indicator_data[indicator] = []
def clear_indicator(self, main=True):
"""清空指标图形"""
# 清空信号图
if main:
for indicator in self.main_indicator_plots:
self.main_plot.removeItem(self.main_indicator_plots[indicator])
self.main_indicator_data = {}
self.main_indicator_plots = {}
else:
for indicator in self.sub_indicator_plots:
self.sub_plot.removeItem(self.sub_indicator_plots[indicator])
self.sub_indicator_data = {}
self.sub_indicator_plots = {}
def plot_indicator(self, datas: dict, is_main=True, clear=False):
"""
刷新指标/信号图( 新数据
:param datas: 所有数据
:param is_main: 是否为主图
:param clear: 是否要清除旧数据
:return:
"""
if clear:
self.clear_indicator(is_main) # 清除主图/副图
if is_main:
for indicator in datas:
self.add_indicator(indicator, is_main) # 逐一添加主图信号/指标
self.main_indicator_data[indicator] = datas[indicator] # 更新组件数据字典
# 调用该信号/指标画布(plotDataItem.setData()),更新数据,更新画笔颜色,更新名称
self.main_indicator_plots[indicator].setData(datas[indicator],
pen=self.main_indicator_colors[indicator][0],
name=indicator)
else:
for indicator in datas:
self.add_indicator(indicator, is_main) # 逐一增加子图指标/信号
self.sub_indicator_data[indicator] = datas[indicator] # 更新组件数据字典
# 调用该信号/指标画布(plotDataItem.setData()),更新数据,更新画笔颜色,更新名称
self.sub_indicator_plots[indicator].setData(datas[indicator],
pen=self.sub_indicator_colors[indicator][0], name=indicator)

View File

@ -3,5 +3,5 @@
HEIGHT_LIST = [3, 5, 10, 'K3', 'K5', 'K10'] HEIGHT_LIST = [3, 5, 10, 'K3', 'K5', 'K10']
FUTURE_RENKO_DB_NAME = 'FutureRenko_Db' FUTURE_RENKO_DB_NAME = 'FutureRenko'
STOCK_RENKO_DB_NAME = 'StockRenko_Db' STOCK_RENKO_DB_NAME = 'StockRenko'

View File

@ -144,7 +144,6 @@ OPTIONTYPE_CTP2VT = {
MAX_FLOAT = sys.float_info.max MAX_FLOAT = sys.float_info.max
symbol_exchange_map = {} symbol_exchange_map = {}
symbol_name_map = {} symbol_name_map = {}
symbol_size_map = {} symbol_size_map = {}
@ -223,7 +222,7 @@ class CtpGateway(BaseGateway):
self.combiner_conf_dict = c.get_config() self.combiner_conf_dict = c.get_config()
if len(self.combiner_conf_dict) > 0: if len(self.combiner_conf_dict) > 0:
self.write_log(u'加载的自定义价差/价比配置:{}'.format(self.combiner_conf_dict)) self.write_log(u'加载的自定义价差/价比配置:{}'.format(self.combiner_conf_dict))
except Exception as ex: # noqa except Exception as ex: # noqa
pass pass
if not self.td_api: if not self.td_api:
self.td_api = CtpTdApi(self) self.td_api = CtpTdApi(self)
@ -790,8 +789,14 @@ class CtpTdApi(TdApi):
account.close_profit = data['CloseProfit'] account.close_profit = data['CloseProfit']
account.holding_profit = data['PositionProfit'] account.holding_profit = data['PositionProfit']
account.trading_day = str(data['TradingDay']) account.trading_day = str(data['TradingDay'])
if '-' not in account.trading_day and len(account.trading_day)== 8: if '-' not in account.trading_day and len(account.trading_day) == 8:
account.trading_day = account.trading_day[0:4] + '-' + account.trading_day[4:6] + '-' + account.trading_day[6:8] account.trading_day = '-'.join(
[
account.trading_day[0:4],
account.trading_day[4:6],
account.trading_day[6:8]
]
)
self.gateway.on_account(account) self.gateway.on_account(account)
@ -1121,6 +1126,7 @@ def adjust_price(price: float) -> float:
price = 0 price = 0
return price return price
class TdxMdApi(): class TdxMdApi():
""" """
通达信数据行情API实现 通达信数据行情API实现
@ -1745,38 +1751,58 @@ class TickCombiner(object):
self.gateway.on_tick(spread_tick) self.gateway.on_tick(spread_tick)
if self.is_ratio: if self.is_ratio:
ratio_tick = TickData(gateway_name=self.gateway_name, ratio_tick = TickData(
symbol=self.symbol, gateway_name=self.gateway_name,
exchange=Exchange.SPD, symbol=self.symbol,
datetime=tick.datetime) exchange=Exchange.SPD,
datetime=tick.datetime
)
ratio_tick.trading_day = tick.trading_day ratio_tick.trading_day = tick.trading_day
ratio_tick.date = tick.date ratio_tick.date = tick.date
ratio_tick.time = tick.time ratio_tick.time = tick.time
# 比率tick # 比率tick
ratio_tick.ask_price_1 = round_to(target=self.price_tick, ratio_tick.ask_price_1 = 100 * self.last_leg1_tick.ask_price_1 * self.leg1_ratio \
value=100 * self.last_leg1_tick.ask_price_1 * self.leg1_ratio / ( / (self.last_leg2_tick.bid_price_1 * self.leg2_ratio) # noqa
self.last_leg2_tick.bid_price_1 * self.leg2_ratio)) ratio_tick.ask_price_1 = round_to(
ratio_tick.ask_volume_1 = min(self.last_leg1_tick.ask_volume_1, self.last_leg2_tick.bid_volume_1) target=self.price_tick,
value=ratio_tick.ask_price_1
)
ratio_tick.ask_volume_1 = min(self.last_leg1_tick.ask_volume_1, self.last_leg2_tick.bid_volume_1)
ratio_tick.bid_price_1 = 100 * self.last_leg1_tick.bid_price_1 * self.leg1_ratio \
/ (self.last_leg2_tick.ask_price_1 * self.leg2_ratio) # noqa
ratio_tick.bid_price_1 = round_to(
target=self.price_tick,
value=ratio_tick.bid_price_1
)
ratio_tick.bid_price_1 = round_to(target=self.price_tick,
value=100 * self.last_leg1_tick.bid_price_1 * self.leg1_ratio / (
self.last_leg2_tick.ask_price_1 * self.leg2_ratio))
ratio_tick.bid_volume_1 = min(self.last_leg1_tick.bid_volume_1, self.last_leg2_tick.ask_volume_1) ratio_tick.bid_volume_1 = min(self.last_leg1_tick.bid_volume_1, self.last_leg2_tick.ask_volume_1)
ratio_tick.lastPrice = round_to(target=self.price_tick, ratio_tick.last_price = (ratio_tick.ask_price_1 + ratio_tick.bid_price_1) / 2
value=(ratio_tick.ask_price_1 + ratio_tick.bid_price_1) / 2) ratio_tick.last_price = round_to(
target=self.price_tick,
value=ratio_tick.last_price
)
# 昨收盘价 # 昨收盘价
if self.last_leg2_tick.pre_close > 0 and self.last_leg1_tick.pre_close > 0: if self.last_leg2_tick.pre_close > 0 and self.last_leg1_tick.pre_close > 0:
ratio_tick.pre_close = round_to(target=self.price_tick, ratio_tick.pre_close = 100 * self.last_leg1_tick.pre_close * self.leg1_ratio / (
value=100 * self.last_leg1_tick.pre_close * self.leg1_ratio / ( self.last_leg2_tick.pre_close * self.leg2_ratio) # noqa
self.last_leg2_tick.pre_close * self.leg2_ratio)) ratio_tick.pre_close = round_to(
target=self.price_tick,
value=ratio_tick.pre_close
)
# 开盘价 # 开盘价
if self.last_leg2_tick.open_price > 0 and self.last_leg1_tick.open_price > 0: if self.last_leg2_tick.open_price > 0 and self.last_leg1_tick.open_price > 0:
ratio_tick.open_price = round_to(target=self.price_tick, ratio_tick.open_price = 100 * self.last_leg1_tick.open_price * self.leg1_ratio / (
value=100 * self.last_leg1_tick.open_price * self.leg1_ratio / ( self.last_leg2_tick.open_price * self.leg2_ratio) # noqa
self.last_leg2_tick.open_price * self.leg2_ratio)) ratio_tick.open_price = round_to(
target=self.price_tick,
value=ratio_tick.open_price
)
# 最高价 # 最高价
if self.ratio_high: if self.ratio_high:
self.ratio_high = max(self.ratio_high, ratio_tick.ask_price_1) self.ratio_high = max(self.ratio_high, ratio_tick.ask_price_1)

0
vnpy/task/__init__.py Normal file
View File

View File

@ -40,7 +40,7 @@ print(u'Celery 使用redis配置:\nbroker:{}\nbackend:{}'.format(broker, backend
app = Celery('vnpy_task', broker=broker) app = Celery('vnpy_task', broker=broker)
# 动态导入task目录下子任务 # 动态导入task目录下子任务
app.conf.CELERY_IMPORTS = ['vnpy.task.celery_app.worker_started'] # app.conf.CELERY_IMPORTS = ['vnpy.task.celery_app.worker_started']
def worker_started(): def worker_started():

View File

@ -1,4 +1,4 @@
{ {
"celery_broker": "amqp://admin:admin@192.168.0.202:5672//", "celery_broker": "amqp://admin:admin@127.0.0.1:5672//",
"celery_backend": "amqp://admin:admin@192.168.0.202:5672//" "celery_backend": "amqp://admin:admin@127.0.0.1:5672//"
} }

View File

@ -24,7 +24,6 @@ from ..object import OrderRequest, SubscribeRequest
from ..utility import load_json, save_json from ..utility import load_json, save_json
from ..setting import SETTING_FILENAME, SETTINGS from ..setting import SETTING_FILENAME, SETTINGS
COLOR_LONG = QtGui.QColor("red") COLOR_LONG = QtGui.QColor("red")
COLOR_SHORT = QtGui.QColor("green") COLOR_SHORT = QtGui.QColor("green")
COLOR_BID = QtGui.QColor(255, 174, 201) COLOR_BID = QtGui.QColor(255, 174, 201)
@ -640,7 +639,7 @@ class TradingWidget(QtWidgets.QWidget):
form1.addRow("方向", self.direction_combo) form1.addRow("方向", self.direction_combo)
form1.addRow("开平", self.offset_combo) form1.addRow("开平", self.offset_combo)
form1.addRow("类型", self.order_type_combo) form1.addRow("类型", self.order_type_combo)
form1.addRow( self.checkFixed, self.price_line) form1.addRow(self.checkFixed, self.price_line)
form1.addRow("数量", self.volume_line) form1.addRow("数量", self.volume_line)
form1.addRow("接口", self.gateway_combo) form1.addRow("接口", self.gateway_combo)
form1.addRow(send_button) form1.addRow(send_button)
@ -899,8 +898,6 @@ class TradingWidget(QtWidgets.QWidget):
except Exception as ex: except Exception as ex:
self.main_engine.write_log(u'tradingWg.autoFillSymbol exception:{}'.format(str(ex))) self.main_engine.write_log(u'tradingWg.autoFillSymbol exception:{}'.format(str(ex)))
#----------------------------------------------------------------------
def close_position(self, cell): def close_position(self, cell):
"""根据持仓信息自动填写交易组件""" """根据持仓信息自动填写交易组件"""
try: try:
@ -926,13 +923,14 @@ class TradingWidget(QtWidgets.QWidget):
self.volume_line.setText(str(pos.volume)) self.volume_line.setText(str(pos.volume))
if pos.direction in [Direction.LONG, Direction.NET]: if pos.direction in [Direction.LONG, Direction.NET]:
self.direction_combo.setCurrentText(Direction.SHORT) self.direction_combo.setCurrentText(Direction.SHORT.value)
else: else:
self.direction_combo.setCurrentText(Direction.LONG) self.direction_combo.setCurrentText(Direction.LONG.value)
except Exception as ex: except Exception as ex:
self.main_engine.write_log(u'tradingWg.closePosition exception:{}'.format(str(ex))) self.main_engine.write_log(u'tradingWg.closePosition exception:{}'.format(str(ex)))
class ActiveOrderMonitor(OrderMonitor): class ActiveOrderMonitor(OrderMonitor):
""" """
Monitor which shows active order only. Monitor which shows active order only.

View File

@ -394,7 +394,7 @@ def save_df_to_excel(file_name, sheet_name, df):
import openpyxl import openpyxl
from openpyxl.utils.dataframe import dataframe_to_rows from openpyxl.utils.dataframe import dataframe_to_rows
# from openpyxl.drawing.image import Image # from openpyxl.drawing.image import Image
except: # noqa except: # noqa
print(u'can not import openpyxl', file=sys.stderr) print(u'can not import openpyxl', file=sys.stderr)
if 'openpyxl' not in sys.modules: if 'openpyxl' not in sys.modules:
@ -407,7 +407,7 @@ def save_df_to_excel(file_name, sheet_name, df):
try: try:
# 读取文件 # 读取文件
wb = openpyxl.load_workbook(file_name) wb = openpyxl.load_workbook(file_name)
except: # noqa except: # noqa
# 创建一个excel workbook # 创建一个excel workbook
wb = openpyxl.Workbook() wb = openpyxl.Workbook()
ws = wb.active ws = wb.active
@ -416,7 +416,7 @@ def save_df_to_excel(file_name, sheet_name, df):
# 定位WorkSheet # 定位WorkSheet
if ws is None: if ws is None:
ws = wb[sheet_name] ws = wb[sheet_name]
except: # noqa except: # noqa
# 创建一个WorkSheet # 创建一个WorkSheet
ws = wb.create_sheet() ws = wb.create_sheet()
ws.title = sheet_name ws.title = sheet_name
@ -450,7 +450,7 @@ def save_text_to_excel(file_name, sheet_name, text):
import openpyxl import openpyxl
# from openpyxl.utils.dataframe import dataframe_to_rows # from openpyxl.utils.dataframe import dataframe_to_rows
# from openpyxl.drawing.image import Image # from openpyxl.drawing.image import Image
except: # noqa except: # noqa
print(u'can not import openpyxl', file=sys.stderr) print(u'can not import openpyxl', file=sys.stderr)
if 'openpyxl' not in sys.modules: if 'openpyxl' not in sys.modules:
@ -461,7 +461,7 @@ def save_text_to_excel(file_name, sheet_name, text):
try: try:
# 读取文件 # 读取文件
wb = openpyxl.load_workbook(file_name) wb = openpyxl.load_workbook(file_name)
except: # noqa except: # noqa
# 创建一个excel workbook # 创建一个excel workbook
wb = openpyxl.Workbook() wb = openpyxl.Workbook()
ws = wb.active ws = wb.active
@ -470,7 +470,7 @@ def save_text_to_excel(file_name, sheet_name, text):
# 定位WorkSheet # 定位WorkSheet
if ws is None: if ws is None:
ws = wb[sheet_name] ws = wb[sheet_name]
except: # noqa except: # noqa
# 创建一个WorkSheet # 创建一个WorkSheet
ws = wb.create_sheet() ws = wb.create_sheet()
ws.title = sheet_name ws.title = sheet_name
@ -516,7 +516,7 @@ def save_images_to_excel(file_name, sheet_name, image_names):
try: try:
# 读取文件 # 读取文件
wb = openpyxl.load_workbook(file_name) wb = openpyxl.load_workbook(file_name)
except: # noqa except: # noqa
# 创建一个excel workbook # 创建一个excel workbook
wb = openpyxl.Workbook() wb = openpyxl.Workbook()
ws = wb.active ws = wb.active
@ -612,6 +612,7 @@ def display_dual_axis(df, columns1, columns2=[], invert_yaxis1=False, invert_yax
else: else:
plt.show() plt.show()
class BarGenerator: class BarGenerator:
""" """
For: For:
@ -624,11 +625,11 @@ class BarGenerator:
""" """
def __init__( def __init__(
self, self,
on_bar: Callable, on_bar: Callable,
window: int = 0, window: int = 0,
on_window_bar: Callable = None, on_window_bar: Callable = None,
interval: Interval = Interval.MINUTE interval: Interval = Interval.MINUTE
): ):
"""Constructor""" """Constructor"""
self.bar = None self.bar = None
@ -1225,7 +1226,7 @@ def get_bars(csv_file: str,
symbol: str, symbol: str,
exchange: Exchange, exchange: Exchange,
start_date: datetime = None, start_date: datetime = None,
end_date: datetime = None,): end_date: datetime = None, ):
""" """
获取bar 获取bar
数据存储目录: 项目/bar_data 数据存储目录: 项目/bar_data

View File

@ -0,0 +1 @@
celery -A vnpy.task.celery_app purge

View File

@ -0,0 +1 @@
celery worker -c 2 -A vnpy.task.celery_app -P eventlet -l debug -f celery.log