├── .gitignore
├── .idea
├── PythonLab.iml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── Cpp64
├── CppBt.pyd
├── libbson-1.0.dll
├── libcrypto-1_1-x64.dll
├── libmongoc-1.0.dll
├── libssl-1_1-x64.dll
├── msvcp120.dll
└── msvcr120.dll
├── CppBt.pyd
├── QIPythonWidget.py
├── README.md
├── __init__.py
├── cppStruct.py
├── css.qss
├── cta.ico
├── ctaBacktesting.py
├── ctaBase.py
├── ctaBasicModel.py
├── ctaEngine.py
├── ctaFunction
├── __init__.py
├── calcFunction.py
├── dataFunction.py
└── visFunction.py
├── ctaSetting.py
├── ctaTask.py
├── ctaTaskPool.py
├── ctaTemplate.py
├── eventEngine.py
├── eventType.py
├── func-button
├── klAna.py
├── klBacktest.py
├── klClearSig.py
├── klHeatmap.py
├── klLoad.py
├── klPlay.py
├── klPnl.py
├── klReload.py
├── klShowdown.py
├── klShowmain.py
└── klSigmode.py
├── historyData
└── .gitignore
├── json
├── CTA_setting.json
├── ContractInfo.json
├── DATA_setting.json
├── uiCtaKLine_button.json
└── uiCtaKLine_input.json
├── libbson-1.0.dll
├── libcrypto-1_1.dll
├── libmongoc-1.0.dll
├── libssl-1_1.dll
├── log
└── .gitignore
├── msvcp120.dll
├── msvcr120.dll
├── notebook
├── .ipynb_checkpoints
│ ├── 主成分分析-checkpoint.ipynb
│ ├── 数据库函数测试-checkpoint.ipynb
│ ├── 策略分析-checkpoint.ipynb
│ └── 策略组合优化-checkpoint.ipynb
├── VT_setting.json
├── __pycache__
│ ├── ctaBase.cpython-36.pyc
│ ├── vtConstant.cpython-36.pyc
│ └── vtFunction.cpython-36.pyc
├── ctaBase.py
├── ctaBase.pyc
├── vtBase.py
├── vtConstant.py
├── vtConstant.pyc
├── vtFunction.py
├── vtFunction.pyc
└── 策略组合优化.ipynb
├── opResults
└── .gitignore
├── strategy
├── BASICStrategy.py
├── __init__.py
└── 期货趋势
│ └── DMAStrategy.py
├── tools
├── .ctaHistoryData.py.un~
├── .peakdetect.py.un~
├── __init__.py
├── __init__.pyc
├── ctaBase.py
├── ctaBase.pyc
├── ctaHistoryData.py
├── ctaHistoryData.pyc
├── ctaHistoryData.py~
├── downcsv.py
├── downcsv.pyc
├── peakdetect.py
├── peakdetect.pyc
├── peakdetect.py~
├── utils.py
├── utils.pyc
├── vtConstant.py
└── vtConstant.pyc
├── trade
└── .gitignore
├── uiBasicIO.py
├── uiBasicWidget.py
├── uiCrosshair.py
├── uiCtaKLine.py
├── uiCtaTaskWidget.py
├── uiCtaWidget.py
├── uiKLine.py
├── vecsig
├── reverse.py
└── trends.py
├── vtConstant.py
├── vtObject.py
├── 启动界面.cmd
└── 研究环境.cmd
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.swp
4 | *.un~
5 | *~
6 |
--------------------------------------------------------------------------------
/.idea/PythonLab.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | timedelta
110 | QtGui
111 | pyqtSignal
112 | Signal
113 | signal
114 |
115 |
116 |
117 |
118 |
119 |
120 |
130 |
131 |
132 |
133 |
134 | true
135 | DEFINITION_ORDER
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 | 1527212552646
201 |
202 |
203 | 1527212552646
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
--------------------------------------------------------------------------------
/Cpp64/CppBt.pyd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/CppBt.pyd
--------------------------------------------------------------------------------
/Cpp64/libbson-1.0.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/libbson-1.0.dll
--------------------------------------------------------------------------------
/Cpp64/libcrypto-1_1-x64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/libcrypto-1_1-x64.dll
--------------------------------------------------------------------------------
/Cpp64/libmongoc-1.0.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/libmongoc-1.0.dll
--------------------------------------------------------------------------------
/Cpp64/libssl-1_1-x64.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/libssl-1_1-x64.dll
--------------------------------------------------------------------------------
/Cpp64/msvcp120.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/msvcp120.dll
--------------------------------------------------------------------------------
/Cpp64/msvcr120.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/Cpp64/msvcr120.dll
--------------------------------------------------------------------------------
/CppBt.pyd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/CppBt.pyd
--------------------------------------------------------------------------------
/QIPythonWidget.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | from qtpy.QtWidgets import *
4 |
5 | #from qtconsole.qt import QtGui
6 | from qtconsole.rich_ipython_widget import RichJupyterWidget
7 | from qtconsole.inprocess import QtInProcessKernelManager
8 | from IPython.lib import guisupport
9 |
10 | ##########################################################################
11 | class QIPythonWidget(RichJupyterWidget):
12 | """ Convenience class for a live IPython console widget"""
13 | #---------------------------------------------------------------------
14 | def __init__(self,ctaEngine, eventEngine, parent, customBanner=None,*args,**kwargs):
15 |
16 | banner = u""
17 | banner += u"\nkTool.datas : 行情数据"
18 | banner += u"\nkTool.parent.spdData : 开仓点分析"
19 | super(QIPythonWidget, self).__init__(banner = banner)
20 |
21 | #self.setFixedWidth(420)
22 |
23 | if customBanner is not None: self.banner=customBanner
24 |
25 | self.font_size = 6
26 | self._display_banner = False
27 | self.kernel_manager = kernel_manager = QtInProcessKernelManager()
28 | kernel_manager.start_kernel(show_banner=False)
29 | kernel_manager.kernel.gui = 'qt'
30 | self.kernel_client = kernel_client = self.kernel_manager.client()
31 | kernel_client.start_channels()
32 |
33 | def stop():
34 | kernel_client.stop_channels()
35 | kernel_manager.shutdown_kernel()
36 | guisupport.get_app_qt4().exit()
37 | self.exit_requested.connect(stop)
38 |
39 | #---------------------------------------------------------------------
40 | def pushVariables(self,variableDict):
41 | """
42 | Given a dictionary containing name / value pairs,
43 | push those variables to the IPython console widget
44 | """
45 | self.kernel_manager.kernel.shell.push(variableDict)
46 | #---------------------------------------------------------------------
47 | def clearTerminal(self):
48 | """
49 | Clears the terminal
50 | """
51 | self._control.clear()
52 |
53 | #---------------------------------------------------------------------
54 | def print_text(self, text):
55 | """
56 | Prints some plain text to the console
57 | """
58 | self._append_plain_text(text)
59 |
60 | #---------------------------------------------------------------------
61 | def execute_command(self, command):
62 | """
63 | Execute a command in the frame of the console widget
64 | """
65 | self._execute(command, False)
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PythonLab
2 | Python Backtesing Tool
3 |
4 | 文档地址:
5 | https://bbs.quantfair.com/python-lab/docs/index.html
6 |
7 | QQ群:
8 | 496877861
9 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | from ctaEngine import CtaEngine
4 | from uiCtaWidget import CtaEngineManager
5 |
6 | appName = 'CtaStrategy'
7 | appDisplayName = u'CTA策略'
8 | appEngine = CtaEngine
9 | appWidget = CtaEngineManager
10 | appIco = 'cta.ico'
--------------------------------------------------------------------------------
/cppStruct.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | import ctypes
3 | from ctypes import Structure,c_char,c_longlong,c_int,c_double
4 |
5 | class QDMarketDataField(Structure):
6 | _fields_ = [
7 | ("vtSymbol", c_char * 31), # 合约代码
8 | ("ctime", c_longlong), # 时间
9 | ("msec", c_int), # 最后修改毫秒
10 | ("lastPrice", c_double), # 最新价
11 | ("volume", c_int), # 数量
12 | ("turnover", c_double), # 成交金额
13 | ("openInterest", c_double), # 持仓量
14 | ("upperLimit", c_double), # 涨停板价
15 | ("lowerLimit", c_double), # 跌停板价
16 | ("bidPrice1", c_double), # 申买价一
17 | ("bidVolume1", c_int), # 申买量一
18 | ("askPrice1", c_double), # 申卖价一
19 | ("askVolume1", c_int), # 申卖量一
20 | ("date", c_char * 13), # 交易日
21 | ]
22 |
23 | class QDBarMarketDataField(Structure):
24 | _fields_ = [
25 | ("date", c_char * 9), # 交易日
26 | ("vtSymbol", c_char * 31), # 合约代码
27 | ("ctime", c_longlong), # 首tick修改时间
28 | ("open", c_double), # 开
29 | ("close", c_double), # 收
30 | ("low", c_double), # 低
31 | ("high", c_double), # 高
32 | ("volume", c_double), # 区间交易量
33 | ]
34 |
35 | class QDRtnOrderField(Structure):
36 | _fields_ = [
37 | ("brokerID", c_char * 11), # 经纪公司代码
38 | ("userID", c_char * 16), # 用户代码
39 | ("participantID", c_char * 11), # 会员代码
40 | ("investorID", c_char * 19), # 投资者代码
41 | ("businessUnit", c_char * 21), # 业务单元
42 | ("vtSymbol", c_char * 31), # 合约代码
43 | ("orderID", c_char * 21), # 报单引用
44 | ("exchange", c_char * 11), # 交易所代码
45 | ("price", c_double), # 价格
46 | ("tradedVolume", c_int), # 今成交数量
47 | ("volumeTotal", c_int), # 剩余数量
48 | ("totalVolume", c_int), # 总数量
49 | ("timeCondition", c_char), # 有效期类型
50 | ("volumeCondition", c_char), # 成交量类型
51 | ("priceType", c_char), # 报单价格条件
52 | ("directioncpp", c_char), # 买卖方向
53 | ("offsetcpp", c_char), # 开平标志
54 | ("hedge", c_char), # 投机套保标志
55 | ("statuscpp", c_char), # 报单状态
56 | ("orderTime", c_char * 21), # 报单时间
57 | ("orderID", c_int), # 请求编号
58 | ]
59 |
60 | class QDRtnTradeField(Structure):
61 | _fields_ = [
62 | ("brokerID", c_char * 11), # 经纪公司代码
63 | ("userID", c_char * 16), # 用户代码
64 | ("investorID", c_char * 19), # 投资者代码
65 | ("businessUnit", c_char * 21), # 业务单元
66 | ("vtSymbol", c_char * 31), # 合约代码
67 | ("orderID", c_char * 21), # 报单引用
68 | ("exchange", c_char * 11), # 交易所代码
69 | ("tradeID", c_char * 21), # 成交编号
70 | ("orderSysID", c_char * 31), # 报单编号
71 | ("participantID", c_char * 11), # 会员代码
72 | ("clientID", c_char * 21), # 客户代码
73 | ("price", c_double), # 价格
74 | ("volume", c_int), # 数量
75 | ("ctime", c_longlong), # 时间
76 | ("tradingDay", c_char * 13), # 交易日
77 | ("tradeTm", c_char * 13), # 成交时间
78 | ("directioncpp", c_char), # 买卖方向
79 | ("offsetcpp", c_char), # 开平标志
80 | ("hedgeFlag", c_char), # 投机套保标志
81 | ]
82 |
83 |
--------------------------------------------------------------------------------
/cta.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/cta.ico
--------------------------------------------------------------------------------
/ctaBase.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件中包含了CTA模块中用到的一些基础设置、类和常量等。
5 | '''
6 |
7 | from __future__ import division
8 | from vtConstant import *
9 |
10 | # 把vn.trader根目录添加到python环境变量中
11 | import sys
12 | sys.path.append('..')
13 |
14 |
15 | # 常量定义
16 | # CTA引擎中涉及到的交易方向类型
17 | CTAORDER_BUY = u'买开'
18 | CTAORDER_SELL = u'卖平'
19 | CTAORDER_SELL_TODAY = u'卖平今'
20 | CTAORDER_SHORT = u'卖开'
21 | CTAORDER_COVER = u'买平'
22 | CTAORDER_COVER_TODAY = u'买平今'
23 | STATUS_PARTTRADED_PARTCANCELLED = u'部成部撤'
24 |
25 | # 本地停止单状态
26 | STOPORDER_WAITING = u'等待中'
27 | STOPORDER_CANCELLED = u'已撤销'
28 | STOPORDER_TRIGGERED = u'已触发'
29 |
30 | # 本地停止单前缀
31 | STOPORDERPREFIX = 'CtaStopOrder.'
32 |
33 | # CTA模块事件
34 | EVENT_CTA_LOG = 'eCtaLog' # CTA相关的日志事件
35 | EVENT_CTA_STRATEGY = 'eCtaStrategy.' # CTA策略状态变化事件
36 |
37 | # 数据库名称
38 | SETTING_DB_NAME = 'VnTrader_Setting_Db'
39 | CAPITAL_DB_NAME = 'vt_trader_cap_db'
40 | TICK_DB_NAME = 'vnTrader_Tick_db'
41 | CAP_DB_NAME = 'vt_trader_cap_db'
42 | DAILY_DB_NAME = 'VnTrader_Daily_Db'
43 | MINUTE_DB_NAME = 'VnTrader_1Min_Db'
44 |
45 | ########################################################################
46 | class CtaBarData(object):
47 | """K线数据"""
48 |
49 | #----------------------------------------------------------------------
50 | def __init__(self):
51 | """Constructor"""
52 | self.vtSymbol = EMPTY_STRING # vt系统代码
53 | self.symbol = EMPTY_STRING # 代码
54 | self.exchange = EMPTY_STRING # 交易所
55 |
56 | self.open = EMPTY_FLOAT # OHLC
57 | self.high = EMPTY_FLOAT
58 | self.low = EMPTY_FLOAT
59 | self.close = EMPTY_FLOAT
60 |
61 | self.date = EMPTY_STRING # bar开始的时间,日期
62 | self.time = EMPTY_STRING # 时间
63 | self.datetime = None # python的datetime时间对象
64 |
65 | self.volume = EMPTY_INT # 成交量
66 | self.openInterest = EMPTY_INT # 持仓量
67 | self.turnover = EMPTY_FLOAT # 成交额
68 |
69 |
70 | ########################################################################
71 | class CtaTickData(object):
72 | """Tick数据"""
73 |
74 | #----------------------------------------------------------------------
75 | def __init__(self):
76 | """Constructor"""
77 | self.vtSymbol = EMPTY_STRING # vt系统代码
78 | self.symbol = EMPTY_STRING # 合约代码
79 | self.exchange = EMPTY_STRING # 交易所代码
80 |
81 | # 成交数据
82 | self.lastPrice = EMPTY_FLOAT # 最新成交价
83 | self.volume = EMPTY_INT # 最新成交量
84 | self.openInterest = EMPTY_INT # 持仓量
85 |
86 | self.upperLimit = EMPTY_FLOAT # 涨停价
87 | self.lowerLimit = EMPTY_FLOAT # 跌停价
88 |
89 | self.turnover = EMPTY_FLOAT # 成交额
90 |
91 | # tick的时间
92 | self.date = EMPTY_STRING # 日期
93 | self.time = EMPTY_STRING # 时间
94 | self.datetime = None # python的datetime时间对象
95 |
96 | # 五档行情
97 | self.bidPrice1 = EMPTY_FLOAT
98 | self.bidPrice2 = EMPTY_FLOAT
99 | self.bidPrice3 = EMPTY_FLOAT
100 | self.bidPrice4 = EMPTY_FLOAT
101 | self.bidPrice5 = EMPTY_FLOAT
102 |
103 | self.askPrice1 = EMPTY_FLOAT
104 | self.askPrice2 = EMPTY_FLOAT
105 | self.askPrice3 = EMPTY_FLOAT
106 | self.askPrice4 = EMPTY_FLOAT
107 | self.askPrice5 = EMPTY_FLOAT
108 |
109 | self.bidVolume1 = EMPTY_INT
110 | self.bidVolume2 = EMPTY_INT
111 | self.bidVolume3 = EMPTY_INT
112 | self.bidVolume4 = EMPTY_INT
113 | self.bidVolume5 = EMPTY_INT
114 |
115 | self.askVolume1 = EMPTY_INT
116 | self.askVolume2 = EMPTY_INT
117 | self.askVolume3 = EMPTY_INT
118 | self.askVolume4 = EMPTY_INT
119 | self.askVolume5 = EMPTY_INT
120 |
121 | ########################################################################
122 | class CtaCapData(object):
123 | """策略资产数据"""
124 |
125 | #----------------------------------------------------------------------
126 | def __init__(self):
127 | """Constructor"""
128 | self.name = EMPTY_STRING # 策略名
129 | self.datetime = None # 时间
130 | self.start = None # 开始时间
131 | self.date = None # 日期
132 | self.cap = EMPTY_FLOAT # 资金
133 | self.pnl = EMPTY_FLOAT # 当日盈亏
134 | self.drawdown = EMPTY_FLOAT # 回撤
135 |
--------------------------------------------------------------------------------
/ctaBasicModel.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件中包含了CTA模块中用到的一些基础设置、类和常量等。
5 | '''
6 |
7 | from __future__ import division
8 | from qtpy import QtGui, QtCore
9 | from ctaBase import *
10 | from eventEngine import *
11 |
12 | ########################################################################
13 | class StrategyBacktesting(QtGui.QStandardItemModel):
14 |
15 | """用于回测的策略信息"""
16 | signal = QtCore.Signal(type(Event()))
17 | #----------------------------------------------------------------------
18 | def __init__(self, eventEngine, ctaEngine=None, view = None):
19 | """Constructor"""
20 | super(StrategyBacktesting, self).__init__(None)
21 |
22 | self.eventEngine = eventEngine
23 | self.ctaEngine = ctaEngine
24 | self.view = view
25 | self.view = view
26 | self.nRow = 0
27 | self.nameItems = {}
28 | self.classItems = {}
29 |
30 | self.initUi()
31 | self.updateData()
32 |
33 | #----------------------------------------------------------------------
34 | def initUi(self):
35 | """"""
36 | self.setHorizontalHeaderItem(0, QtGui.QStandardItem(u'策略信息'))
37 | self.setHorizontalHeaderItem(1, QtGui.QStandardItem(u'交易合约'))
38 | self.setHorizontalHeaderItem(2, QtGui.QStandardItem(u'使用资金'))
39 |
40 | #----------------------------------------------------------------------
41 | def registerEvent(self):
42 | """注册事件监听"""
43 | self.signal.connect(self.updateData)
44 | self.eventEngine.register(EVENT_CTA_STRATEGY, self.signal.emit)
45 |
46 | #----------------------------------------------------------------------
47 | def updateData(self,name0=''):
48 | if self.nRow > 0:
49 | self.removeRows(0,self.nRow)
50 | self.classItems = {}
51 | self.nameItems = {}
52 | for name in self.ctaEngine.strategyDict:
53 | paramDict = self.ctaEngine.getStrategyParam(name)
54 | className = paramDict['className']
55 | if not className in self.classItems:
56 | self.classItems[className] = QtGui.QStandardItem(paramDict['className'])
57 | self.appendRow([self.classItems[className]])
58 | nameItem = QtGui.QStandardItem(name)
59 | self.nameItems[name] = nameItem
60 | if name != name0:
61 | nameItem.setCheckState(False)
62 | else:
63 | nameItem.setCheckState(True)
64 | nameItem.setCheckable(False)
65 | self.classItems[className].appendRow([
66 | nameItem,
67 | QtGui.QStandardItem(str(paramDict['symbolList'])),
68 | QtGui.QStandardItem(str(paramDict['capital']))])
69 | self.nRow = len(self.classItems.values())
70 | if self.view: self.view.expandAll()
71 |
72 | #----------------------------------------------------------------------
73 | def checkName(self,name0):
74 | for name in self.nameItems:
75 | if name0==name:
76 | self.nameItems[name].setCheckState(True)
77 | else:
78 | self.nameItems[name].setCheckState(False)
79 |
80 | ########################################################################
81 | class StrategyParam(QtGui.QStandardItemModel):
82 |
83 | """用于回测的策略信息"""
84 | signal = QtCore.Signal(type(Event()))
85 | #----------------------------------------------------------------------
86 | def __init__(self, eventEngine, ctaEngine=None):
87 | """Constructor"""
88 | super(StrategyParam, self).__init__(None)
89 |
90 | self.eventEngine = eventEngine
91 | self.ctaEngine = ctaEngine
92 | self.name = ''
93 | self.nRow = 0
94 | self.nameItems = {}
95 |
96 | #----------------------------------------------------------------------
97 | def registerEvent(self):
98 | """注册事件监听"""
99 | self.signal.connect(self.updateData)
100 | self.eventEngine.register(EVENT_CTA_STRATEGY, self.signal.emit)
101 |
102 | #----------------------------------------------------------------------
103 | def updateData(self,name,append = False):
104 | if not append:
105 | #self.removeRows(0,self.nRow)
106 | self.clear()
107 | self.nRow = 1
108 | else:
109 | self.nRow += 1
110 | paramDict = self.ctaEngine.getStrategyParam(name)
111 | keys = []
112 | values = []
113 | for k,v in paramDict.items():
114 | keys.append(k)
115 | values.append(v)
116 | for i in range(len(keys)):
117 | self.setHorizontalHeaderItem(i, QtGui.QStandardItem(str(keys[i])))
118 | self.appendRow([
119 | QtGui.QStandardItem(str(v)) for v in values
120 | ])
121 |
122 |
123 | ########################################################################
124 | class MongoData(QtGui.QStandardItemModel):
125 |
126 | """数据信息"""
127 | signal = QtCore.Signal(type(Event()))
128 | #----------------------------------------------------------------------
129 | def __init__(self, eventEngine, ctaEngine=None):
130 | """Constructor"""
131 | super(MongoData, self).__init__(None)
132 |
133 | self.eventEngine = eventEngine
134 | self.ctaEngine = ctaEngine
135 | self.name = ''
136 | self.nRow = 0
137 | self.nameItems = {}
138 |
139 | #----------------------------------------------------------------------
140 | def registerEvent(self):
141 | """注册事件监听"""
142 | self.signal.connect(self.updateData)
143 | self.eventEngine.register(EVENT_CTA_STRATEGY, self.signal.emit)
144 |
145 | #----------------------------------------------------------------------
146 | def updateData(self,name):
147 | if self.nRow > 0:
148 | self.removeRows(0,self.nRow)
149 | self.setHorizontalHeaderItem(0, QtGui.QStandardItem(u'合约'))
150 | self.setHorizontalHeaderItem(1, QtGui.QStandardItem(u'数据范围'))
151 | self.setHorizontalHeaderItem(2, QtGui.QStandardItem(u'文件大小'))
152 | self.appendRow([
153 | QtGui.QStandardItem(str(v)) for v in values])
154 |
155 |
156 |
--------------------------------------------------------------------------------
/ctaEngine.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件中实现了CTA策略引擎,针对CTA类型的策略,抽象简化了部分底层接口的功能。
5 | '''
6 |
7 | import os
8 | import time
9 | import copy
10 | import json
11 | import pymongo
12 | import ctaTaskPool
13 | ctaTaskPool.taskPool = ctaTaskPool.ctaTaskPool()
14 | import traceback
15 | import multiprocessing
16 |
17 |
18 | from datetime import datetime,timedelta
19 | from collections import OrderedDict
20 | from eventEngine import *
21 |
22 | from ctaBase import *
23 | from ctaFunction import *
24 | from vtConstant import *
25 | from ctaSetting import *
26 | from ctaBacktesting import *
27 | from vtObject import VtLogData
28 |
29 | ########################################################################
30 | class CtaEngine(object):
31 | """CTA策略引擎"""
32 |
33 | settingFileName = 'CTA_setting.json'
34 | settingFileName = os.getcwd() + '\\json\\' + settingFileName
35 |
36 | #----------------------------------------------------------------------
37 | def __init__(self, mainEngine, eventEngine, settingFileName = 'CTA_setting.json'):
38 | """Constructor"""
39 | self.mainEngine = mainEngine
40 | self.eventEngine = eventEngine
41 | settingFileName0 = settingFileName
42 | self.settingFileName = os.getcwd() + '\\json\\' + settingFileName0
43 | self.optimism = False
44 | self.q = multiprocessing.Queue()
45 |
46 | # 当前日期
47 | self.today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
48 |
49 | # key为策略名称,value为策略实例,注意策略名称不允许重复
50 | self.strategyDict = {}
51 |
52 | #----------------------------------------------------------------------
53 | def writeCtaLog(self, content):
54 | """快速发出CTA模块日志事件"""
55 | log = VtLogData()
56 | log.logContent = content
57 | event = Event(type_=EVENT_CTA_LOG)
58 | event.dict_['data'] = log
59 | self.eventEngine.put(event)
60 |
61 | #----------------------------------------------------------------------
62 | def loadStrategy(self, setting):
63 | """载入策略"""
64 | try:
65 | name = setting['name']
66 | className = setting['className']
67 | except Exception, e:
68 | self.writeCtaLog(u'载入策略出错:%s' %e)
69 | return
70 |
71 | # 获取策略类
72 | strategyClass = STRATEGY_CLASS.get(className, None)
73 | if not strategyClass:
74 | self.writeCtaLog(u'找不到策略类:%s' %className)
75 | return
76 |
77 | # 防止策略重名
78 | if name in self.strategyDict:
79 | pass
80 | else:
81 | # 创建策略实例
82 | strategy = strategyClass(self, setting)
83 | self.strategyDict[name] = strategy
84 |
85 | #----------------------------------------------------------------------
86 | def updateStrategy(self, name):
87 | """更新策略配置"""
88 | if name in self.strategyDict:
89 | strategy = self.strategyDict[name]
90 |
91 | with open(self.settingFileName) as f:
92 | l = json.load(f)
93 | for setting in l:
94 | if setting[u'name'] == name:
95 | self.callStrategyFunc(strategy, strategy.onUpdate,setting)
96 |
97 | #----------------------------------------------------------------------
98 | def backtestStrategy(self, name, startTime = '20161001', endTime = '20161030', slippage = 0, mode = 'T'):
99 | """回测单个策略"""
100 | setting_bt = {}
101 | with open(self.settingFileName) as f:
102 | l = json.load(f)
103 | for setting in l:
104 | if setting[u'name'] == name:
105 | setting_bt = setting
106 | setting_bt['StartTime'] = startTime
107 | setting_bt['EndTime'] = endTime
108 | q = True if mode[0:2] == 'BV' else False
109 | xmode = 'bt-perf' if mode[0:2] in ['BP','TP'] else 'bt-f'
110 | xmode = 'bt-c' if mode[0:2] in ['BC','TC'] else xmode
111 | t = ctaTaskPool.taskPool.addTask('bt',args=(setting_bt, startTime, endTime, slippage, self.optimism, mode, q), mode = xmode, runmode = mode)
112 |
113 | #----------------------------------------------------------------------
114 | def backtestRollingStrategy(self, name, optimizationSetting, startTime = '20161001', endTime = '20161030', rollingDays=20, slippage = 0, mode = 'T'):
115 | """滚动优化回测单个策略"""
116 | setting_bt = {}
117 | with open(self.settingFileName) as f:
118 | l = json.load(f)
119 | for setting in l:
120 | if setting[u'name'] == name:
121 | setting_bt = setting
122 | setting_bt['StartTime'] = startTime
123 | setting_bt['EndTime'] = endTime
124 | q = True if mode == 'BV' else False
125 | ctaTaskPool.backtestingRollingE(setting_bt, optimizationSetting, startTime, endTime, rollingDays, slippage, self.optimism, mode, q)
126 | self.putStrategyEvent(name)
127 |
128 | #----------------------------------------------------------------------
129 | def backtestSplitStrategy(self, name, startTime = '20161001', endTime = '20161030', splitDays=20, slippage = 0, mode = 'T'):
130 | """分段回测单个策略"""
131 | setting_bt = {}
132 | with open(self.settingFileName) as f:
133 | l = json.load(f)
134 | for setting in l:
135 | if setting[u'name'] == name:
136 | setting_bt = setting
137 | q = True if mode == 'BV' else False
138 | xmode = 'bt-perf' if mode[0:2] in ['BP','TP'] else 'bt-f'
139 | xmode = 'bt-c' if mode[0:2] in ['BC','TC'] else xmode
140 | # 生成分段回测序列
141 | dataStartDate = datetime.strptime(startTime, '%Y%m%d') if len(startTime) == 8\
142 | else datetime.strptime(startTime, '%Y%m%d %H:%M:%S')
143 | dataEndDate = datetime.strptime(endTime, '%Y%m%d') if len(endTime) == 8\
144 | else datetime.strptime(endTime, '%Y%m%d %H:%M:%S')
145 | timeList = deque([])
146 | dataStepDate = dataStartDate
147 | while dataStepDate+timedelta(days=splitDays) <= dataEndDate:
148 | dataStepDate += timedelta(days=splitDays)
149 | timeList.append(dataStepDate)
150 | timeList.append(dataEndDate)
151 | dtSt = dataStartDate.strftime('%Y%m%d')
152 | while timeList:
153 | # 设置回测用的数据起始日期
154 | dtEd = timeList.popleft().strftime('%Y%m%d')
155 | setting_bt['StartTime'] = dtSt
156 | setting_bt['EndTime'] = dtEd
157 | t = ctaTaskPool.taskPool.addTask('bt',args=(copy.deepcopy(setting_bt), dtSt, dtEd, slippage, self.optimism, mode, q, False), mode = xmode, runmode = mode)
158 | dtSt = dtEd
159 |
160 | #----------------------------------------------------------------------
161 | def optimizeStrategy(self, name, optimizationSetting, startTime = '20161001', endTime = '20161030', slippage = 0, mode='T'):
162 | """参数扫描"""
163 | setting_bt = {}
164 | with open(self.settingFileName) as f:
165 | l = json.load(f)
166 | for setting in l:
167 | if setting[u'name'] == name:
168 | setting_bt = setting
169 | ctaTaskPool.optimizeE(setting_bt,optimizationSetting,startTime,endTime,slippage,self.optimism,mode)
170 | self.putStrategyEvent(name)
171 |
172 | #----------------------------------------------------------------------
173 | def reportStrategy(self):
174 | """策略组合报告"""
175 | strategyNames = []
176 | strategyBases = []
177 | for name,strategy in self.strategyDict.items():
178 | strategyNames.append(name)
179 | strategyBases.append(strategy.capital)
180 | rtn_table,cap_table = get_daily_rtn(strategyNames,strategyBases)
181 | weis = get_best_wei(rtn_table,30)
182 | print weis
183 | plotPortfolioCurve(cap_table,weis)
184 |
185 | #----------------------------------------------------------------------
186 | def saveSetting(self):
187 | """保存策略配置"""
188 | with open(self.settingFileName, 'w') as f:
189 | l = []
190 | for strategy in self.strategyDict.values():
191 | setting = {}
192 | for param in strategy.paramList:
193 | value = str(strategy.__getattribute__(param))
194 | if not param == 'name' and value.isdigit():
195 | value = eval(value)
196 | elif not param == 'name':
197 | try:
198 | value = float(value)
199 | except Exception, e:
200 | pass
201 | setting[param] = value
202 | l.append(setting)
203 |
204 | jsonL = json.dumps(l, indent=4)
205 | f.write(jsonL)
206 |
207 | #----------------------------------------------------------------------
208 | def loadSetting(self):
209 | """读取策略配置"""
210 | with open(self.settingFileName) as f:
211 | l = json.load(f)
212 | for setting in l:
213 | self.loadStrategy(setting)
214 |
215 | #----------------------------------------------------------------------
216 | def getStrategyVar(self, name):
217 | """获取策略当前的变量字典"""
218 | if name in self.strategyDict:
219 | strategy = self.strategyDict[name]
220 | varDict = OrderedDict()
221 | for key in strategy.varList:
222 | varDict[key] = strategy.__getattribute__(key)
223 | return varDict
224 | else:
225 | self.writeCtaLog(u'策略实例不存在:' + name)
226 | return None
227 |
228 | #----------------------------------------------------------------------
229 | def getStrategyParam(self, name):
230 | """获取策略的参数字典"""
231 | if name in self.strategyDict:
232 | strategy = self.strategyDict[name]
233 | paramDict = OrderedDict()
234 | for key in strategy.paramList:
235 | paramDict[key] = strategy.__getattribute__(key)
236 | return paramDict
237 | else:
238 | self.writeCtaLog(u'策略实例不存在:' + name)
239 | return None
240 |
241 | #----------------------------------------------------------------------
242 | def setStrategyParam(self, name, paramDict):
243 | """设置策略的参数字典"""
244 | if name in self.strategyDict:
245 | strategy = self.strategyDict[name]
246 | for key in strategy.paramList:
247 | strategy.__setattr__(key, paramDict[key])
248 | else:
249 | self.writeCtaLog(u'策略实例不存在:' + name)
250 | return None
251 |
252 | #----------------------------------------------------------------------
253 | def putStrategyEvent(self, name):
254 | """触发策略状态变化事件(通常用于通知GUI更新)"""
255 | event = Event(EVENT_CTA_STRATEGY+name)
256 | self.eventEngine.put(event)
257 |
258 | #----------------------------------------------------------------------
259 | def output(self, content):
260 | """输出内容"""
261 | self.writeCtaLog(content)
262 |
263 |
--------------------------------------------------------------------------------
/ctaFunction/__init__.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | """
3 | 包含一些CTA因子挖掘中常用的函数
4 | """
5 | from __future__ import division
6 |
7 | from dataFunction import *
8 | from visFunction import *
9 | from calcFunction import *
10 |
11 |
--------------------------------------------------------------------------------
/ctaFunction/calcFunction.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | import numba as nb
4 | import numpy as np
5 | import pandas as pd
6 | from ctaBase import *
7 | from datetime import timedelta
8 | from numpy import inf,nan,float32,float64
9 | from dataFunction import loadStrategyData
10 |
11 | #----------------------------------------------------------------------
12 | def formatNumber(n):
13 | """格式化数字到字符串"""
14 | rn = round(n, 2) # 保留两位小数
15 | return format(rn, ',') # 加上千分符
16 |
17 | #------------------------------------------------
18 | def calc_sharpe_ratio(returns, periods=250):
19 | """计算夏普比率"""
20 | return np.sqrt(periods) * (np.mean(returns)) / np.std(returns)
21 |
22 | #------------------------------------------------
23 | def calc_drawdowns(caps):
24 | """计算最大回测和最大回测周期"""
25 | hwm = [0]
26 | eq_idxs = caps.index.values
27 | eq_idx = len(caps.index.values)
28 | drawdown = pd.Series(index = range(1,eq_idx))
29 | duration = pd.Series(index = range(0,eq_idx))
30 | duration[0] = 0
31 | for t in range(1,eq_idx):
32 | cur_hwm = max(hwm[t-1], caps[eq_idxs[t]])
33 | hwm.append(cur_hwm)
34 | drawdown[t]= hwm[t] - caps[eq_idxs[t]]
35 | duration[t]= 0 if drawdown[t] == 0 else duration[t-1] + 1
36 | return drawdown.max(), int(duration.max())
37 |
38 | #------------------------------------------------
39 | def reshape_min(datas):
40 | """数据按分钟切片"""
41 | pnl_idxs = datas.index
42 | start = pnl_idxs[0].replace(second = 0,microsecond = 0)
43 | end = pnl_idxs[-1].replace(second = 0,microsecond = 0)
44 | minutes = int((end-start).total_seconds()/60)+2
45 | min_idx = [start + timedelta(minutes=i) for i in xrange(minutes)]
46 | index = 0
47 | datas_min = pd.Series(index = min_idx)
48 | for i in xrange(minutes):
49 | datas_min[start+timedelta(minutes=i)] = 0
50 | while index < len(pnl_idxs.values) and start+timedelta(minutes=i) > pnl_idxs[index]:
51 | datas_min[start+timedelta(minutes=i)] += datas[pnl_idxs.values[index]]
52 | index += 1
53 | return datas_min
54 |
55 | # 计算结算表现
56 | #------------------------------------------------
57 | def calcPerf(times,pnls,fees):
58 | """数据按分钟切片"""
59 | pnlList = [] # 每笔盈亏序列
60 | capital = 0 # 资金
61 | maxCapital = 0 # 资金最高净值
62 | drawdown = 0 # 回撤
63 |
64 | totalResult = 0 # 总成交数量
65 | totalCommission = 0 # 总手续费
66 |
67 | timeList = [] # 时间序列
68 | capitalList = [] # 盈亏汇总的时间序列
69 | drawdownList = [] # 回撤的时间序列
70 |
71 | winningResult = 0 # 盈利次数
72 | losingResult = 0 # 亏损次数
73 | totalWinning = 0 # 总盈利金额
74 | totalLosing = 0 # 总亏损金额
75 |
76 | for t,pnl,fee in zip(times,pnls,fees):
77 | if pnl !=0 :
78 | capital += pnl
79 | maxCapital = max(capital, maxCapital)
80 | drawdown = round(capital,2)
81 | pnlList.append(pnl)
82 |
83 | # 交易的时间戳使用平仓时间
84 | timeList.append(t)
85 | capitalList.append(capital)
86 | drawdownList.append(drawdown)
87 |
88 | totalResult += 1
89 | totalCommission += fee
90 |
91 | if pnl >= 0:
92 | winningResult += 1
93 | totalWinning += pnl
94 | else:
95 | losingResult += 1
96 | totalLosing += pnl
97 |
98 | # 计算盈亏相关数据
99 | averageWinning = 0
100 | averageLosing = 0
101 | profitLossRatio = 0
102 | winningRate = 0 if totalResult==0 else winningResult*1.0/totalResult*100
103 | averageWinning = 0 if winningResult==0 else totalWinning/winningResult
104 | averageLosing = 0 if losingResult==0 else totalLosing/losingResult
105 | profitLossRatio = 0 if averageLosing==0 else -averageWinning/averageLosing
106 |
107 | # 返回回测结果
108 | d = {}
109 | d['name'] = u'向量回测'
110 | d['capital'] = round(capital,2)
111 | d['maxCapital'] = maxCapital
112 | d['drawdown'] = drawdown
113 | d['totalResult'] = round(totalResult,2)
114 | d['totalCommission'] = round(totalCommission,2)
115 | d['timeList'] = timeList
116 | d['pnlList'] = pnlList
117 | d['capitalList'] = capitalList
118 | d['drawdownList'] = drawdownList
119 | d['winningRate'] = round(winningRate,2)
120 | d['averageWinning'] = round(averageWinning,2)
121 | d['averageLosing'] = round(averageLosing,2)
122 | d['profitLossRatio'] = round(profitLossRatio,2)
123 | d['datas'] = None
124 |
125 | return d
126 |
127 | @nb.autojit
128 | #------------------------------------------------
129 | def get_capital_np(markets,signals,size,commiRate,climit = 4, wlimit = 2, op=True):
130 | """使用numpy回测,标签的盈亏, op 表示是否延迟一个tick以后撮合"""
131 | postions = np.zeros(len(signals))
132 | actions = np.zeros(len(signals))
133 | costs = np.zeros(len(signals))
134 | pnls = np.zeros(len(signals))
135 | fees = np.zeros(len(signals))
136 | lastsignal = 0
137 | lastpos = 0
138 | lastcost = 0
139 | num = 0
140 | for num in range(1,len(signals)):
141 | postions[num] = lastpos
142 | actions[num] = 0
143 | costs[num] = lastcost
144 | pnls[num] = 0
145 | # 止盈止损
146 | if lastpos > 0 and \
147 | (markets[num,1]<=lastcost-climit or markets[num,1]>=lastcost+wlimit):
148 | postions[num] = 0
149 | actions[num] = -1
150 | costs[num] = 0
151 | fees[num] = (markets[num,1]+lastcost)*size*commiRate
152 | pnls[num] = (markets[num,1]-lastcost)*size-fees[num]
153 | elif lastpos < 0 and \
154 | (markets[num,0]>=lastcost+climit or markets[num,0]<=lastcost-wlimit):
155 | postions[num] = 0
156 | actions[num] = 1
157 | costs[num] = 0
158 | fees[num] = (markets[num,0]+lastcost)*size*commiRate
159 | pnls[num] = (lastcost-markets[num,0])*size-fees[num]
160 | # 开仓
161 | if op:
162 | lastsignal = signals[num]
163 | if lastsignal > 0 and lastpos == 0:
164 | postions[num] = 1
165 | actions[num] = 1
166 | costs[num] = markets[num,0]
167 | elif lastsignal < 0 and lastpos == 0:
168 | postions[num] = -1
169 | actions[num] = -1
170 | costs[num] = markets[num,1]
171 | lastpos = postions[num]
172 | lastcost = costs[num]
173 | lastsignal = signals[num]
174 | return pnls,actions,fees
175 |
176 | #------------------------------------------------
177 | def get_perf(datas,signals,size,commiRate):
178 | """并行回测,标签的盈亏"""
179 |
180 | # 计算交易信号,并整理数据
181 | predatas = datas[datas.columns.drop(['askPrice1','bidPrice1'])].values
182 | datas = datas[['askPrice1','bidPrice1']]
183 | datas['signals'] = signals
184 |
185 | # 计算仓位信息
186 | datas = datas[datas['signals']!=0]
187 | datas['position'] = datas['signals'].diff()
188 | datas = datas.dropna(axis=0, how='any')
189 | posInfo = datas[datas['position']!=0]
190 |
191 | # 根据仓位信息计算开仓成本
192 | posInfo['cost'] = posInfo.apply(lambda x:x['position']*x['askPrice1'] if x['position']>0 else -x['position']*x['bidPrice1'],axis=1)
193 |
194 | # 计算每笔的手续费,第一笔被重复计算
195 | posInfo['feeP'] = abs(posInfo['cost'])*size*commiRate
196 |
197 | # 计算每笔盈亏,第一笔是错误的
198 | posInfo['pnl'] = (posInfo['cost']-posInfo['cost'].shift(1))/2
199 | posInfo['pnl'] = posInfo.apply(lambda x:-x['pnl'] if x['position']>0 else x['pnl'],axis=1) - (posInfo['feeP'] + posInfo['feeP'].shift(1))/2
200 |
201 | # 修正错误信息
202 | posInfo['position'].iloc[0] = posInfo['position'].iloc[0]/2
203 | posInfo['position'].iloc[-1] = posInfo['position'].iloc[-1]/2
204 | posInfo['pnl'][0] = 0
205 |
206 | # 计算总手续费和总资金
207 | posInfo['fee'] = posInfo.apply(np.cumsum)['feeP']
208 | posInfo['cap'] = posInfo.apply(np.cumsum)['pnl']
209 | pnl_min = reshape_min(posInfo['pnl'])
210 | cap_min = reshape_min(posInfo['cap'])
211 | period = posInfo['pnl'].count()
212 | mdd, ddt = calc_drawdowns(posInfo['cap'])
213 | return posInfo['cap'][-1],mdd
214 |
215 | #------------------------------------------------
216 | def get_daily_rtn(strategyNames,strategyBases,startDate='20100101',endDate='20181030'):
217 | """获取每日盈亏"""
218 | fields = ['name','date','pnl']
219 | rtns = pd.DataFrame()
220 | caps = pd.DataFrame()
221 | for name,base in zip(strategyNames,strategyBases):
222 | datas = loadStrategyData(CAPITAL_DB_NAME,name,startDate,endDate,fields)
223 | datas['pnl']=datas['pnl']/base
224 | rtns = pd.concat([rtns,datas],axis = 0)
225 | datas=datas.set_index('date')
226 | datas['cap']=datas.apply(np.cumsum)['pnl']
227 | #datas.plot(kind='line',title = name)
228 | datas.reset_index(drop=False,inplace=True)
229 | caps = pd.concat([caps,datas],axis = 0)
230 | rtn_table = pd.crosstab(rtns['date'],rtns['name'], values = rtns['pnl'], aggfunc = sum) # 一维表变为二维表
231 | rtn_table.fillna(0, inplace = True)
232 | cap_table = pd.crosstab(caps['date'],caps['name'], values = caps['cap'], aggfunc = sum) # 一维表变为二维表
233 | cap_table.fillna(method='pad', inplace = True) # 将NaN置换为0
234 | cap_table.fillna(0, inplace = True) # 将NaN置换为0
235 | return rtn_table,cap_table
236 | #plt.show()
237 | #cap_table.head(20)
238 |
239 |
240 | #------------------------------------------------
241 | def get_best_wei(rtn_table,risk_aversion):
242 | """获取指定风险厌恶系数下的最优策略配置组合"""
243 | from cvxopt import matrix, solvers
244 | cov_mat = rtn_table.cov() * 250 # 协方差矩阵(1年250个交易日)
245 | exp_rtn = rtn_table.mean() * 250 # 标的预期收益(1年250个交易日)
246 | P = risk_aversion * matrix(cov_mat.values)
247 | q = -1 * matrix(exp_rtn.values)
248 | G = matrix(np.vstack((np.diag(np.ones(len(exp_rtn))),np.diag(-np.ones(len(exp_rtn))))))
249 | h = matrix(np.array([np.ones(len(exp_rtn)),np.zeros(len(exp_rtn))]).reshape(len(exp_rtn)*2,1))
250 | A = matrix(np.ones(len(exp_rtn)),(1,len(exp_rtn)))
251 | b = matrix([1.0])
252 | solvers.options['show_progress'] = True
253 | sol = solvers.qp(P, q, G, h, A, b)
254 | weis=pd.DataFrame(index=exp_rtn.index,data = np.round(sol['x'],2), columns = ['weight']) # 权重精确到小数点后两位
255 | return weis
256 |
257 |
--------------------------------------------------------------------------------
/ctaFunction/dataFunction.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/ctaFunction/dataFunction.py
--------------------------------------------------------------------------------
/ctaFunction/visFunction.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | """
3 | 包含一些CTA因子的可视化函数
4 | """
5 | import numpy as np
6 | import pandas as pd
7 | import seaborn as sns
8 | import matplotlib
9 | matplotlib.use('Qt4Agg')
10 | #import matplotlib as mpl
11 | #mpl.rcParams["font.sans-serif"] = ["Microsoft YaHei"]#
12 | #mpl.rcParams['axes.unicode_minus'] = False
13 | import matplotlib.pyplot as plt
14 | from calcFunction import get_capital_np,calcPerf,formatNumber
15 |
16 | #----------------------------------------------------------------------
17 | def showBtResult(d):
18 | """
19 | 显示回测结果
20 | """
21 | name = d.get('name')
22 | timeList = d['timeList']
23 | pnlList = d['pnlList']
24 | capitalList = d['capitalList']
25 | drawdownList = d['drawdownList']
26 |
27 | print(u'显示回测结果')
28 | # 输出
29 | if len(timeList)>0:
30 | print('-' * 30)
31 | print(u'第一笔交易:\t%s' % d['timeList'][0])
32 | print(u'最后一笔交易:\t%s' % d['timeList'][-1])
33 |
34 | print(u'总交易次数:\t%s' % formatNumber(d['totalResult']))
35 | print(u'总盈亏:\t%s' % formatNumber(d['capital']))
36 | print(u'最大回撤: \t%s' % formatNumber(min(d['drawdownList'])))
37 |
38 | print(u'平均每笔盈亏:\t%s' %formatNumber(d['capital']/d['totalResult']))
39 | print(u'平均每笔佣金:\t%s' %formatNumber(d['totalCommission']/d['totalResult']))
40 |
41 | print(u'胜率\t\t%s%%' %formatNumber(d['winningRate']))
42 | print(u'平均每笔盈利\t%s' %formatNumber(d['averageWinning']))
43 | print(u'平均每笔亏损\t%s' %formatNumber(d['averageLosing']))
44 | print(u'盈亏比:\t%s' %formatNumber(d['profitLossRatio']))
45 | print(u'显示回测结果')
46 |
47 | # 绘图
48 | import matplotlib.pyplot as plt
49 | from matplotlib.dates import AutoDateLocator, DateFormatter
50 | autodates = AutoDateLocator()
51 | yearsFmt = DateFormatter('%m-%d')
52 |
53 | pCapital = plt.subplot(3, 1, 1)
54 | pCapital.set_ylabel("capital")
55 | pCapital.plot(timeList,capitalList)
56 | plt.gcf().autofmt_xdate() #设置x轴时间外观
57 | plt.gcf().subplots_adjust(bottom=0.1)
58 | plt.gca().xaxis.set_major_locator(autodates) #设置时间间隔
59 | plt.gca().xaxis.set_major_formatter(yearsFmt) #设置时间显示格式
60 |
61 | pDD = plt.subplot(3, 1, 2)
62 | pDD.set_ylabel("dd")
63 | pDD.bar(range(len(drawdownList)), drawdownList)
64 |
65 | pPnl = plt.subplot(3, 1, 3)
66 | pPnl.set_ylabel("pnl")
67 | pPnl.hist(pnlList, bins=20)
68 |
69 | plt.subplots_adjust(bottom=0.05,hspace=0.3)
70 | plt.show()
71 |
72 | #----------------------------------------------------------------------
73 | def plotSigCaps(timeList,signals,markets,climit=4,wlimit=2,size=1,rate=0.0001,op=True):
74 | """
75 | 打印某一个信号的资金曲线
76 | """
77 | plt.close()
78 | pnls,poss,fees = get_capital_np(markets,signals,size,rate,\
79 | climit=climit, wlimit=wlimit,op=op)
80 | d = calcPerf(timeList,pnls,fees)
81 | showBtResult(d)
82 |
83 | #----------------------------------------------------------------------
84 | def plotFactors(datas,factors=None):
85 | """打印因子数据到一张图上"""
86 | plt.close()
87 | factors = datas.columns.tolist() if factors is None else factors
88 | if 'pnl' in factors:
89 | factors.remove('pnl')
90 | sns.pairplot(datas, vars=factors, hue="pnl", size=1.5)
91 | plt.show()
92 |
93 | #----------------------------------------------------------------------
94 | def plotSigHeats(signals,markets,start=0,step=2,size=1,iters=6):
95 | """
96 | 打印信号回测盈损热度图,寻找参数稳定岛
97 | """
98 | sigMat = pd.DataFrame(index=range(iters),columns=range(iters))
99 | for i in range(iters):
100 | for j in range(iters):
101 | climit = start + i*step
102 | wlimit = start + j*step
103 | pnls,poss,fees = get_capital_np(markets,signals,size,0.0001,climit=climit,wlimit=wlimit,op=False)
104 | caps = np.cumsum(pnls[pnls!=0])
105 | sigMat[i][j] = caps[-1]
106 | #ratioDict = {}
107 | #for i in range(iters):
108 | # for j in range(iters):
109 | # climit = start + i*step
110 | # wlimit = start + j*step
111 | # ratio = wlimit/climit
112 | # if ratio in ratioDict:
113 | # ratioDict[ratio] += sigMat[i][j]
114 | # else:
115 | # ratioDict[ratio] = sigMat[i][j]
116 | #ratioList = []
117 | #capList = []
118 | #ratioDict0 = sorted(ratioDict.iteritems(),key=lambda d:d[0])
119 | #for k,v in ratioDict0:
120 | # ratioList.append(k)
121 | # capList.append(v)
122 | #plt.plot(ratioList,capList)
123 | #plt.xlabel(u'盈亏比')
124 | #plt.ylabel(u'盈利')
125 | sns.heatmap(sigMat.values.astype(np.float64),annot=True,fmt='.2f',annot_kws={"weight": "bold"})
126 | xTicks = [i+0.5 for i in range(iters)]
127 | yTicks = [iters-i-0.5 for i in range(iters)]
128 | xyLabels = [str(start+i*step) for i in range(iters)]
129 | _, labels = plt.yticks(yTicks,xyLabels)
130 | plt.setp(labels, rotation=0)
131 | _, labels = plt.xticks(xTicks,xyLabels)
132 | plt.setp(labels, rotation=90)
133 | plt.xlabel('LossStop @')
134 | plt.ylabel('ProfitTarget @')
135 | return sigMat
136 |
137 | #----------------------------------------------------------------------
138 | def plotVarVPnl(pdData,s):
139 | """
140 | 展示策略状态的10分位盈亏
141 | """
142 | data = pdData.copy()
143 | minV = min(data[s])
144 | maxV = max(data[s])
145 | data[s+'_split'] = data[s].apply(lambda x:round(x*10/(maxV-minV))*(maxV-minV)/10)
146 | data.groupby(s+'_split').pnl.sum().plot(kind='bar')
147 | plt.show()
148 |
149 | #------------------------------------------------
150 | def plotPortfolioCurve(cap_table,weis):
151 | """输出策略组合的资金曲线"""
152 | plt.close()
153 | names = weis.index.tolist()
154 | cap_table['tol']=1
155 | for n in names:
156 | cap_table['tol'] += cap_table[n]*weis['weight'][n]
157 | cap_table['tol'].plot()
158 | plt.show()
159 |
--------------------------------------------------------------------------------
/ctaSetting.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 在本文件中引入所有希望在系统中使用的策略类
5 |
6 | 这个字典中保存了需要运行的策略的名称和策略类的映射关系,
7 | 用户的策略类写好后,先在该文件中引入,并设置好名称,然后
8 | 在CTA_setting.json中写入具体每个策略对象的类和合约设置。
9 | '''
10 | import os
11 | import sys
12 | import re
13 | import imp
14 | import glob
15 | import talib
16 | sys.path.append('./strategy')
17 | allfile=[]
18 | def getallfile(path):
19 | allfilelist=os.listdir(path)
20 | for f in allfilelist:
21 | filepath=os.path.join(path,f)
22 | #判断是不是文件夹
23 | if os.path.isdir(filepath):
24 | getallfile(filepath)
25 | allfile.append(filepath)
26 | return allfile
27 | pattern = re.compile(r"[^*]*.py$")
28 | # 导入所有策略
29 | ALL_STRATEGIES = []
30 | strategyPath = os.getcwd() + '/strategy/'
31 | #allPath = glob.glob(strategyPath+r'*.py')
32 | allPath = getallfile(strategyPath)
33 | for path in allPath:
34 | if pattern.match(path):
35 | fileName = path.split("\\")[-1]
36 | modelName = fileName.split("/")[-1].split(".")[0]
37 | if not modelName == '__init__':
38 | ALL_STRATEGIES.append(modelName)
39 | imp.load_source("strategy", path)
40 | STRATEGY_CLASS = {}
41 | for s in ALL_STRATEGIES:
42 | cls_obj = getattr(sys.modules['strategy'], s)
43 | STRATEGY_CLASS[s] = cls_obj
44 |
--------------------------------------------------------------------------------
/ctaTask.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | '''
3 | 本文件中包含的是回测任务模块,用于回测任务的管理。
4 | '''
5 | import os
6 | from datetime import datetime
7 | import multiprocessing
8 | ########################################################################
9 | def openLog(name):
10 | os.system('notepad log\{}.log'.format(name))
11 |
12 | ########################################################################
13 | class ctaTask(multiprocessing.Process):
14 | """
15 | 回测任务进程对象
16 | """
17 | #----------------------------------------------------------------------
18 | def __init__(self, name='', target= None, args=(), kwargs={},mode='front',showfunc=None,outq=None,runmode='bar'):
19 | """构造函数"""
20 | super(ctaTask,self).__init__(target=target,args=args,kwargs=kwargs)
21 | self.outq = outq
22 | self.name = name
23 | self.mode = mode
24 | self.runmode = runmode
25 | self.startTM = None
26 | self.runTM = None
27 | self.showfunc = showfunc
28 | if isinstance(args[0],dict) and 'name' in args[0]:
29 | args[0]['name'] = '.'.join([self.name,args[0]['name']])
30 | self.setting = args[0]
31 | else:
32 | self.setting = {}
33 | self.results = {}
34 | self.state = u'等待中'
35 |
36 | #----------------------------------------------------------------------
37 | def stopTask(self):
38 | """停止任务"""
39 | self.state = u'已停止'
40 | try:
41 | self.runTM = datetime.now()-self.startTM
42 | self.terminate()
43 | except:
44 | self.runTM = None
45 |
46 | #----------------------------------------------------------------------
47 | def startTask(self):
48 | """开始任务"""
49 | self.startTM = datetime.now()
50 | self.daemon = True
51 | self.start()
52 |
53 | #----------------------------------------------------------------------
54 | def update(self,setting,results,state):
55 | """更新任务完成"""
56 | if results:
57 | self.setting,self.results = setting,results
58 | self.state = state
59 | if self.state == u'已完成':
60 | self.runTM = datetime.now()-self.startTM
61 |
62 | #----------------------------------------------------------------------
63 | def run(self):
64 | """任务函数"""
65 | if self._target:
66 | setting,results = self._target(*self._args, **self._kwargs)
67 | self.outq.put(tuple([self.name,setting,results]))
68 |
69 | #----------------------------------------------------------------------
70 | def show(self):
71 | """显示结果"""
72 | if self.state == u'已完成':
73 | self.showfunc(self.results)
74 |
75 | #----------------------------------------------------------------------
76 | def log(self):
77 | """显示结果"""
78 | if self.state == u'已完成':
79 | p = multiprocessing.Process(target = openLog, args=(self.setting.get('name'),))
80 | p.start()
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/ctaTaskPool.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | '''
3 | 本文件中包含的是回测任务模块,用于回测任务的管理。
4 | '''
5 | from Queue import Queue, Empty
6 | import cProfile,pstats
7 | import traceback
8 | import multiprocessing
9 | from ctaTask import ctaTask
10 | from threading import Thread
11 | from ctaBacktesting import backtesting,optimize,showBtResult,runParallelOptimization,backtestingRolling
12 |
13 | # 无日志回测单个策略
14 | #---------------------------------------------------------------------------------------
15 | def optimizeB(setting_bt, StartTime = '', EndTime = '', slippage = 0, optimism = False, mode = 'T', q = False):
16 | """回测单个策略"""
17 | try:
18 | return optimize(setting_bt, {}, StartTime, EndTime, slippage, optimism, mode)
19 | except Exception, e:
20 | print(u'回测策略出错:%s' %e)
21 | print 'traceback.print_exc():'; traceback.print_exc()
22 | return setting_bt['name'],{},0
23 |
24 | # 含日志回测单个策略
25 | #---------------------------------------------------------------------------------------
26 | def backtestingE(setting_bt, StartTime = '', EndTime = '', slippage = 0, optimism = False, mode = 'T', q = False, plot = True):
27 | """回测单个策略"""
28 | try:
29 | return backtesting(setting_bt, StartTime, EndTime, slippage, optimism, mode, q, plot=plot)
30 | except Exception, e:
31 | print(u'回测策略出错:%s' %e)
32 | print 'traceback.print_exc():'; traceback.print_exc()
33 | return setting_bt['name'],{},0
34 |
35 | # 使用C++引擎回测单个策略
36 | #---------------------------------------------------------------------------------------
37 | def backtestingC(setting_bt, StartTime = '', EndTime = '', slippage = 0, optimism = False, mode = 'T', q = False, plot = True):
38 | """回测单个策略"""
39 | try:
40 | return backtesting(setting_bt, StartTime, EndTime, slippage, optimism, mode, q, runmode='CPP', plot=plot)
41 | except Exception, e:
42 | print(u'回测策略出错:%s' %e)
43 | print 'traceback.print_exc():'; traceback.print_exc()
44 | return setting_bt['name'],{},0
45 |
46 | # 无日志回测单个策略,同时分析性能
47 | #---------------------------------------------------------------------------------------
48 | def backtestingPerfE(setting_bt, StartTime = '', EndTime = '', slippage = 0, optimism = False, mode = 'T', q = False):
49 | """回测单个策略,同时分析性能"""
50 | try:
51 | p = cProfile.Profile()
52 | p.enable()
53 | res = backtesting(setting_bt, StartTime, EndTime, slippage, optimism, mode, q)
54 | p.disable()
55 | p.create_stats()
56 | stats = pstats.Stats(p)
57 | stats.sort_stats('tottime').print_stats(20)
58 | return res
59 | except Exception, e:
60 | print(u'回测策略出错:%s' %e)
61 | print 'traceback.print_exc():'; traceback.print_exc()
62 | return setting_bt['name'],{},0
63 |
64 | # 滚动分析,回测单个策略
65 | #---------------------------------------------------------------------------------------
66 | def backtestingRollingE(setting_bt, optimizationSetting, StartTime = '', EndTime = '', RollingDays = 20, slippage = 0, optimism = False, mode = 'T', q = False):
67 | """滚动回测单个策略"""
68 | try:
69 | return backtestingRolling(setting_bt, optimizationSetting, StartTime, EndTime, RollingDays, slippage, optimism, mode, q)
70 | except Exception, e:
71 | print(u'回测策略出错:%s' %e)
72 | print 'traceback.print_exc():'; traceback.print_exc()
73 | return setting_bt['name'],{},0
74 |
75 | # 参数扫描
76 | #---------------------------------------------------------------------------------------
77 | def optimizeE(setting_bt, optimizationSetting, StartTime = '20161001', EndTime = '20161030', slippage = 0, optimism = False, mode = 'T',q=False):
78 | """对策略参数扫描"""
79 | try:
80 | return runParallelOptimization(setting_bt,optimizationSetting,optimism,StartTime,EndTime,slippage,mode)
81 | except Exception, e:
82 | print(u'回测策略出错:%s' %e)
83 | print 'traceback.print_exc():'; traceback.print_exc()
84 | return setting_bt['name'],{},0
85 |
86 | taskPool = None
87 | ########################################################################
88 | class ctaTaskPool(object):
89 | """
90 | 回测任务池
91 | """
92 | MAX_RUNNING_TASK = 4
93 | taskCount = 0
94 | allTask = {}
95 | #----------------------------------------------------------------------
96 | def __init__(self,ee=None):
97 | """构造函数"""
98 | self.ee = ee
99 | self.inq = Queue()
100 | self.outq = multiprocessing.Queue()
101 | self.thread = Thread(target=self.runTask)
102 | self.__active = False
103 | self.workingTask = []
104 | self.startTaskPool()
105 |
106 | #---------------------------------------------------------------------------------------
107 | def runTask(self):
108 | """监听函数"""
109 | while self.__active:
110 | try:
111 | if len(self.workingTask) < self.MAX_RUNNING_TASK:
112 | task = self.inq.get(block=True ,timeout=1)
113 | task.update(None,None,u'运行中')
114 | task.startTask()
115 | self.workingTask.append(task)
116 | except Empty:
117 | pass
118 | try:
119 | name,setting,results = self.outq.get(block=True ,timeout=1)
120 | self.allTask.get(name).update(setting,results,u'已完成')
121 | self.allTask.get(name).terminate()
122 | self.workingTask.remove(self.allTask.get(name))
123 | except Empty:
124 | pass
125 |
126 | #-----------------------------------------------
127 | def startTaskPool(self):
128 | """开始监听任务队列"""
129 | self.__active = True
130 | self.thread.start()
131 |
132 | #-----------------------------------------------
133 | def stopTaskPool(self):
134 | """结束监听任务队列"""
135 | self.__active = False
136 | self.thread.join()
137 |
138 | # 任务相关
139 | #---------------------------------------------------------------------------------------
140 | def addTask(self, name='', args=(), kwargs={}, mode='bt-f', runmode = 'bar'):
141 | """新增任务"""
142 | name0 = '-'.join([name,str(self.taskCount)])
143 | target = backtestingE if mode == 'bt-f' else \
144 | backtestingC if mode == 'bt-c' else\
145 | backtestingPerfE if mode == 'bt-perf' else\
146 | optimizeE if mode == 'op' else\
147 | backtestingRollingE if mode == 'bt-r' else optimizeB
148 | self.allTask[name0] = ctaTask(name0, target, args, kwargs, mode, showfunc = showBtResult, outq=self.outq, runmode=runmode)
149 | self.inq.put(self.allTask[name0])
150 | self.taskCount +=1
151 | return name0
152 |
153 | #---------------------------------------------------------------------------------------
154 | def getTask(self, name=''):
155 | """获取任务"""
156 | return self.allTask[name]
157 |
158 | #---------------------------------------------------------------------------------------
159 | def startTask(self, name=''):
160 | """停止任务"""
161 | self.allTask[name].startTask()
162 |
163 | #---------------------------------------------------------------------------------------
164 | def stopTask(self, name=''):
165 | """停止任务"""
166 | self.allTask[name].stopTask()
167 | if self.allTask.get(name) in self.workingTask:
168 | self.workingTask.remove(self.allTask.get(name))
169 |
170 |
--------------------------------------------------------------------------------
/eventEngine.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | # 系统模块
4 | from Queue import Queue, Empty
5 | from threading import Thread
6 | from time import sleep
7 | from collections import defaultdict
8 |
9 | # 第三方模块
10 | from qtpy.QtCore import QTimer
11 |
12 | # 自己开发的模块
13 | from eventType import *
14 |
15 |
16 | ########################################################################
17 | class EventEngine(object):
18 | """
19 | 事件驱动引擎
20 | 事件驱动引擎中所有的变量都设置为了私有,这是为了防止不小心
21 | 从外部修改了这些变量的值或状态,导致bug。
22 |
23 | 变量说明
24 | __queue:私有变量,事件队列
25 | __active:私有变量,事件引擎开关
26 | __thread:私有变量,事件处理线程
27 | __timer:私有变量,计时器
28 | __handlers:私有变量,事件处理函数字典
29 |
30 |
31 | 方法说明
32 | __run: 私有方法,事件处理线程连续运行用
33 | __process: 私有方法,处理事件,调用注册在引擎中的监听函数
34 | __onTimer:私有方法,计时器固定事件间隔触发后,向事件队列中存入计时器事件
35 | start: 公共方法,启动引擎
36 | stop:公共方法,停止引擎
37 | register:公共方法,向引擎中注册监听函数
38 | unregister:公共方法,向引擎中注销监听函数
39 | put:公共方法,向事件队列中存入新的事件
40 |
41 | 事件监听函数必须定义为输入参数仅为一个event对象,即:
42 |
43 | 函数
44 | def func(event)
45 | ...
46 |
47 | 对象方法
48 | def method(self, event)
49 | ...
50 |
51 | """
52 |
53 | #----------------------------------------------------------------------
54 | def __init__(self):
55 | """初始化事件引擎"""
56 | # 事件队列
57 | self.__queue = Queue()
58 |
59 | # 事件引擎开关
60 | self.__active = False
61 |
62 | # 事件处理线程
63 | self.__thread = Thread(target = self.__run)
64 |
65 | # 计时器,用于触发计时器事件
66 | self.__timer = QTimer()
67 | self.__timer.timeout.connect(self.__onTimer)
68 |
69 | # 这里的__handlers是一个字典,用来保存对应的事件调用关系
70 | # 其中每个键对应的值是一个列表,列表中保存了对该事件进行监听的函数功能
71 | self.__handlers = defaultdict(list)
72 |
73 | # __generalHandlers是一个列表,用来保存通用回调函数(所有事件均调用)
74 | self.__generalHandlers = []
75 |
76 | #----------------------------------------------------------------------
77 | def __run(self):
78 | """引擎运行"""
79 | while self.__active == True:
80 | try:
81 | event = self.__queue.get(block = True, timeout = 1) # 获取事件的阻塞时间设为1秒
82 | self.__process(event)
83 | except Empty:
84 | pass
85 |
86 | #----------------------------------------------------------------------
87 | def __process(self, event):
88 | """处理事件"""
89 | # 检查是否存在对该事件进行监听的处理函数
90 | if event.type_ in self.__handlers:
91 | # 若存在,则按顺序将事件传递给处理函数执行
92 | [handler(event) for handler in self.__handlers[event.type_]]
93 |
94 | # 以上语句为Python列表解析方式的写法,对应的常规循环写法为:
95 | #for handler in self.__handlers[event.type_]:
96 | #handler(event)
97 |
98 | # 调用通用处理函数进行处理
99 | if self.__generalHandlers:
100 | [handler(event) for handler in self.__generalHandlers]
101 |
102 | #----------------------------------------------------------------------
103 | def __onTimer(self):
104 | """向事件队列中存入计时器事件"""
105 | # 创建计时器事件
106 | event = Event(type_=EVENT_TIMER)
107 |
108 | # 向队列中存入计时器事件
109 | self.put(event)
110 |
111 | #----------------------------------------------------------------------
112 | def start(self, timer=True):
113 | """
114 | 引擎启动
115 | timer:是否要启动计时器
116 | """
117 | # 将引擎设为启动
118 | self.__active = True
119 |
120 | # 启动事件处理线程
121 | self.__thread.start()
122 |
123 | # 启动计时器,计时器事件间隔默认设定为1秒
124 | if timer:
125 | self.__timer.start(1000)
126 |
127 | #----------------------------------------------------------------------
128 | def stop(self):
129 | """停止引擎"""
130 | # 将引擎设为停止
131 | self.__active = False
132 |
133 | # 停止计时器
134 | self.__timer.stop()
135 |
136 | # 等待事件处理线程退出
137 | self.__thread.join()
138 |
139 | #----------------------------------------------------------------------
140 | def register(self, type_, handler):
141 | """注册事件处理函数监听"""
142 | # 尝试获取该事件类型对应的处理函数列表,若无defaultDict会自动创建新的list
143 | handlerList = self.__handlers[type_]
144 |
145 | # 若要注册的处理器不在该事件的处理器列表中,则注册该事件
146 | if handler not in handlerList:
147 | handlerList.append(handler)
148 |
149 | #----------------------------------------------------------------------
150 | def unregister(self, type_, handler):
151 | """注销事件处理函数监听"""
152 | # 尝试获取该事件类型对应的处理函数列表,若无则忽略该次注销请求
153 | handlerList = self.__handlers[type_]
154 |
155 | # 如果该函数存在于列表中,则移除
156 | if handler in handlerList:
157 | handlerList.remove(handler)
158 |
159 | # 如果函数列表为空,则从引擎中移除该事件类型
160 | if not handlerList:
161 | del self.__handlers[type_]
162 |
163 | #----------------------------------------------------------------------
164 | def put(self, event):
165 | """向事件队列中存入事件"""
166 | self.__queue.put(event)
167 |
168 | #----------------------------------------------------------------------
169 | def registerGeneralHandler(self, handler):
170 | """注册通用事件处理函数监听"""
171 | if handler not in self.__generalHandlers:
172 | self.__generalHandlers.append(handler)
173 |
174 | #----------------------------------------------------------------------
175 | def unregisterGeneralHandler(self, handler):
176 | """注销通用事件处理函数监听"""
177 | if handler in self.__generalHandlers:
178 | self.__generalHandlers.remove(handler)
179 |
180 |
181 |
182 | ########################################################################
183 | class EventEngine2(object):
184 | """
185 | 计时器使用python线程的事件驱动引擎
186 | """
187 |
188 | #----------------------------------------------------------------------
189 | def __init__(self):
190 | """初始化事件引擎"""
191 | # 事件队列
192 | self.__queue = Queue()
193 |
194 | # 事件引擎开关
195 | self.__active = False
196 |
197 | # 事件处理线程
198 | self.__thread = Thread(target = self.__run)
199 |
200 | # 计时器,用于触发计时器事件
201 | self.__timer = Thread(target = self.__runTimer)
202 | self.__timerActive = False # 计时器工作状态
203 | self.__timerSleep = 1 # 计时器触发间隔(默认1秒)
204 |
205 | # 这里的__handlers是一个字典,用来保存对应的事件调用关系
206 | # 其中每个键对应的值是一个列表,列表中保存了对该事件进行监听的函数功能
207 | self.__handlers = defaultdict(list)
208 |
209 | # __generalHandlers是一个列表,用来保存通用回调函数(所有事件均调用)
210 | self.__generalHandlers = []
211 |
212 | #----------------------------------------------------------------------
213 | def __run(self):
214 | """引擎运行"""
215 | while self.__active == True:
216 | try:
217 | event = self.__queue.get(block = True, timeout = 1) # 获取事件的阻塞时间设为1秒
218 | self.__process(event)
219 | except Empty:
220 | pass
221 |
222 | #----------------------------------------------------------------------
223 | def __process(self, event):
224 | """处理事件"""
225 | # 检查是否存在对该事件进行监听的处理函数
226 | if event.type_ in self.__handlers:
227 | # 若存在,则按顺序将事件传递给处理函数执行
228 | [handler(event) for handler in self.__handlers[event.type_]]
229 |
230 | # 以上语句为Python列表解析方式的写法,对应的常规循环写法为:
231 | #for handler in self.__handlers[event.type_]:
232 | #handler(event)
233 |
234 | # 调用通用处理函数进行处理
235 | if self.__generalHandlers:
236 | [handler(event) for handler in self.__generalHandlers]
237 |
238 | #----------------------------------------------------------------------
239 | def __runTimer(self):
240 | """运行在计时器线程中的循环函数"""
241 | while self.__timerActive:
242 | # 创建计时器事件
243 | event = Event(type_=EVENT_TIMER)
244 |
245 | # 向队列中存入计时器事件
246 | self.put(event)
247 |
248 | # 等待
249 | sleep(self.__timerSleep)
250 |
251 | #----------------------------------------------------------------------
252 | def start(self, timer=True):
253 | """
254 | 引擎启动
255 | timer:是否要启动计时器
256 | """
257 | # 将引擎设为启动
258 | self.__active = True
259 |
260 | # 启动事件处理线程
261 | self.__thread.start()
262 |
263 | # 启动计时器,计时器事件间隔默认设定为1秒
264 | if timer:
265 | self.__timerActive = True
266 | self.__timer.start()
267 |
268 | #----------------------------------------------------------------------
269 | def stop(self):
270 | """停止引擎"""
271 | # 将引擎设为停止
272 | self.__active = False
273 |
274 | # 停止计时器
275 | self.__timerActive = False
276 | self.__timer.join()
277 |
278 | # 等待事件处理线程退出
279 | self.__thread.join()
280 |
281 | #----------------------------------------------------------------------
282 | def register(self, type_, handler):
283 | """注册事件处理函数监听"""
284 | # 尝试获取该事件类型对应的处理函数列表,若无defaultDict会自动创建新的list
285 | handlerList = self.__handlers[type_]
286 |
287 | # 若要注册的处理器不在该事件的处理器列表中,则注册该事件
288 | if handler not in handlerList:
289 | handlerList.append(handler)
290 |
291 | #----------------------------------------------------------------------
292 | def unregister(self, type_, handler):
293 | """注销事件处理函数监听"""
294 | # 尝试获取该事件类型对应的处理函数列表,若无则忽略该次注销请求
295 | handlerList = self.__handlers[type_]
296 |
297 | # 如果该函数存在于列表中,则移除
298 | if handler in handlerList:
299 | handlerList.remove(handler)
300 |
301 | # 如果函数列表为空,则从引擎中移除该事件类型
302 | if not handlerList:
303 | del self.__handlers[type_]
304 |
305 | #----------------------------------------------------------------------
306 | def put(self, event):
307 | """向事件队列中存入事件"""
308 | self.__queue.put(event)
309 |
310 | #----------------------------------------------------------------------
311 | def registerGeneralHandler(self, handler):
312 | """注册通用事件处理函数监听"""
313 | if handler not in self.__generalHandlers:
314 | self.__generalHandlers.append(handler)
315 |
316 | #----------------------------------------------------------------------
317 | def unregisterGeneralHandler(self, handler):
318 | """注销通用事件处理函数监听"""
319 | if handler in self.__generalHandlers:
320 | self.__generalHandlers.remove(handler)
321 |
322 |
323 | ########################################################################
324 | class Event:
325 | """事件对象"""
326 |
327 | #----------------------------------------------------------------------
328 | def __init__(self, type_=None):
329 | """Constructor"""
330 | self.type_ = type_ # 事件类型
331 | self.dict_ = {} # 字典用于保存具体的事件数据
332 |
333 |
334 | #----------------------------------------------------------------------
335 | def test():
336 | """测试函数"""
337 | import sys
338 | from datetime import datetime
339 | from PyQt4.QtCore import QCoreApplication
340 |
341 | def simpletest(event):
342 | print u'处理每秒触发的计时器事件:%s' % str(datetime.now())
343 |
344 | app = QCoreApplication(sys.argv)
345 |
346 | ee = EventEngine2()
347 | #ee.register(EVENT_TIMER, simpletest)
348 | ee.registerGeneralHandler(simpletest)
349 | ee.start()
350 |
351 | app.exec_()
352 |
353 |
354 | # 直接运行脚本可以进行测试
355 | if __name__ == '__main__':
356 | test()
357 |
--------------------------------------------------------------------------------
/eventType.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件仅用于存放对于事件类型常量的定义。
5 |
6 | 由于python中不存在真正的常量概念,因此选择使用全大写的变量名来代替常量。
7 | 这里设计的命名规则以EVENT_前缀开头。
8 |
9 | 常量的内容通常选择一个能够代表真实意义的字符串(便于理解)。
10 |
11 | 建议将所有的常量定义放在该文件中,便于检查是否存在重复的现象。
12 | '''
13 | # 事件引擎内部通信
14 | EVENT_REG = 'eReg' # 事件引擎注册事件
15 |
16 | # 系统相关
17 | EVENT_TIMER = 'eTimer' # 计时器事件,每隔1秒发送一次
18 | EVENT_LOG = 'eLog' # 日志事件,全局通用
19 | EVENT_F5 = 'eF5' # 键盘事件
20 | EVENT_F2 = 'eF2' # 键盘事件
21 |
22 | # Gateway相关
23 | EVENT_TICK = 'eTick.' # TICK行情事件,可后接具体的vtSymbol
24 | EVENT_BAR = 'eBar.' # BAR行情事件,可后接具体的vtSymbol
25 | EVENT_ERROR = 'eError.' # 错误回报事件
26 |
27 | # 回测模块相关
28 | EVENT_CTA_TASK = 'eCtaTask' # 任务更新事件
29 | EVENT_CTA_LOG = 'eCtaLog' # 日志事件
30 | EVENT_CTA_STRATEGY = 'eCtaStrategy.' # 策略状态变化事件
31 | EVENT_CTA_STRATEGY_LOAD = 'eCtaStrategyLoad.' # 策略状态变化事件
32 | EVENT_CTA_STRATEGY_PARAM = 'eCtaStrategyParam.'# 策略参数变化事件
33 |
34 | #----------------------------------------------------------------------
35 | def test():
36 | """检查是否存在内容重复的常量定义"""
37 | check_dict = {}
38 |
39 | global_dict = globals()
40 |
41 | for key, value in global_dict.items():
42 | if '__' not in key: # 不检查python内置对象
43 | if value in check_dict:
44 | check_dict[value].append(key)
45 | else:
46 | check_dict[value] = [key]
47 |
48 | for key, value in check_dict.items():
49 | if len(value)>1:
50 | print u'存在重复的常量定义:' + str(key)
51 | for name in value:
52 | print name
53 | print ''
54 |
55 | print u'测试完毕'
56 |
57 |
58 | # 直接运行脚本可以进行测试
59 | if __name__ == '__main__':
60 | test()
61 |
--------------------------------------------------------------------------------
/func-button/klAna.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | from ctaFunction import *
6 | #----------------------------------------------------------------------
7 | def klAna(self):
8 | data = self.spdData.copy()
9 | data['pnl'] = ['p' if d > 0 else 'l' for d in data['pnl']]
10 | plotFactors(data)
11 |
12 |
--------------------------------------------------------------------------------
/func-button/klBacktest.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | import numpy as np
6 | from ctaFunction.calcFunction import *
7 | from ctaFunction.visFunction import *
8 |
9 | #----------------------------------------------------------------------
10 | def klBacktest(self):
11 | wLimit = self.getInputParamByName('wLimit')
12 | cLimit = self.getInputParamByName('cLimit')
13 | size = self.getInputParamByName('size')
14 | sLippage = self.getInputParamByName('sLippage')
15 | tickers = pd.DataFrame()
16 | tickers['bidPrice1'] = self.pdBars['open']-sLippage
17 | tickers['askPrice1'] = self.pdBars['open']+sLippage
18 | markets = tickers.values
19 | signals = np.array(self.signalsOpen)
20 | plotSigCaps(tickers.index.to_pydatetime(),signals,markets,cLimit,wLimit,size=size)
21 |
--------------------------------------------------------------------------------
/func-button/klClearSig.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | #----------------------------------------------------------------------
6 | def klClearSig(self):
7 | kTool = self.canvas
8 | for sig in kTool.sigPlots:
9 | kTool.pwKL.removeItem(kTool.sigPlots[sig])
10 | kTool.sigData = {}
11 | kTool.sigPlots = {}
12 | for sig in kTool.subSigPlots:
13 | kTool.pwOI.removeItem(kTool.subSigPlots[sig])
14 | kTool.subSigData = {}
15 | kTool.subSigPlots = {}
16 |
--------------------------------------------------------------------------------
/func-button/klHeatmap.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | import numpy as np
6 | from ctaFunction.calcFunction import *
7 | from ctaFunction.visFunction import *
8 |
9 | #----------------------------------------------------------------------
10 | def klHeatmap(self):
11 | start = self.getInputParamByName('wLimit')
12 | step = self.getInputParamByName('cLimit')
13 | sLippage = self.getInputParamByName('sLippage')
14 | size = self.getInputParamByName('size')
15 | tickers = pd.DataFrame()
16 | tickers['bidPrice1'] = self.pdBars['open']-sLippage
17 | tickers['askPrice1'] = self.pdBars['open']+sLippage
18 | markets = tickers.values
19 | signals = np.array(self.signalsOpen)
20 | plotSigHeats(signals,markets,start=start,step=step,size=size,iters=6)
21 | plt.show()
22 |
--------------------------------------------------------------------------------
/func-button/klLoad.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 |
6 | #----------------------------------------------------------------------
7 | def klLoad(self,bars=None):
8 | """载入合约数据"""
9 | kTool = self.canvas
10 | for sig in kTool.sigPlots:
11 | kTool.pwKL.removeItem(kTool.sigPlots[sig])
12 | kTool.sigData = {}
13 | kTool.sigPlots = {}
14 | for sig in kTool.subSigPlots:
15 | kTool.pwOI.removeItem(kTool.subSigPlots[sig])
16 | kTool.subSigData = {}
17 | kTool.subSigPlots = {}
18 | self.loadData()
19 | hideSplash()
20 |
21 |
--------------------------------------------------------------------------------
/func-button/klPlay.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | #----------------------------------------------------------------------
6 | def klPlay(self):
7 | self.startPlay()
8 |
9 |
--------------------------------------------------------------------------------
/func-button/klPnl.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | from ctaFunction import *
6 | #----------------------------------------------------------------------
7 | def klPnl(self):
8 | s = self.getInputParamByName('signalName')
9 | plotVarVPnl(self.spdData,s)
10 |
11 |
--------------------------------------------------------------------------------
/func-button/klReload.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | #----------------------------------------------------------------------
6 | def klReload(self):
7 | self.editDict['signalName'].clear()
8 | self.editDict['signalName'].addItems(ctaBase.ALL_BAR_SIGNALS)
9 |
--------------------------------------------------------------------------------
/func-button/klShowdown.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | import pandas as pd
6 | import numpy as np
7 |
8 | #----------------------------------------------------------------------
9 | def klShowdown(self):
10 | """信号曲线"""
11 | sigName = self.getInputParamByName('signalName')
12 | self.canvas.listOpenInterest = self.stateData[sigName]
13 | self.canvas.datas['openInterest'] = np.array(self.stateData[sigName])
14 | self.canvas.plotOI(0,len(self.stateData[sigName]))
15 | self.canvas.showSig({sigName:self.stateData[sigName]},False)
16 |
--------------------------------------------------------------------------------
/func-button/klShowmain.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 | import pandas as pd
6 |
7 | #----------------------------------------------------------------------
8 | def klShowmain(self):
9 | """信号分析报告"""
10 | sigName = self.getInputParamByName('signalName')
11 | self.canvas.showSig({sigName:self.stateData[sigName]},True)
12 |
13 |
--------------------------------------------------------------------------------
/func-button/klSigmode.py:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | """
3 | 插入所有需要的库,和函数
4 | """
5 |
6 | #----------------------------------------------------------------------
7 | def klSigmode(self):
8 | """查找模式"""
9 | if self.mode == 'deal':
10 | self.canvas.updateSig(self.signalsOpen)
11 | self.mode = 'dealOpen'
12 | else:
13 | self.canvas.updateSig(self.signals)
14 | self.mode = 'deal'
15 |
16 |
--------------------------------------------------------------------------------
/historyData/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.swp
4 | /log/
5 | /trade/
6 |
--------------------------------------------------------------------------------
/json/CTA_setting.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/json/ContractInfo.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "rb",
4 | "mPrice": 1,
5 | "mRate": 0.00010085,
6 | "mRateT": 0.00010085,
7 | "mFee": 0.0,
8 | "mMon": 0.01,
9 | "mSize": 10,
10 | "mLevel": 0.07
11 | },
12 | {
13 | "name": "ni",
14 | "mPrice": 1,
15 | "mRate": 0.00010085,
16 | "mRateT": 0.00010085,
17 | "mFee": 0.0,
18 | "mMon": 0.01,
19 | "mSize": 1,
20 | "mLevel": 0.07
21 | },
22 | {
23 | "name": "Ni",
24 | "mPrice": 0.05,
25 | "mRate": 0.00005085,
26 | "mRateT": 0.000050085,
27 | "mFee": 0.0,
28 | "mMon": 0.01,
29 | "mSize": 1,
30 | "mLevel": 0.07
31 | },
32 | {
33 | "name": "au",
34 | "mPrice": 0.005,
35 | "mRate": 0.00004,
36 | "mRateT": 0.0,
37 | "mFee": 0.0,
38 | "mMon": 0.01,
39 | "mSize": 1000,
40 | "mLevel": 0.07
41 | },
42 | {
43 | "name": "al",
44 | "mPrice": 5,
45 | "mRate": 0.00005,
46 | "mRateT": 0.0,
47 | "mFee": 0.0,
48 | "mMon": 0.01,
49 | "mSize": 5,
50 | "mLevel": 0.07
51 | },
52 | {
53 | "name": "zn",
54 | "mPrice": 5,
55 | "mRate": 0.00004,
56 | "mRateT": 0.0,
57 | "mFee": 0.0,
58 | "mMon": 0.01,
59 | "mSize": 5,
60 | "mLevel": 0.07
61 | },
62 | {
63 | "name": "ag",
64 | "mPrice": 1,
65 | "mRate": 0.00005,
66 | "mRateT": 0.00005,
67 | "mFee": 0.0,
68 | "mMon": 0.01,
69 | "mSize": 10,
70 | "mLevel": 0.07
71 | },
72 | {
73 | "name": "cu",
74 | "mPrice": 10,
75 | "mRate": 0.00005,
76 | "mRateT": 0.00005,
77 | "mFee": 0.0,
78 | "mMon": 0.01,
79 | "mSize": 5,
80 | "mLevel": 0.07
81 | },
82 | {
83 | "name": "pb",
84 | "mPrice": 5,
85 | "mRate": 0.00005,
86 | "mRateT": 0.00005,
87 | "mFee": 0.0,
88 | "mMon": 0.01,
89 | "mSize": 5,
90 | "mLevel": 0.07
91 | },
92 | {
93 | "name": "ru",
94 | "mPrice": 5,
95 | "mRate": 0.00004585,
96 | "mRateT": 0.00004585,
97 | "mFee": 0.0,
98 | "mMon": 0.01,
99 | "mSize": 10,
100 | "mLevel": 0.07
101 | },
102 | {
103 | "name": "bu",
104 | "mPrice": 2,
105 | "mRate": 0.0001,
106 | "mRateT": 0.0001,
107 | "mFee": 0.0,
108 | "mMon": 0.01,
109 | "mSize": 10,
110 | "mLevel": 0.07
111 | },
112 | {
113 | "name": "hc",
114 | "mPrice": 1,
115 | "mRate": 0.0001,
116 | "mRateT": 0.0001,
117 | "mFee": 0.0,
118 | "mMon": 0.01,
119 | "mSize": 10,
120 | "mLevel": 0.07
121 | },
122 | {
123 | "name": "T",
124 | "mPrice": 0.005,
125 | "mRate": 0.000003,
126 | "mRateT": 0.000003,
127 | "mFee": 0.0,
128 | "mMon": 0.01,
129 | "mSize": 10000,
130 | "mLevel": 0.07
131 | },
132 | {
133 | "name": "TF",
134 | "mPrice": 0.005,
135 | "mRate": 0.000003,
136 | "mRateT": 0.000003,
137 | "mFee": 0.0,
138 | "mMon": 0.01,
139 | "mSize": 10000,
140 | "mLevel": 0.07
141 | },
142 | {
143 | "name": "pp",
144 | "mPrice": 1,
145 | "mRate": 0.00015,
146 | "mRateT": 0.00015,
147 | "mFee": 0.0,
148 | "mMon": 0.01,
149 | "mSize": 5,
150 | "mLevel": 0.07
151 | },
152 | {
153 | "name": "l",
154 | "mPrice": 5,
155 | "mRate": 0.00007,
156 | "mRateT": 0.00007,
157 | "mFee": 0.0,
158 | "mMon": 0.01,
159 | "mSize": 5,
160 | "mLevel": 0.07
161 | },
162 | {
163 | "name": "m",
164 | "mPrice": 1,
165 | "mRate": 0.00006,
166 | "mRateT": 0.00006,
167 | "mFee": 0.0,
168 | "mMon": 0.01,
169 | "mSize": 10,
170 | "mLevel": 0.07
171 | },
172 | {
173 | "name": "c",
174 | "mPrice": 1,
175 | "mRate": 0.00007,
176 | "mRateT": 0.00007,
177 | "mFee": 0.0,
178 | "mMon": 0.01,
179 | "mSize": 10,
180 | "mLevel": 0.07
181 | },
182 | {
183 | "name": "a",
184 | "mPrice": 1,
185 | "mRate": 0.00006,
186 | "mRateT": 0.00006,
187 | "mFee": 0.0,
188 | "mMon": 0.01,
189 | "mSize": 10,
190 | "mLevel": 0.07
191 | },
192 | {
193 | "name": "cs",
194 | "mPrice": 1,
195 | "mRate": 0.00006,
196 | "mRateT": 0.00006,
197 | "mFee": 0.0,
198 | "mMon": 0.01,
199 | "mSize": 10,
200 | "mLevel": 0.07
201 | },
202 | {
203 | "name": "i",
204 | "mPrice": 0.5,
205 | "mRate": 0.000045,
206 | "mRateT": 0.000045,
207 | "mFee": 0.0,
208 | "mMon": 0.01,
209 | "mSize": 100,
210 | "mLevel": 0.07
211 | },
212 | {
213 | "name": "j",
214 | "mPrice": 0.5,
215 | "mRate": 0.0001,
216 | "mRateT": 0.0001,
217 | "mFee": 0.0,
218 | "mMon": 0.01,
219 | "mSize": 100,
220 | "mLevel": 0.07
221 | },
222 | {
223 | "name": "jm",
224 | "mPrice": 0.5,
225 | "mRate": 0.0001,
226 | "mRateT": 0.0001,
227 | "mFee": 0.0,
228 | "mMon": 0.01,
229 | "mSize": 60,
230 | "mLevel": 0.07
231 | },
232 | {
233 | "name": "jd",
234 | "mPrice": 1,
235 | "mRate": 0.00015,
236 | "mRateT": 0.00015,
237 | "mFee": 0.0,
238 | "mMon": 0.01,
239 | "mSize": 5,
240 | "mLevel": 0.07
241 | },
242 | {
243 | "name": "p",
244 | "mPrice": 2,
245 | "mRate": 0.00006,
246 | "mRateT": 0.00006,
247 | "mFee": 0.0,
248 | "mMon": 0.01,
249 | "mSize": 10,
250 | "mLevel": 0.07
251 | },
252 | {
253 | "name": "v",
254 | "mPrice": 5,
255 | "mRate": 0.00007,
256 | "mRateT": 0.00007,
257 | "mFee": 0.0,
258 | "mMon": 0.01,
259 | "mSize": 5,
260 | "mLevel": 0.07
261 | },
262 | {
263 | "name": "SR",
264 | "mPrice": 1,
265 | "mRate": 0.00005,
266 | "mRateT": 0.00005,
267 | "mFee": 0.0,
268 | "mMon": 0.01,
269 | "mSize": 10,
270 | "mLevel": 0.07
271 | },
272 | {
273 | "name": "CF",
274 | "mPrice": 5,
275 | "mRate": 0.00008,
276 | "mRateT": 0.00008,
277 | "mFee": 0.0,
278 | "mMon": 0.01,
279 | "mSize": 5,
280 | "mLevel": 0.07
281 | },
282 | {
283 | "name": "ZC",
284 | "mPrice": 0.2,
285 | "mRate": 0.00006,
286 | "mRateT": 0.00006,
287 | "mFee": 0.0,
288 | "mMon": 0.01,
289 | "mSize": 100,
290 | "mLevel": 0.07
291 | },
292 | {
293 | "name": "FG",
294 | "mPrice": 1,
295 | "mRate": 0.00012,
296 | "mRateT": 0.00012,
297 | "mFee": 0.0,
298 | "mMon": 0.01,
299 | "mSize": 20,
300 | "mLevel": 0.07
301 | },
302 | {
303 | "name": "TA",
304 | "mPrice": 2,
305 | "mRate": 0.00012,
306 | "mRateT": 0.00012,
307 | "mFee": 0.0,
308 | "mMon": 0.01,
309 | "mSize": 5,
310 | "mLevel": 0.07
311 | },
312 | {
313 | "name": "MA",
314 | "mPrice": 1,
315 | "mRate": 0.00007,
316 | "mRateT": 0.00007,
317 | "mFee": 0.0,
318 | "mMon": 0.01,
319 | "mSize": 10,
320 | "mLevel": 0.07
321 | },
322 | {
323 | "name": "WH",
324 | "mPrice": 1,
325 | "mRate": 0.00005,
326 | "mRateT": 0.00005,
327 | "mFee": 0.0,
328 | "mMon": 0.01,
329 | "mSize": 20,
330 | "mLevel": 0.07
331 | },
332 | {
333 | "name": "PM",
334 | "mPrice": 1,
335 | "mRate": 0.00005,
336 | "mRateT": 0.00005,
337 | "mFee": 0.0,
338 | "mMon": 0.01,
339 | "mSize": 50,
340 | "mLevel": 0.07
341 | },
342 | {
343 | "name": "RI",
344 | "mPrice": 1,
345 | "mRate": 0.00005,
346 | "mRateT": 0.00005,
347 | "mFee": 0.0,
348 | "mMon": 0.01,
349 | "mSize": 20,
350 | "mLevel": 0.07
351 | },
352 | {
353 | "name": "LR",
354 | "mPrice": 1,
355 | "mRate": 0.00005,
356 | "mRateT": 0.00005,
357 | "mFee": 0.0,
358 | "mMon": 0.01,
359 | "mSize": 20,
360 | "mLevel": 0.07
361 | },
362 | {
363 | "name": "JR",
364 | "mPrice": 1,
365 | "mRate": 0.00005,
366 | "mRateT": 0.00005,
367 | "mFee": 0.0,
368 | "mMon": 0.01,
369 | "mSize": 20,
370 | "mLevel": 0.07
371 | },
372 | {
373 | "name": "RS",
374 | "mPrice": 1,
375 | "mRate": 0.00004,
376 | "mRateT": 0.00004,
377 | "mFee": 0.0,
378 | "mMon": 0.01,
379 | "mSize": 10,
380 | "mLevel": 0.07
381 | },
382 | {
383 | "name": "OI",
384 | "mPrice": 2,
385 | "mRate": 0.00003,
386 | "mRateT": 0.00003,
387 | "mFee": 0.0,
388 | "mMon": 0.01,
389 | "mSize": 10,
390 | "mLevel": 0.07
391 | },
392 | {
393 | "name": "RM",
394 | "mPrice": 1,
395 | "mRate": 0.00012,
396 | "mRateT": 0.00012,
397 | "mFee": 0.0,
398 | "mMon": 0.01,
399 | "mSize": 10,
400 | "mLevel": 0.07
401 | },
402 | {
403 | "name": "SM",
404 | "mPrice": 2,
405 | "mRate": 0.00008,
406 | "mRateT": 0.00008,
407 | "mFee": 0.0,
408 | "mMon": 0.01,
409 | "mSize": 5,
410 | "mLevel": 0.07
411 | },
412 | {
413 | "name": "SF",
414 | "mPrice": 2,
415 | "mRate": 0.00005,
416 | "mRateT": 0.00005,
417 | "mFee": 0.0,
418 | "mMon": 0.01,
419 | "mSize": 5,
420 | "mLevel": 0.07
421 | },
422 | {
423 | "name": "y",
424 | "mPrice": 2,
425 | "mRate": 0.00012,
426 | "mRateT": 0.00012,
427 | "mFee": 0.0,
428 | "mMon": 0.01,
429 | "mSize": 10,
430 | "mLevel": 0.07
431 | }
432 | ]
433 |
--------------------------------------------------------------------------------
/json/DATA_setting.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "dbname": "vnTrader_Tick_db",
4 | "mode": ["T","TP","TC"],
5 | "type": "tick"
6 | },
7 | {
8 | "dbname": "VnTrader_1Min_Db",
9 | "mode": ["B","BP"],
10 | "type": "bar"
11 | },
12 | {
13 | "dbname": "VnTrader_5Min_Db",
14 | "mode": ["B5","BP5"],
15 | "type": "bar"
16 | },
17 | {
18 | "dbname": "VnTrader_15Min_Db",
19 | "mode": ["B15","BP15"],
20 | "type": "bar"
21 | },
22 | {
23 | "dbname": "VnTrader_25Min_Db",
24 | "mode": ["B25","BP25"],
25 | "type": "bar"
26 | },
27 | {
28 | "dbname": "VnTrader_60Min_Db",
29 | "mode": ["B60","BP60"],
30 | "type": "bar"
31 | },
32 | {
33 | "dbname": "VnTrader_Daily_Db",
34 | "mode": ["BD","BPD"],
35 | "type": "bar"
36 | }
37 | ]
38 |
--------------------------------------------------------------------------------
/json/uiCtaKLine_button.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "class": "结果展示",
4 | "label": "信号回测",
5 | "width": 2,
6 | "func": "klBacktest",
7 | "style":"greenButton"
8 | },
9 | {
10 | "class": "结果展示",
11 | "label": "盈亏统计",
12 | "width": 2,
13 | "func": "klPnl",
14 | "style":"greenButton"
15 | },
16 | {
17 | "class": "结果展示",
18 | "label": "胜率归因",
19 | "width": 2,
20 | "func": "klAna",
21 | "style":"greenButton"
22 | },
23 | {
24 | "class": "结果展示",
25 | "label": "盈损热力",
26 | "width": 2,
27 | "func": "klHeatmap",
28 | "style":"greenButton"
29 | },
30 | {
31 | "class": "信号控制",
32 | "label": "只看开仓",
33 | "width": 2,
34 | "func": "klSigmode",
35 | "style":"redButton"
36 | },
37 | {
38 | "class": "信号控制",
39 | "label": "主图显示",
40 | "width": 2,
41 | "func": "klShowmain",
42 | "style":"redButton"
43 | },
44 | {
45 | "class": "信号控制",
46 | "label": "副图显示",
47 | "width": 2,
48 | "func": "klShowdown",
49 | "style":"redButton"
50 | },
51 | {
52 | "class": "信号控制",
53 | "label": "清空信号",
54 | "width": 2,
55 | "func": "klClearSig",
56 | "style":"redButton"
57 | }
58 | ]
59 |
--------------------------------------------------------------------------------
/json/uiCtaKLine_input.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "signalName",
4 | "default": "",
5 | "class": "信号输入",
6 | "label": "信号选择",
7 | "width": 4,
8 | "type": "List",
9 | "eval": false,
10 | "ListVar" : "[]"
11 | },
12 | {
13 | "name": "wLimit",
14 | "default": "10",
15 | "class": "回测参数",
16 | "label": "止盈/开始",
17 | "width": 2,
18 | "eval": true,
19 | "type": "Edit"
20 | },
21 | {
22 | "name": "cLimit",
23 | "default": "4",
24 | "class": "回测参数",
25 | "label": "止损/间隔",
26 | "width": 2,
27 | "eval": true,
28 | "type": "Edit"
29 | },
30 | {
31 | "name": "size",
32 | "default": "4",
33 | "class": "回测参数",
34 | "label": "下单手数",
35 | "width": 2,
36 | "eval": true,
37 | "type": "Edit"
38 | },
39 | {
40 | "name": "sLippage",
41 | "default": "1",
42 | "class": "回测参数",
43 | "label": "合约滑点",
44 | "width": 2,
45 | "eval": true,
46 | "type": "Edit"
47 | }
48 | ]
49 |
--------------------------------------------------------------------------------
/libbson-1.0.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/libbson-1.0.dll
--------------------------------------------------------------------------------
/libcrypto-1_1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/libcrypto-1_1.dll
--------------------------------------------------------------------------------
/libmongoc-1.0.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/libmongoc-1.0.dll
--------------------------------------------------------------------------------
/libssl-1_1.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/libssl-1_1.dll
--------------------------------------------------------------------------------
/log/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.swp
4 | /log/
5 | /trade/
6 |
--------------------------------------------------------------------------------
/msvcp120.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/msvcp120.dll
--------------------------------------------------------------------------------
/msvcr120.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/msvcr120.dll
--------------------------------------------------------------------------------
/notebook/VT_setting.json:
--------------------------------------------------------------------------------
1 | {
2 | "fontFamily": "微软雅黑",
3 | "fontSize": 12,
4 |
5 | "mongoHost": "localhost",
6 | "mongoPort": 27017,
7 |
8 | "mcHost": "localhost",
9 | "mcPort": 11210,
10 |
11 | "phoneNumber": "",
12 | "smsWarn": false,
13 |
14 | "mongoHost0": "139.196.138.251",
15 | "mongoPort0": 27017,
16 |
17 | "darkStyle": true
18 | }
19 |
--------------------------------------------------------------------------------
/notebook/__pycache__/ctaBase.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/__pycache__/ctaBase.cpython-36.pyc
--------------------------------------------------------------------------------
/notebook/__pycache__/vtConstant.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/__pycache__/vtConstant.cpython-36.pyc
--------------------------------------------------------------------------------
/notebook/__pycache__/vtFunction.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/__pycache__/vtFunction.cpython-36.pyc
--------------------------------------------------------------------------------
/notebook/ctaBase.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件中包含了CTA模块中用到的一些基础设置、类和常量等。
5 | '''
6 |
7 | from __future__ import division
8 |
9 |
10 | # 把vn.trader根目录添加到python环境变量中
11 | import sys
12 | import os
13 | import glob
14 | import imp
15 | sys.path.append('..')
16 |
17 |
18 | # 常量定义
19 | # CTA引擎中涉及到的交易方向类型
20 | CTAORDER_BUY = u'买开'
21 | CTAORDER_SELL = u'卖平'
22 | CTAORDER_SELL_TODAY = u'卖平今'
23 | CTAORDER_SHORT = u'卖开'
24 | CTAORDER_COVER = u'买平'
25 | CTAORDER_COVER_TODAY = u'买平今'
26 |
27 | # 本地停止单状态
28 | STOPORDER_WAITING = u'等待中'
29 | STOPORDER_CANCELLED = u'已撤销'
30 | STOPORDER_TRIGGERED = u'已触发'
31 |
32 | # 本地停止单前缀
33 | STOPORDERPREFIX = 'CtaStopOrder.'
34 |
35 | # 数据库名称
36 | SETTING_DB_NAME = 'VnTrader_Setting_Db'
37 | TICK_DB_NAME = 'vnTrader_Tick_db'
38 | CAP_DB_NAME = 'vt_trader_cap_db'
39 | DAILY_DB_NAME = 'VnTrader_Daily_Db'
40 | MINUTE_DB_NAME = 'VnTrader_1Min_Db'
41 |
42 |
43 | # CTA引擎中涉及的数据类定义
44 | from vtConstant import EMPTY_UNICODE, EMPTY_STRING, EMPTY_FLOAT, EMPTY_INT
45 |
46 | ########################################################################
47 | class CtaBarData(object):
48 | """K线数据"""
49 |
50 | #----------------------------------------------------------------------
51 | def __init__(self):
52 | """Constructor"""
53 | self.vtSymbol = EMPTY_STRING # vt系统代码
54 | self.symbol = EMPTY_STRING # 代码
55 | self.exchange = EMPTY_STRING # 交易所
56 |
57 | self.open = EMPTY_FLOAT # OHLC
58 | self.high = EMPTY_FLOAT
59 | self.low = EMPTY_FLOAT
60 | self.close = EMPTY_FLOAT
61 |
62 | self.date = EMPTY_STRING # bar开始的时间,日期
63 | self.time = EMPTY_STRING # 时间
64 | self.datetime = None # python的datetime时间对象
65 |
66 | self.volume = EMPTY_INT # 成交量
67 | self.openInterest = EMPTY_INT # 持仓量
68 |
69 |
70 | ########################################################################
71 | class CtaTickData(object):
72 | """Tick数据"""
73 |
74 | #----------------------------------------------------------------------
75 | def __init__(self):
76 | """Constructor"""
77 | self.vtSymbol = EMPTY_STRING # vt系统代码
78 | self.symbol = EMPTY_STRING # 合约代码
79 | self.exchange = EMPTY_STRING # 交易所代码
80 |
81 | # 成交数据
82 | self.lastPrice = EMPTY_FLOAT # 最新成交价
83 | self.volume = EMPTY_INT # 最新成交量
84 | self.openInterest = EMPTY_INT # 持仓量
85 |
86 | self.upperLimit = EMPTY_FLOAT # 涨停价
87 | self.lowerLimit = EMPTY_FLOAT # 跌停价
88 |
89 | self.turnover = EMPTY_FLOAT # 成交额
90 |
91 | # tick的时间
92 | self.date = EMPTY_STRING # 日期
93 | self.time = EMPTY_STRING # 时间
94 | self.datetime = None # python的datetime时间对象
95 |
96 | # 五档行情
97 | self.bidPrice1 = EMPTY_FLOAT
98 | self.bidPrice2 = EMPTY_FLOAT
99 | self.bidPrice3 = EMPTY_FLOAT
100 | self.bidPrice4 = EMPTY_FLOAT
101 | self.bidPrice5 = EMPTY_FLOAT
102 |
103 | self.askPrice1 = EMPTY_FLOAT
104 | self.askPrice2 = EMPTY_FLOAT
105 | self.askPrice3 = EMPTY_FLOAT
106 | self.askPrice4 = EMPTY_FLOAT
107 | self.askPrice5 = EMPTY_FLOAT
108 |
109 | self.bidVolume1 = EMPTY_INT
110 | self.bidVolume2 = EMPTY_INT
111 | self.bidVolume3 = EMPTY_INT
112 | self.bidVolume4 = EMPTY_INT
113 | self.bidVolume5 = EMPTY_INT
114 |
115 | self.askVolume1 = EMPTY_INT
116 | self.askVolume2 = EMPTY_INT
117 | self.askVolume3 = EMPTY_INT
118 | self.askVolume4 = EMPTY_INT
119 | self.askVolume5 = EMPTY_INT
120 |
--------------------------------------------------------------------------------
/notebook/ctaBase.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/ctaBase.pyc
--------------------------------------------------------------------------------
/notebook/vtBase.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | from vtConstant import *
4 | import time
5 |
6 | ########################################################################
7 | class VtBaseData(object):
8 | """回调函数推送数据的基础类,其他数据类继承于此"""
9 |
10 | #----------------------------------------------------------------------
11 | def __init__(self):
12 | """Constructor"""
13 | self.gatewayName = EMPTY_STRING # Gateway名称
14 | self.rawData = None # 原始数据
15 |
16 |
17 | ########################################################################
18 | class VtTickData(VtBaseData):
19 | """Tick行情数据类"""
20 |
21 | #----------------------------------------------------------------------
22 | def __init__(self):
23 | """Constructor"""
24 | super(VtTickData, self).__init__()
25 |
26 | # 代码相关
27 | self.symbol = EMPTY_STRING # 合约代码
28 | self.exchange = EMPTY_STRING # 交易所代码
29 | self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
30 |
31 | # 成交数据
32 | self.lastPrice = EMPTY_FLOAT # 最新成交价
33 | self.lastVolume = EMPTY_INT # 最新成交量
34 | self.volume = EMPTY_INT # 今天总成交量
35 | self.openInterest = EMPTY_INT # 持仓量
36 | self.time = EMPTY_STRING # 时间 11:20:56.5
37 | self.date = EMPTY_STRING # 日期 20151009
38 |
39 | # 常规行情
40 | self.openPrice = EMPTY_FLOAT # 今日开盘价
41 | self.highPrice = EMPTY_FLOAT # 今日最高价
42 | self.lowPrice = EMPTY_FLOAT # 今日最低价
43 | self.preClosePrice = EMPTY_FLOAT
44 |
45 | self.upperLimit = EMPTY_FLOAT # 涨停价
46 | self.lowerLimit = EMPTY_FLOAT # 跌停价
47 |
48 | self.turnover = EMPTY_FLOAT # 成交额
49 |
50 | # 五档行情
51 | self.bidPrice1 = EMPTY_FLOAT
52 | self.bidPrice2 = EMPTY_FLOAT
53 | self.bidPrice3 = EMPTY_FLOAT
54 | self.bidPrice4 = EMPTY_FLOAT
55 | self.bidPrice5 = EMPTY_FLOAT
56 |
57 | self.askPrice1 = EMPTY_FLOAT
58 | self.askPrice2 = EMPTY_FLOAT
59 | self.askPrice3 = EMPTY_FLOAT
60 | self.askPrice4 = EMPTY_FLOAT
61 | self.askPrice5 = EMPTY_FLOAT
62 |
63 | self.bidVolume1 = EMPTY_INT
64 | self.bidVolume2 = EMPTY_INT
65 | self.bidVolume3 = EMPTY_INT
66 | self.bidVolume4 = EMPTY_INT
67 | self.bidVolume5 = EMPTY_INT
68 |
69 | self.askVolume1 = EMPTY_INT
70 | self.askVolume2 = EMPTY_INT
71 | self.askVolume3 = EMPTY_INT
72 | self.askVolume4 = EMPTY_INT
73 | self.askVolume5 = EMPTY_INT
74 |
75 |
76 | ########################################################################
77 | class VtTradeData(VtBaseData):
78 | """成交数据类"""
79 |
80 | #----------------------------------------------------------------------
81 | def __init__(self):
82 | """Constructor"""
83 | super(VtTradeData, self).__init__()
84 |
85 | # 代码编号相关
86 | self.symbol = EMPTY_STRING # 合约代码
87 | self.exchange = EMPTY_STRING # 交易所代码
88 | self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
89 |
90 | self.tradeID = EMPTY_STRING # 成交编号
91 | self.vtTradeID = EMPTY_STRING # 成交在vt系统中的唯一编号,通常是 Gateway名.成交编号
92 |
93 | self.orderID = EMPTY_STRING # 订单编号
94 | self.vtOrderID = EMPTY_STRING # 订单在vt系统中的唯一编号,通常是 Gateway名.订单编号
95 |
96 | # 成交相关
97 | self.direction = EMPTY_UNICODE # 成交方向
98 | self.offset = EMPTY_UNICODE # 成交开平仓
99 | self.price = EMPTY_FLOAT # 成交价格
100 | self.volume = EMPTY_INT # 成交数量
101 | self.tradeTime = EMPTY_STRING # 成交时间
102 |
103 |
104 | ########################################################################
105 | class VtOrderData(VtBaseData):
106 | """订单数据类"""
107 |
108 | #----------------------------------------------------------------------
109 | def __init__(self):
110 | """Constructor"""
111 | super(VtOrderData, self).__init__()
112 |
113 | # 代码编号相关
114 | self.symbol = EMPTY_STRING # 合约代码
115 | self.exchange = EMPTY_STRING # 交易所代码
116 | self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
117 |
118 | self.orderID = EMPTY_STRING # 订单编号
119 | self.vtOrderID = EMPTY_STRING # 订单在vt系统中的唯一编号,通常是 Gateway名.订单编号
120 |
121 | # 报单相关
122 | self.direction = EMPTY_UNICODE # 报单方向
123 | self.offset = EMPTY_UNICODE # 报单开平仓
124 | self.price = EMPTY_FLOAT # 报单价格
125 | self.priceType = EMPTY_UNICODE # 报单价格
126 | self.totalVolume = EMPTY_INT # 报单总数量
127 | self.tradedVolume = EMPTY_INT # 报单成交数量
128 | self.status = EMPTY_UNICODE # 报单状态
129 |
130 | self.orderTime = EMPTY_STRING # 发单时间
131 | self.cancelTime = EMPTY_STRING # 撤单时间
132 |
133 | # CTP/LTS相关
134 | self.frontID = EMPTY_INT # 前置机编号
135 | self.sessionID = EMPTY_INT # 连接编号
136 |
137 |
138 | ########################################################################
139 | class VtPositionData(VtBaseData):
140 | """持仓数据类"""
141 |
142 | #----------------------------------------------------------------------
143 | def __init__(self):
144 | """Constructor"""
145 | super(VtPositionData, self).__init__()
146 |
147 | # 代码编号相关
148 | self.symbol = EMPTY_STRING # 合约代码
149 | self.exchange = EMPTY_STRING # 交易所代码
150 | self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,合约代码.交易所代码
151 |
152 | # 持仓相关
153 | self.direction = EMPTY_STRING # 持仓方向
154 | self.position = EMPTY_INT # 持仓量
155 | self.frozen = EMPTY_INT # 冻结数量
156 | self.price = EMPTY_FLOAT # 持仓均价
157 | self.vtPositionName = EMPTY_STRING # 持仓在vt系统中的唯一代码,通常是vtSymbol.方向
158 |
159 | # 20151020添加
160 | self.ydPosition = EMPTY_INT # 昨持仓
161 |
162 |
163 | ########################################################################
164 | class VtAccountData(VtBaseData):
165 | """账户数据类"""
166 |
167 | #----------------------------------------------------------------------
168 | def __init__(self):
169 | """Constructor"""
170 | super(VtAccountData, self).__init__()
171 |
172 | # 账号代码相关
173 | self.datetime = None
174 | self.accountID = EMPTY_STRING # 账户代码
175 | self.vtAccountID = EMPTY_STRING # 账户在vt中的唯一代码,通常是 Gateway名.账户代码
176 |
177 | # 数值相关
178 | self.preBalance = EMPTY_FLOAT # 昨日账户结算净值
179 | self.balance = EMPTY_FLOAT # 账户净值
180 | self.available = EMPTY_FLOAT # 可用资金
181 | self.commission = EMPTY_FLOAT # 今日手续费
182 | self.margin = EMPTY_FLOAT # 保证金占用
183 | self.closeProfit = EMPTY_FLOAT # 平仓盈亏
184 | self.positionProfit = EMPTY_FLOAT # 持仓盈亏
185 | self.Check = False # 是否通过检查
186 |
187 |
188 | ########################################################################
189 | class VtMarginRateData(VtBaseData):
190 | """保证金比例数据类"""
191 |
192 | #----------------------------------------------------------------------
193 | def __init__(self):
194 | """Constructor"""
195 | super(VtMarginRateData, self).__init__()
196 |
197 | # 账号代码相关
198 | self.vtSymbol = EMPTY_STRING # 合约代码
199 |
200 | # 数值相关
201 | self.LongMarginRatioByMoney = EMPTY_FLOAT # 多头保证金率
202 | self.LongMarginRatioByVolume = EMPTY_FLOAT # 多头保证金费
203 | self.ShortMarginRatioByMoney = EMPTY_FLOAT # 空头保证金率
204 | self.ShortMarginRatioByVolume = EMPTY_FLOAT # 空头保证金肥
205 | self.IsRelative = False # 是否相对交易所收取
206 | self.Check = False # 是否通过检查
207 |
208 |
209 | ########################################################################
210 | class VtCommissionRateData(VtBaseData):
211 | """保证金比例数据类"""
212 |
213 | #----------------------------------------------------------------------
214 | def __init__(self):
215 | """Constructor"""
216 | super(VtCommissionRateData, self).__init__()
217 |
218 | # 账号代码相关
219 | self.vtSymbol = EMPTY_STRING # 合约代码
220 |
221 | # 数值相关
222 | self.OpenRatioByMoney = EMPTY_FLOAT # 开仓手续费率
223 | self.OpenRatioByVolume = EMPTY_FLOAT # 开仓手续费
224 | self.CloseRatioByMoney = EMPTY_FLOAT # 平仓手续费率
225 | self.CloseRatioByVolume = EMPTY_FLOAT # 平仓手续费
226 | self.CloseTodayRatioByMoney = EMPTY_FLOAT # 平今手续费率
227 | self.CloseTodayRatioByVolume = EMPTY_FLOAT # 平今手续费
228 |
229 |
230 | ########################################################################
231 | class VtErrorData(VtBaseData):
232 | """错误数据类"""
233 |
234 | #----------------------------------------------------------------------
235 | def __init__(self):
236 | """Constructor"""
237 | super(VtErrorData, self).__init__()
238 |
239 | self.errorID = EMPTY_STRING # 错误代码
240 | self.errorMsg = EMPTY_UNICODE # 错误信息
241 | self.additionalInfo = EMPTY_UNICODE # 补充信息
242 |
243 |
244 | ########################################################################
245 | class VtLogData(VtBaseData):
246 | """日志数据类"""
247 |
248 | #----------------------------------------------------------------------
249 | def __init__(self):
250 | """Constructor"""
251 | super(VtLogData, self).__init__()
252 |
253 | self.logTime = time.strftime('%X', time.localtime()) # 日志生成时间
254 | self.logContent = EMPTY_UNICODE # 日志信息
255 |
256 |
257 | ########################################################################
258 | class VtContractData(VtBaseData):
259 | """合约详细信息类"""
260 |
261 | #----------------------------------------------------------------------
262 | def __init__(self):
263 | """Constructor"""
264 | super(VtBaseData, self).__init__()
265 |
266 | self.symbol = EMPTY_STRING # 代码
267 | self.exchange = EMPTY_STRING # 交易所代码
268 | self.vtSymbol = EMPTY_STRING # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
269 | self.name = EMPTY_UNICODE # 合约中文名
270 |
271 | self.productClass = EMPTY_UNICODE # 合约类型
272 | self.size = EMPTY_INT # 合约大小
273 | self.priceTick = EMPTY_FLOAT # 合约最小价格TICK
274 |
275 | # 期权相关
276 | self.strikePrice = EMPTY_FLOAT # 期权行权价
277 | self.underlyingSymbol = EMPTY_STRING # 标的物合约代码
278 | self.optionType = EMPTY_UNICODE # 期权类型
279 |
280 |
281 | ########################################################################
282 | class VtSubscribeReq(object):
283 | """订阅行情时传入的对象类"""
284 |
285 | #----------------------------------------------------------------------
286 | def __init__(self):
287 | """Constructor"""
288 | self.symbol = EMPTY_STRING # 代码
289 | self.exchange = EMPTY_STRING # 交易所
290 |
291 | # 以下为IB相关
292 | self.productClass = EMPTY_UNICODE # 合约类型
293 | self.currency = EMPTY_STRING # 合约货币
294 | self.expiry = EMPTY_STRING # 到期日
295 | self.strikePrice = EMPTY_FLOAT # 行权价
296 | self.optionType = EMPTY_UNICODE # 期权类型
297 |
298 |
299 | ########################################################################
300 | class VtOrderReq(object):
301 | """发单时传入的对象类"""
302 |
303 | #----------------------------------------------------------------------
304 | def __init__(self):
305 | """Constructor"""
306 | self.symbol = EMPTY_STRING # 代码
307 | self.exchange = EMPTY_STRING # 交易所
308 | self.price = EMPTY_FLOAT # 价格
309 | self.volume = EMPTY_INT # 数量
310 |
311 | self.priceType = EMPTY_STRING # 价格类型
312 | self.direction = EMPTY_STRING # 买卖
313 | self.offset = EMPTY_STRING # 开平
314 |
315 | # 以下为IB相关
316 | self.productClass = EMPTY_UNICODE # 合约类型
317 | self.currency = EMPTY_STRING # 合约货币
318 | self.expiry = EMPTY_STRING # 到期日
319 | self.strikePrice = EMPTY_FLOAT # 行权价
320 | self.optionType = EMPTY_UNICODE # 期权类型
321 |
322 |
323 | ########################################################################
324 | class VtCancelOrderReq(object):
325 | """撤单时传入的对象类"""
326 |
327 | #----------------------------------------------------------------------
328 | def __init__(self):
329 | """Constructor"""
330 | self.symbol = EMPTY_STRING # 代码
331 | self.exchange = EMPTY_STRING # 交易所
332 |
333 | # 以下字段主要和CTP、LTS类接口相关
334 | self.orderID = EMPTY_STRING # 报单号
335 | self.frontID = EMPTY_STRING # 前置机号
336 | self.sessionID = EMPTY_STRING # 会话号
337 |
338 |
--------------------------------------------------------------------------------
/notebook/vtConstant.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | # 默认空值
4 | EMPTY_STRING = ''
5 | EMPTY_UNICODE = u''
6 | EMPTY_INT = 0
7 | EMPTY_FLOAT = 0.0
8 |
9 | # 方向常量
10 | DIRECTION_NONE = u'无方向'
11 | DIRECTION_LONG = u'多'
12 | DIRECTION_SHORT = u'空'
13 | DIRECTION_UNKNOWN = u'未知'
14 | DIRECTION_NET = u'净'
15 | DIRECTION_SELL = u'卖出' # IB接口
16 |
17 | # 开平常量
18 | OFFSET_NONE = u'无开平'
19 | OFFSET_OPEN = u'开仓'
20 | OFFSET_CLOSE = u'平仓'
21 | OFFSET_CLOSETODAY = u'平今'
22 | OFFSET_CLOSEYESTERDAY = u'平昨'
23 | OFFSET_UNKNOWN = u'未知'
24 |
25 | # 状态常量
26 | STATUS_NOTTRADED = u'未成交'
27 | STATUS_PARTTRADED = u'部分成交'
28 | STATUS_PARTTRADED_PARTCANCELLED = u'部成部撤'
29 | STATUS_ALLTRADED = u'全部成交'
30 | STATUS_CANCELLED = u'已撤销'
31 | STATUS_UNKNOWN = u'未知'
32 |
33 | # 合约类型常量
34 | PRODUCT_EQUITY = u'股票'
35 | PRODUCT_FUTURES = u'期货'
36 | PRODUCT_OPTION = u'期权'
37 | PRODUCT_INDEX = u'指数'
38 | PRODUCT_COMBINATION = u'组合'
39 | PRODUCT_FOREX = u'外汇'
40 | PRODUCT_UNKNOWN = u'未知'
41 | PRODUCT_SPOT = u'现货'
42 | PRODUCT_DEFER = u'延期'
43 | PRODUCT_NONE = ''
44 |
45 | # 价格类型常量
46 | PRICETYPE_LIMITPRICE = u'限价'
47 | PRICETYPE_MARKETPRICE = u'市价'
48 | PRICETYPE_FAK = u'FAK'
49 | PRICETYPE_FOK = u'FOK'
50 |
51 | # 期权类型
52 | OPTION_CALL = u'看涨期权'
53 | OPTION_PUT = u'看跌期权'
54 |
55 | # 交易所类型
56 | EXCHANGE_SSE = 'SSE' # 上交所
57 | EXCHANGE_SZSE = 'SZSE' # 深交所
58 | EXCHANGE_CFFEX = 'CFFEX' # 中金所
59 | EXCHANGE_SHFE = 'SHFE' # 上期所
60 | EXCHANGE_CZCE = 'CZCE' # 郑商所
61 | EXCHANGE_DCE = 'DCE' # 大商所
62 | EXCHANGE_SGE = 'SGE' # 上金所
63 | EXCHANGE_UNKNOWN = 'UNKNOWN'# 未知交易所
64 | EXCHANGE_NONE = '' # 空交易所
65 | EXCHANGE_HKEX = 'HKEX' # 港交所
66 |
67 | EXCHANGE_SMART = 'SMART' # IB智能路由(股票、期权)
68 | EXCHANGE_NYMEX = 'NYMEX' # IB 期货
69 | EXCHANGE_GLOBEX = 'GLOBEX' # CME电子交易平台
70 | EXCHANGE_IDEALPRO = 'IDEALPRO' # IB外汇ECN
71 |
72 | EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
73 |
74 | # 货币类型
75 | CURRENCY_USD = 'USD' # 美元
76 | CURRENCY_CNY = 'CNY' # 人民币
77 | CURRENCY_UNKNOWN = 'UNKNOWN' # 未知货币
78 | CURRENCY_NONE = '' # 空货币
79 |
--------------------------------------------------------------------------------
/notebook/vtConstant.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/vtConstant.pyc
--------------------------------------------------------------------------------
/notebook/vtFunction.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | """
4 | 包含一些开放中常用的函数
5 | """
6 |
7 | import decimal
8 | import json
9 | import pymongo
10 | import pandas as pd
11 | from datetime import datetime
12 |
13 | MAX_NUMBER = 10000000000000
14 | MAX_DECIMAL = 4
15 |
16 | #----------------------------------------------------------------------
17 | def safeUnicode(value):
18 | """检查接口数据潜在的错误,保证转化为的字符串正确"""
19 | # 检查是数字接近0时会出现的浮点数上限
20 | if type(value) is int or type(value) is float:
21 | if value > MAX_NUMBER:
22 | value = 0
23 |
24 | # 检查防止小数点位过多
25 | if type(value) is float:
26 | d = decimal.Decimal(str(value))
27 | if abs(d.as_tuple().exponent) > MAX_DECIMAL:
28 | value = round(value, ndigits=MAX_DECIMAL)
29 |
30 | return unicode(value)
31 |
32 | #----------------------------------------------------------------------
33 | def loadMongoSetting(path=""):
34 | """载入MongoDB数据库的配置"""
35 | try:
36 | f = file(path+"VT_setting.json")
37 | setting = json.load(f)
38 | host = setting['mongoHost']
39 | port = setting['mongoPort']
40 | except:
41 | host = 'localhost'
42 | port = 27017
43 |
44 | return host, port
45 |
46 | #----------------------------------------------------------------------
47 | def loadHistoryData(dbName, symbol, start="20151001", end="",
48 | fields=['datetime','lastPrice'],pdformat=True):
49 | """载入历史数据"""
50 | dataEndDate = None
51 | if 'datetime' not in fields:
52 | fields.insert(0,'datetime')
53 | host, port = loadMongoSetting()
54 | dbClient = pymongo.MongoClient(host, port, socketKeepAlive=True)
55 | collection = dbClient[dbName][symbol]
56 |
57 | if len(start) == 8:
58 | dataStartDate = datetime.strptime(start, '%Y%m%d')
59 | else:
60 | dataStartDate = datetime.strptime(start, '%Y%m%d %H:%M:%S')
61 |
62 | if len(end) == 8:
63 | dataEndDate = datetime.strptime(end, '%Y%m%d')
64 | elif len(end) > 0:
65 | dataEndDate = datetime.strptime(end, '%Y%m%d %H:%M:%S')
66 |
67 | # 载入回测数据
68 | if not dataEndDate:
69 | flt = {'datetime':{'$gte':dataStartDate}} # 数据过滤条件
70 | else:
71 | flt = {'datetime':{'$gte':dataStartDate,
72 | '$lte':dataEndDate}}
73 | dbCursor = collection.find(flt,no_cursor_timeout=True).batch_size(1000)
74 |
75 | if not pdformat:
76 | return dbCursor
77 |
78 | datas = pd.DataFrame([data for data in\
79 | dbCursor],columns=fields,index=range(0,dbCursor.count()))
80 | datas = datas.set_index('datetime')
81 |
82 | return datas
83 |
84 |
85 | #----------------------------------------------------------------------
86 | def loadStrategyData(dbName, name, start="20151001", end="",
87 | fields=['date','pnl'],pdformat=True):
88 | """载入历史数据"""
89 | dataEndDate = None
90 | if 'date' not in fields:
91 | fields.insert(0,'date')
92 | host, port = loadMongoSetting()
93 | dbClient = pymongo.MongoClient(host, port, socketKeepAlive=True)
94 | collection = dbClient[dbName][name]
95 |
96 | if len(start) == 8:
97 | dataStartDate = datetime.strptime(start, '%Y%m%d')
98 | else:
99 | dataStartDate = datetime.strptime(start, '%Y%m%d %H:%M:%S')
100 |
101 | if len(end) == 8:
102 | dataEndDate = datetime.strptime(end, '%Y%m%d')
103 | elif len(end) > 0:
104 | dataEndDate = datetime.strptime(end, '%Y%m%d %H:%M:%S')
105 |
106 | # 载入回测数据
107 | if not dataEndDate:
108 | flt = {'date':{'$gte':dataStartDate}} # 数据过滤条件
109 | else:
110 | flt = {'date':{'$gte':dataStartDate,
111 | '$lte':dataEndDate}}
112 | dbCursor = collection.find(flt,no_cursor_timeout=True).batch_size(1000)
113 |
114 | if not pdformat:
115 | return dbCursor
116 |
117 | datas = pd.DataFrame([data for data in\
118 | dbCursor],columns=fields,index=range(0,dbCursor.count()))
119 | #datas = datas.set_index('date')
120 |
121 | return datas
122 |
123 |
124 | #----------------------------------------------------------------------
125 | def loadMcSetting(path=""):
126 | """载入Memcache配置"""
127 | try:
128 | f = file(path+"VT_setting.json")
129 | setting = json.load(f)
130 | host = setting['mcHost']
131 | port = setting['mcPort']
132 | except:
133 | host = 'localhost'
134 | port = 11210
135 |
136 | return host, port
137 |
138 | #----------------------------------------------------------------------
139 | def loadMongoSetting0(path=""):
140 | """载入MongoDB数据库的配置"""
141 | try:
142 | f = file(path+"VT_setting.json")
143 | setting = json.load(f)
144 | host = setting['mongoHost0']
145 | port = setting['mongoPort0']
146 | except:
147 | host = 'localhost'
148 | port = 27017
149 |
150 | return host, port
151 |
152 | #----------------------------------------------------------------------
153 | def loadPhoneSetting(path=""):
154 | """载入电话配置"""
155 | try:
156 | f = file(path+"VT_setting.json")
157 | setting = json.load(f)
158 | phone = setting['phoneNumber']
159 | sms = setting['smsWarn']
160 | except:
161 | phone = ''
162 | sms = False
163 |
164 | return phone, sms
165 |
166 | #----------------------------------------------------------------------
167 | def todayDate():
168 | """获取当前本机电脑时间的日期"""
169 | return datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
170 |
171 |
172 |
--------------------------------------------------------------------------------
/notebook/vtFunction.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/notebook/vtFunction.pyc
--------------------------------------------------------------------------------
/opResults/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.swp
4 | /log/
5 | /trade/
6 |
--------------------------------------------------------------------------------
/strategy/BASICStrategy.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | """
4 | 基础策略模板,
5 | """
6 |
7 | from __future__ import division
8 | from ctaBase import *
9 | from ctaTemplate import *
10 |
11 | ########################################################################
12 | class BASICStrategy(CtaTemplate):
13 | """基础策略模板"""
14 |
15 | # 基础信息
16 | author = u'binbinwei' # 作者
17 | className = 'BASICStrategy' # 策略类名称
18 | name = EMPTY_UNICODE # 策略实例名称
19 |
20 | # 策略参数
21 | vtSymbol = '600435' # 合约
22 | exchange = 'SSE' # 交易所
23 | mPrice = 0.01 # 一跳的价格
24 | nMin = 1 # 操作级别分钟数
25 | initDays = 10 # 初始化数据所用的天数
26 |
27 | # 策略变量
28 |
29 |
30 | # 参数列表,保存了参数的名称
31 | paramList = []
32 |
33 | # 变量列表,保存了变量的名称
34 | varList = []
35 |
36 | #----------------------------------------------------------------------
37 | def __init__(self,ctaEngine=None,setting={}):
38 | """Constructor"""
39 | super(BASICStrategy, self).__init__(ctaEngine,setting)
40 |
41 | self.V = 1
42 |
43 | # 创建K线合成器对象
44 | self.bm = BarManager(self.onBar,self.nMin)
45 |
46 | # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建,
47 | # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
48 | # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读
49 | # 策略时方便(更多是个编程习惯的选择)
50 |
51 | #----------------------------------------------------------------------
52 | def onTick(self, tick):
53 | """收到行情TICK推送(必须由用户继承实现)"""
54 | super(BASICStrategy, self).onTick(tick)
55 | self.bm.updateTick(tick)
56 |
57 | #----------------------------------------------------------------------
58 | def onBar(self, bar):
59 | """收到Bar推送(必须由用户继承实现)"""
60 | super(BASICStrategy, self).onBar(bar)
61 |
62 | #----------------------------------------------------------------------
63 | def getCtaIndictor(self,bar):
64 | """计算指标数据"""
65 | # 计算指标数值
66 | return
67 |
68 | #----------------------------------------------------------------------
69 | def getCtaSignal(self,bar):
70 | """计算交易信号"""
71 | close = bar.close
72 | hour = bar.datetime.hour
73 | minute = bar.datetime.minute
74 | # 定义尾盘,尾盘不交易并且空仓
75 | self.endOfDay = False
76 | # 判断是否要进行交易
77 | self.buySig = False
78 | self.shortSig = False
79 | self.coverSig = False
80 | self.sellSig = False
81 | # 交易价格
82 | self.longPrice = bar.close
83 | self.shortPrice = bar.close
84 |
85 | #----------------------------------------------------------------------
86 | def onTrade(self, trade):
87 | super(BASICStrategy, self).onTrade(trade,log=True)
88 |
89 |
--------------------------------------------------------------------------------
/strategy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/strategy/__init__.py
--------------------------------------------------------------------------------
/strategy/期货趋势/DMAStrategy.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | """
4 | 双均线策略
5 |
6 | 注意事项:
7 | 1. 作者不对交易盈利做任何保证,策略代码仅供参考
8 | 2. 本策略需要用到talib,没有安装的用户请先参考www.vnpy.org上的教程安装
9 | """
10 |
11 | from __future__ import division
12 | from ctaBase import *
13 | from ctaTemplate import *
14 |
15 |
16 | ########################################################################
17 | class DMAStrategy(CtaTemplate):
18 | """双均线交易策略"""
19 | vtSymbol = 'rb1801'
20 | exchange = 'SHFE'
21 | className = 'DMAStrategy'
22 | author = u'binbinwei'
23 | name = EMPTY_UNICODE # 策略实例名称
24 |
25 | # 策略参数
26 | N = 5 # 快均线周期
27 | P = 20 # 慢均线周期
28 | A = 26 # 止损
29 | mPrice = 5 # 一跳的价格
30 | nMin = 1 # 操作级别分钟数
31 | initDays = 10 # 初始化数据所用的天数
32 |
33 | # 策略变量
34 | ma0 = 0 # 当前K线慢均线数值
35 | ma1 = 0 # 当前K线快均线数值
36 | ma00 = 0 # 上一个K线慢均线数值
37 | ma10 = 0 # 上一个K线快均线数值
38 |
39 | # 参数列表,保存了参数的名称
40 | paramList = ['N',
41 | 'P',
42 | 'A',
43 | 'V',
44 | 'nMin',
45 | 'mPrice']
46 |
47 | # 变量列表,保存了变量的名称
48 | varList = ['trading',
49 | 'ma0',
50 | 'ma1',
51 | 'pos']
52 |
53 | # 参数映射表
54 | paramMap = {'N' :u'快均线周期',
55 | 'P' :u'慢均线周期',
56 | 'A' :u'止损指标',
57 | 'v' :u'下单手数',
58 | 'nMin':u'K线分钟',
59 | 'exchange':u'交易所',
60 | 'vtSymbol':u'合约'}
61 |
62 | # 变量映射表
63 | varMap = {'trading' :u'交易中',
64 | 'ma0' :u'慢均线',
65 | 'ma1' :u'快均线'}
66 |
67 |
68 | #----------------------------------------------------------------------
69 | def __init__(self,ctaEngine=None,setting={}):
70 | """Constructor"""
71 | super(DMAStrategy, self).__init__(ctaEngine,setting)
72 |
73 | self.widgetClass = None
74 | self.widget = None
75 |
76 | self.bm = BarManager(self.onBar,self.nMin)
77 |
78 | self.cost = 0 # 持仓成本
79 |
80 | self.V = 1 # 下单手数
81 | self.ma0 = 0 # 当前K线慢均线数值
82 | self.ma1 = 0 # 当前K线快均线数值
83 | self.ma00 = 0 # 上一个K线慢均线数值
84 | self.ma10 = 0 # 上一个K线快均线数值
85 |
86 |
87 | # 启动界面
88 | self.signal = 0 # 买卖标志
89 | self.mainSigs = ['ma0','ma1','cost'] # 主图显示
90 | self.subSigs = [] # 副图显示
91 | #self.getGui()
92 |
93 | # 注意策略类中的可变对象属性(通常是list和dict等),在策略初始化时需要重新创建,
94 | # 否则会出现多个策略实例之间数据共享的情况,有可能导致潜在的策略逻辑错误风险,
95 | # 策略类中的这些可变对象属性可以选择不写,全都放在__init__下面,写主要是为了阅读
96 | # 策略时方便(更多是个编程习惯的选择)
97 |
98 | #----------------------------------------------------------------------
99 | def onTick(self, tick):
100 | """收到行情TICK推送"""
101 | super(DMAStrategy, self).onTick(tick)
102 | # 过滤涨跌停和集合竞价
103 | if tick.lastPrice == 0 or tick.askPrice1==0 or tick.bidPrice1==0:
104 | return
105 | self.bm.updateTick(tick)
106 |
107 | #----------------------------------------------------------------------
108 | def onBar(self, bar):
109 | """收到Bar推送(必须由用户继承实现)"""
110 | self.bar = bar
111 | if self.tradeDate != bar.datetime.date():
112 | self.tradeDate = bar.datetime.date()
113 |
114 | # 记录数据
115 | if not self.am.updateBar(bar):
116 | return
117 |
118 | # 计算指标
119 | self.getCtaIndictor(bar)
120 |
121 | # 计算信号
122 | self.getCtaSignal(bar)
123 |
124 | # 简易信号执行
125 | self.execSignal(self.V)
126 |
127 | # 发出状态更新事件
128 | if (not self.widget is None) and (not self.bar is None):
129 | data = {'bar':self.bar,'sig':self.signal,'ma0':self.ma0,'ma1':self.ma1,'cost':self.cost}
130 | self.widget.addBar(data)
131 | if self.trading:
132 | self.putEvent()
133 |
134 | #----------------------------------------------------------------------
135 | def getCtaIndictor(self,bar):
136 | """计算指标数据"""
137 | # 计算指标数值
138 | ma = self.am.sma(self.P,True)
139 | ma1 = self.am.sma(self.N,True)
140 | self.ma0,self.ma00 = ma[-1],ma[-2]
141 | self.ma1,self.ma10 = ma1[-1],ma1[-2]
142 |
143 | #----------------------------------------------------------------------
144 | def getCtaSignal(self,bar):
145 | """计算交易信号"""
146 | close = bar.close
147 | hour = bar.datetime.hour
148 | minute = bar.datetime.minute
149 | # 定义尾盘,尾盘不交易并且空仓
150 | self.endOfDay = hour==14 and minute >=40
151 | # 判断是否要进行交易
152 | self.buySig = self.ma1 > self.ma0 and self.ma10 < self.ma00
153 | self.shortSig = self.ma1 < self.ma0 and self.ma10 > self.ma00
154 | self.coverSig = self.buySig or close >= self.cost+self.A*close
155 | self.sellSig = self.shortSig or close <= self.cost-self.A*close
156 | # 交易价格
157 | self.longPrice = bar.close
158 | self.shortPrice = bar.close
159 |
160 | #----------------------------------------------------------------------
161 | def execSignal(self,volume):
162 | """简易交易信号执行"""
163 | pos = self.pos[self.vtSymbol]
164 | endOfDay = self.endOfDay
165 | # 挂单未成交
166 | if not self.orderID is None:
167 | self.cancelOrder(self.orderID)
168 |
169 | self.signal = 0
170 | # 当前无仓位
171 | if pos == 0 and not self.endOfDay:
172 | # 买开,卖开
173 | if self.shortSig:
174 | self.signal = -self.shortPrice
175 | self.orderID = self.short(self.shortPrice, volume)
176 | elif self.buySig:
177 | self.signal = self.longPrice
178 | self.orderID = self.buy(self.longPrice, volume)
179 |
180 | # 持有多头仓位
181 | elif pos > 0 and (self.sellSig or self.endOfDay):
182 | self.signal = -self.shortPrice
183 | self.orderID = self.sell(self.shortPrice, pos)
184 |
185 | # 持有空头仓位
186 | elif pos < 0 and (self.coverSig or self.endOfDay):
187 | self.signal = self.longPrice
188 | self.orderID = self.cover(self.longPrice, -pos)
189 |
190 | #----------------------------------------------------------------------
191 | def onTrade(self, trade):
192 | super(DMAStrategy, self).onTrade(trade,log=True)
193 |
194 | #----------------------------------------------------------------------
195 | def onStart(self):
196 | self.loadBar(3)
197 | super(DMAStrategy, self).onStart()
198 | #self.getGui()
199 |
200 | #----------------------------------------------------------------------
201 | def onStop(self):
202 | super(DMAStrategy, self).onStop()
203 | if not self.widget is None:
204 | self.widget.clear()
205 | #self.closeGui()
206 |
--------------------------------------------------------------------------------
/tools/.ctaHistoryData.py.un~:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/.ctaHistoryData.py.un~
--------------------------------------------------------------------------------
/tools/.peakdetect.py.un~:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/.peakdetect.py.un~
--------------------------------------------------------------------------------
/tools/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/__init__.py
--------------------------------------------------------------------------------
/tools/__init__.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/__init__.pyc
--------------------------------------------------------------------------------
/tools/ctaBase.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | '''
4 | 本文件中包含了CTA模块中用到的一些基础设置、类和常量等。
5 | '''
6 | from __future__ import division
7 |
8 | # 把vn.trader根目录添加到python环境变量中
9 | import sys
10 | sys.path.append('..')
11 |
12 | from vtConstant import *
13 |
14 | # 常量定义
15 | # CTA引擎中涉及到的交易方向类型
16 | CTAORDER_BUY = u'买开'
17 | CTAORDER_SELL = u'卖平'
18 | CTAORDER_SELL_TODAY = u'卖平今'
19 | CTAORDER_SHORT = u'卖开'
20 | CTAORDER_COVER = u'买平'
21 | CTAORDER_COVER_TODAY = u'买平今'
22 | STATUS_PARTTRADED_PARTCANCELLED = u'部成部撤'
23 |
24 | # 本地停止单状态
25 | STOPORDER_WAITING = u'等待中'
26 | STOPORDER_CANCELLED = u'已撤销'
27 | STOPORDER_TRIGGERED = u'已触发'
28 |
29 | # 本地停止单前缀
30 | STOPORDERPREFIX = 'CtaStopOrder.'
31 |
32 | # CTA模块事件
33 | EVENT_CTA_LOG = 'eCtaLog' # CTA相关的日志事件
34 | EVENT_CTA_STRATEGY = 'eCtaStrategy.' # CTA策略状态变化事件
35 |
36 | # 数据库名称
37 | SETTING_DB_NAME = 'VnTrader_Setting_Db'
38 | TICK_DB_NAME = 'vnTrader_Tick_db'
39 | BAR_DB_NAME = 'vnTrader_Bar_Db'
40 | CAP_DB_NAME = 'vt_trader_cap_db'
41 | DAILY_DB_NAME = 'VnTrader_Daily_Db'
42 | MINUTE_DB_NAME = 'VnTrader_1Min_Db'
43 |
44 | ########################################################################
45 | class CtaBarData(object):
46 | """K线数据"""
47 |
48 | #----------------------------------------------------------------------
49 | def __init__(self):
50 | """Constructor"""
51 | self.vtSymbol = EMPTY_STRING # vt系统代码
52 | self.symbol = EMPTY_STRING # 代码
53 | self.exchange = EMPTY_STRING # 交易所
54 |
55 | self.open = EMPTY_FLOAT # OHLC
56 | self.high = EMPTY_FLOAT
57 | self.low = EMPTY_FLOAT
58 | self.close = EMPTY_FLOAT
59 |
60 | self.date = EMPTY_STRING # bar开始的时间,日期
61 | self.time = EMPTY_STRING # 时间
62 | self.datetime = None # python的datetime时间对象
63 |
64 | self.volume = EMPTY_INT # 成交量
65 | self.openInterest = EMPTY_INT # 持仓量
66 | self.turnover = EMPTY_FLOAT # 成交额
67 |
68 |
69 | ########################################################################
70 | class CtaTickData(object):
71 | """Tick数据"""
72 |
73 | #----------------------------------------------------------------------
74 | def __init__(self):
75 | """Constructor"""
76 | self.vtSymbol = EMPTY_STRING # vt系统代码
77 | self.symbol = EMPTY_STRING # 合约代码
78 | self.exchange = EMPTY_STRING # 交易所代码
79 |
80 | # 成交数据
81 | self.lastPrice = EMPTY_FLOAT # 最新成交价
82 | self.volume = EMPTY_INT # 最新成交量
83 | self.openInterest = EMPTY_INT # 持仓量
84 |
85 | self.upperLimit = EMPTY_FLOAT # 涨停价
86 | self.lowerLimit = EMPTY_FLOAT # 跌停价
87 |
88 | self.turnover = EMPTY_FLOAT # 成交额
89 |
90 | # tick的时间
91 | self.date = EMPTY_STRING # 日期
92 | self.time = EMPTY_STRING # 时间
93 | self.datetime = None # python的datetime时间对象
94 |
95 | # 五档行情
96 | self.bidPrice1 = EMPTY_FLOAT
97 | self.bidPrice2 = EMPTY_FLOAT
98 | self.bidPrice3 = EMPTY_FLOAT
99 | self.bidPrice4 = EMPTY_FLOAT
100 | self.bidPrice5 = EMPTY_FLOAT
101 |
102 | self.askPrice1 = EMPTY_FLOAT
103 | self.askPrice2 = EMPTY_FLOAT
104 | self.askPrice3 = EMPTY_FLOAT
105 | self.askPrice4 = EMPTY_FLOAT
106 | self.askPrice5 = EMPTY_FLOAT
107 |
108 | self.bidVolume1 = EMPTY_INT
109 | self.bidVolume2 = EMPTY_INT
110 | self.bidVolume3 = EMPTY_INT
111 | self.bidVolume4 = EMPTY_INT
112 | self.bidVolume5 = EMPTY_INT
113 |
114 | self.askVolume1 = EMPTY_INT
115 | self.askVolume2 = EMPTY_INT
116 | self.askVolume3 = EMPTY_INT
117 | self.askVolume4 = EMPTY_INT
118 | self.askVolume5 = EMPTY_INT
119 |
120 | ########################################################################
121 | class CtaCapData(object):
122 | """策略资产数据"""
123 |
124 | #----------------------------------------------------------------------
125 | def __init__(self):
126 | """Constructor"""
127 | self.name = EMPTY_STRING # 策略名
128 | self.datetime = None # 时间
129 | self.start = None # 开始时间
130 | self.date = None # 日期
131 | self.cap = EMPTY_FLOAT # 资金
132 | self.pnl = EMPTY_FLOAT # 当日盈亏
133 | self.drawdown = EMPTY_FLOAT # 回撤
134 |
--------------------------------------------------------------------------------
/tools/ctaBase.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/ctaBase.pyc
--------------------------------------------------------------------------------
/tools/ctaHistoryData.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/ctaHistoryData.pyc
--------------------------------------------------------------------------------
/tools/downcsv.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | import os,urllib2,urllib
3 | import multiprocessing
4 | from multiprocessing import freeze_support
5 | from utils import *
6 |
7 | currentP = 0
8 |
9 | #设置下载后存放的存储路径'C:\Users\yinyao\Desktop\Python code'
10 | path=r'.\historyData'
11 |
12 | #设置下载链接的路径
13 | #baseurl="http://120.136.172.92:52300/dclevel2/{}/{}/"
14 | #baseurl="http://120.136.172.92:52300/es/{}/{}/"
15 | #baseurl="http://120.136.172.92:52300/szse/{}/{}/"
16 | baseurl="http://120.136.172.92:52300/sse/{}/{}/"
17 | #baseurl="http://120.136.172.92:52300/shfe/{}/{}/"
18 |
19 |
20 | #定义下载函数downLoadPicFromURL(本地文件夹,网页URL)
21 | def downLoadCSVFromURL(dest_dir,URL):
22 | try:
23 | #urllib.urlretrieve(URL , dest_dir)
24 | with open(dest_dir,'wc') as f:
25 | f.write(getHTML(URL).read())
26 | return dest_dir
27 | except Exception, e:
28 | print(u'出错:%s' %e)
29 | print 'traceback.print_exc():'; traceback.print_exc()
30 |
31 | def downSymbolLevel2(symbol,year,month):
32 | global currentP
33 | pool = multiprocessing.Pool(processes=multiprocessing.cpu_count())
34 | urls = []
35 | files = []
36 | URL = baseurl.format(year,month)
37 | html = getHTML(URL)
38 | soup = BeautifulSoup(html, 'html.parser')
39 | #soup = get_page(URL)
40 | links = soup.find_all('a')
41 | for link in links:
42 | file_name = link.get_text()[1:]
43 | if symbol+'_' == file_name[0:len(symbol)+1]:
44 | url = URL+file_name
45 | dest_dir=os.path.join(path,file_name)
46 | urls.append(url)
47 | print u'开始下载 : '+url
48 | files.append(dest_dir)
49 | l = []
50 | currentP=0
51 | #----------------------------------------------------------------------
52 | def showProcessBar(result):
53 | """显示进度条"""
54 | global currentP
55 | currentP+=1
56 | print(result+u' 下载完成!')
57 | for url,filename in zip(urls,files):
58 | l.append(pool.apply_async(downLoadCSVFromURL, args=(filename,url,), callback=showProcessBar))
59 | pool.close()
60 | pool.join()
61 |
62 | #运行
63 | if __name__ == '__main__':
64 | freeze_support()
65 | symbols = ['600547']
66 | months = ['04','05','06','07','08','09']
67 | for symbol in symbols:
68 | for month in months:
69 | downSymbolLevel2(symbol,'2017',month)
70 | #downLoadPicFromURL(dest_dir,url)
71 |
72 |
--------------------------------------------------------------------------------
/tools/downcsv.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/downcsv.pyc
--------------------------------------------------------------------------------
/tools/peakdetect.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/peakdetect.pyc
--------------------------------------------------------------------------------
/tools/utils.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | import requests
3 | import lxml
4 | import time
5 | from bs4 import BeautifulSoup
6 | import time
7 | import sys
8 | import gzip
9 | import socket
10 | import urllib2
11 |
12 | user = 'admin'
13 | password = 'Qxy475'
14 |
15 | headers = {
16 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
17 | (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36',
18 | 'Accept-Encoding': 'gzip, deflate, sdch',
19 | 'Accept-Language': 'zh-CN,zh;q=0.8'
20 | }
21 |
22 | header = {
23 | 'Connection' : 'keep-alive',
24 | 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0',
25 | 'Accept-Language' : 'en-US,en;q=0.5',
26 | 'Accept-Encoding' : 'gzip, deflate'
27 | }
28 |
29 |
30 | def get_page(url):
31 | r = requests.get(url, headers=headers)
32 | try:
33 | soup = BeautifulSoup(r.content.decode("utf-8"), 'lxml')
34 | except UnicodeDecodeError:
35 | soup = BeautifulSoup(str(r.content,encoding="gbk"), 'lxml')
36 | return soup
37 |
38 | def getHTML(URL):
39 | passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
40 | passman.add_password(None, URL, user, password)
41 | handler = urllib2.HTTPBasicAuthHandler(passman)
42 | opener = urllib2.build_opener(handler)
43 | urllib2.install_opener(opener)
44 | html = urllib2.urlopen(urllib2.Request(URL, None, header))
45 | return html
46 |
--------------------------------------------------------------------------------
/tools/utils.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/utils.pyc
--------------------------------------------------------------------------------
/tools/vtConstant.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 |
3 | # 默认空值
4 | EMPTY_STRING = ''
5 | EMPTY_UNICODE = u''
6 | EMPTY_INT = 0
7 | EMPTY_FLOAT = 0.0
8 |
9 | # 方向常量
10 | DIRECTION_NONE = u'无方向'
11 | DIRECTION_LONG = u'多'
12 | DIRECTION_SHORT = u'空'
13 | DIRECTION_UNKNOWN = u'未知'
14 | DIRECTION_NET = u'净'
15 | DIRECTION_SELL = u'卖出' # IB接口
16 |
17 | # 开平常量
18 | OFFSET_NONE = u'无开平'
19 | OFFSET_OPEN = u'开仓'
20 | OFFSET_CLOSE = u'平仓'
21 | OFFSET_CLOSETODAY = u'平今'
22 | OFFSET_CLOSEYESTERDAY = u'平昨'
23 | OFFSET_UNKNOWN = u'未知'
24 |
25 | # 状态常量
26 | STATUS_NOTTRADED = u'未成交'
27 | STATUS_PARTTRADED = u'部分成交'
28 | STATUS_ALLTRADED = u'全部成交'
29 | STATUS_CANCELLED = u'已撤销'
30 | STATUS_PARTTRADED_PARTCANCELLED = u'部成部撤'
31 | STATUS_UNKNOWN = u'未知'
32 |
33 | # 合约类型常量
34 | PRODUCT_EQUITY = u'股票'
35 | PRODUCT_FUTURES = u'期货'
36 | PRODUCT_OPTION = u'期权'
37 | PRODUCT_INDEX = u'指数'
38 | PRODUCT_COMBINATION = u'组合'
39 | PRODUCT_FOREX = u'外汇'
40 | PRODUCT_UNKNOWN = u'未知'
41 | PRODUCT_SPOT = u'现货'
42 | PRODUCT_DEFER = u'延期'
43 | PRODUCT_NONE = ''
44 |
45 | # 价格类型常量
46 | PRICETYPE_LIMITPRICE = u'限价'
47 | PRICETYPE_MARKETPRICE = u'市价'
48 | PRICETYPE_FAK = u'FAK'
49 | PRICETYPE_FOK = u'FOK'
50 |
51 | # 期权类型
52 | OPTION_CALL = u'看涨期权'
53 | OPTION_PUT = u'看跌期权'
54 |
55 | # 交易所类型
56 | EXCHANGE_SSE = 'SSE' # 上交所
57 | EXCHANGE_SZSE = 'SZSE' # 深交所
58 | EXCHANGE_CFFEX = 'CFFEX' # 中金所
59 | EXCHANGE_SHFE = 'SHFE' # 上期所
60 | EXCHANGE_CZCE = 'CZCE' # 郑商所
61 | EXCHANGE_DCE = 'DCE' # 大商所
62 | EXCHANGE_SGE = 'SGE' # 上金所
63 | EXCHANGE_UNKNOWN = 'UNKNOWN'# 未知交易所
64 | EXCHANGE_NONE = '' # 空交易所
65 | EXCHANGE_HKEX = 'HKEX' # 港交所
66 |
67 | EXCHANGE_CME = 'CME'
68 | EXCHANGE_LME = 'LME'
69 | EXCHANGE_GTJA = 'GTJA'
70 | EXCHANGE_INE = 'INE'
71 | EXCHANGE_BXG = 'BXG'
72 |
73 | EXCHANGE_SGX = 'SGX'
74 |
75 | EXCHANGE_BMD = 'BMD'
76 | EXCHANGE_CBOT = 'CBOT'
77 | EXCHANGE_NYBOT = 'NYBOT'
78 | EXCHANGE_TOCOM = 'TOCOM'
79 | EXCHANGE_KRX = 'KRX'
80 | EXCHANGE_LIFFE = 'LIFFE'
81 | EXCHANGE_COMEX = 'COMEX'
82 |
83 | EXCHANGE_SMART = 'SMART' # IB智能路由(股票、期权)
84 | EXCHANGE_NYMEX = 'NYMEX' # IB 期货
85 | EXCHANGE_GLOBEX = 'GLOBEX' # CME电子交易平台
86 | EXCHANGE_IDEALPRO = 'IDEALPRO' # IB外汇ECN
87 |
88 | EXCHANGE_OANDA = 'OANDA' # OANDA外汇做市商
89 |
90 | # 货币类型
91 | CURRENCY_USD = 'USD' # 美元
92 | CURRENCY_CNY = 'CNY' # 人民币
93 | CURRENCY_UNKNOWN = 'UNKNOWN' # 未知货币
94 | CURRENCY_NONE = '' # 空货币
95 |
--------------------------------------------------------------------------------
/tools/vtConstant.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonnejs/PythonLab/e9765430e42508ecbcd8c854c42f640c7bca3567/tools/vtConstant.pyc
--------------------------------------------------------------------------------
/trade/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.swp
4 | /log/
5 | /trade/
6 |
--------------------------------------------------------------------------------
/uiBasicIO.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # PyQt
3 | from uiBasicWidget import *
4 | # Others
5 | import os
6 | import imp
7 | import sys
8 | import json
9 | import glob
10 | from functools import partial
11 | from collections import OrderedDict
12 |
13 | # 导入按钮函数
14 | #---------------------------------------------------------------------------------------
15 | ALL_FUNC_BUTTON = []
16 | funcBtnPath = os.getcwd() + '/func-button/'
17 | allPath = glob.glob(funcBtnPath+r'*.py')
18 | for path in allPath:
19 | fileName = path.split("\\")[-1]
20 | modelName = fileName.split(".")[0]
21 | ALL_FUNC_BUTTON.append(modelName)
22 | imp.load_source('ctaFuncButttons',path)
23 |
24 | BUTTON_FUNC = {}
25 | from ctaFuncButttons import *
26 | for func_bt in ALL_FUNC_BUTTON:
27 | fn_obj = getattr(sys.modules['ctaFuncButttons'], func_bt)
28 | BUTTON_FUNC[func_bt] = fn_obj
29 |
30 | # 字符串转换
31 | #---------------------------------------------------------------------------------------
32 | try:
33 | _fromUtf8 = QtCore.QString.fromUtf8
34 | except AttributeError:
35 | def _fromUtf8(s):
36 | return s
37 |
38 | ########################################################################
39 | class uiBasicIO(QWidget):
40 | """通过json文件,自动生成输入框和按钮的元类"""
41 |
42 | #----------------------------------------------------------------------
43 | def __init__(self,parent=None,inpFile='',btnFile=''):
44 | """初始化函数"""
45 | super(uiBasicIO,self).__init__(parent)
46 |
47 | # 输入框数据
48 | self.classDict = OrderedDict()
49 | self.labelDict = {}
50 | self.widthDict = {}
51 | self.typeDict = {}
52 | self.evalDict = {}
53 | self.editDict = {}
54 | # 按钮数据
55 | self.bClassDict = OrderedDict()
56 | self.bWidthDict = {}
57 | self.bFunDict = {}
58 | self.buttonDict = {}
59 | # 输入框和按钮
60 | self.groupInput = None
61 | self.groupProcess = None
62 | # 输入框和按钮的配置文件
63 | self.inpFile = inpFile
64 | self.btnFile = btnFile
65 |
66 | self.loadInputSetting()
67 | self.loadButtonSetting()
68 | self.initBasicUi()
69 |
70 | #----------------------------------------------------------------------
71 | def getInputParamByName(self,name):
72 | """获得输入框参数值"""
73 | typeName = self.typeDict[name]
74 | editCell = self.editDict[name]
75 | val = str(editCell.currentText()) if typeName == 'List' else str(editCell.text())
76 | try:
77 | return (eval(val) if self.evalDict[name] else val)
78 | except:
79 | return val
80 |
81 | #----------------------------------------------------------------------
82 | def loadInputSetting(self):
83 | """载入输入框界面配置"""
84 | settingFile = self.inpFile
85 | with open(settingFile) as f:
86 | for setting in json.load(f):
87 | name = setting['name']
88 | label = setting['label']
89 | typeName = setting['type']
90 | evalType = setting['eval']
91 | width = setting['width']
92 | className = setting['class']
93 | default = setting['default']
94 | # 标签
95 | self.labelDict[name] = QLabel(label)
96 | self.labelDict[name].setAlignment(QtCore.Qt.AlignCenter)
97 | # 宽度
98 | self.widthDict[name] = width
99 | # 输入框类型
100 | self.typeDict[name] = typeName
101 | self.evalDict[name] = evalType
102 | # 分类
103 | if className in self.classDict:
104 | self.classDict[className].append(name)
105 | else:
106 | self.classDict[className] = [name]
107 | # 输入框
108 | if typeName == 'Edit':
109 | self.editDict[name] = QLineEdit()
110 | self.editDict[name].setText(default)
111 | elif typeName == 'List':
112 | self.editDict[name] = QComboBox()
113 | self.editDict[name].addItems(eval(setting['ListVar']))
114 |
115 | #----------------------------------------------------------------------
116 | def loadButtonSetting(self):
117 | """载入按钮界面配置"""
118 | settingFile = self.btnFile
119 | with open(settingFile) as f:
120 | for setting in json.load(f):
121 | label = setting['label']
122 | func = setting['func']
123 | width = setting['width']
124 | className = setting['class']
125 | style = setting['style']
126 | # 按钮
127 | self.buttonDict[func] = QPushButton(label)
128 | self.buttonDict[func].setObjectName(_fromUtf8(style))
129 | self.buttonDict[func].clicked.connect(partial(BUTTON_FUNC[func],self))
130 | # 宽度
131 | self.bWidthDict[func] = width
132 | # 分类
133 | if className in self.bClassDict:
134 | self.bClassDict[className].append(func)
135 | else:
136 | self.bClassDict[className] = [func]
137 |
138 | #----------------------------------------------------------------------
139 | def initBasicUi(self):
140 | """初始化界面"""
141 | # 根据配置文件生成输入框界面
142 | self.groupInput = QGroupBox()
143 | self.groupInput.setTitle(u'')
144 | gridup = QGridLayout()
145 | i = 0
146 | for className in self.classDict:
147 | classIndex = i
148 | # 标题和输入框
149 | for name in self.classDict[className]:
150 | width = self.widthDict[name]
151 | qLabel = self.labelDict[name]
152 | qEdit = self.editDict[name]
153 | gridup.addWidget(qLabel, 1, i)
154 | gridup.addWidget(qEdit, 2, i)
155 | gridup.setColumnStretch(i, width)
156 | i+=1
157 | # 分类标题
158 | qcLabel = QLabel(className)
159 | qcLabel.setAlignment(QtCore.Qt.AlignCenter)
160 | qcLabel.setFont(QFont("Roman times",10,QFont.Bold))
161 | gridup.addWidget(qcLabel, 0, classIndex,1,i-classIndex)
162 | # 分隔符
163 | for j in xrange(0,3):
164 | qcSplit = QLabel(u'|')
165 | qcSplit.setAlignment(QtCore.Qt.AlignCenter)
166 | gridup.addWidget(qcSplit, j, i)
167 | i+=1
168 | self.groupInput.setLayout(gridup)
169 |
170 | # 根据配置文件生成按钮界面
171 | self.groupProcess = QGroupBox()
172 | self.groupProcess.setTitle(u'')
173 | griddown = QGridLayout()
174 | i = 0
175 | for className in self.bClassDict:
176 | classIndex = i
177 | # 标题和输入框
178 | for name in self.bClassDict[className]:
179 | width = self.bWidthDict[name]
180 | qButton = self.buttonDict[name]
181 | griddown.addWidget(qButton, 1, i)
182 | griddown.setColumnStretch(i, width)
183 | i+=1
184 | # 分类标题
185 | qcLabel = QLabel(className)
186 | qcLabel.setAlignment(QtCore.Qt.AlignCenter)
187 | qcLabel.setFont(QFont("Roman times",10,QFont.Bold))
188 | griddown.addWidget(qcLabel, 0, classIndex,1,i-classIndex)
189 | # 分隔符
190 | for j in xrange(0,2):
191 | qcSplit = QLabel(u'|')
192 | qcSplit.setAlignment(QtCore.Qt.AlignCenter)
193 | griddown.addWidget(qcSplit, j, i)
194 | i+=1
195 | self.groupProcess.setLayout(griddown)
196 |
197 |
--------------------------------------------------------------------------------
/uiCrosshair.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | import sys,os
3 | import pyqtgraph as pg
4 | import datetime as dt
5 | import numpy as np
6 | import traceback
7 |
8 | from pyqtgraph.Qt import QtGui, QtCore
9 | from pyqtgraph.Point import Point
10 |
11 | ########################################################################
12 | # 十字光标支持
13 | ########################################################################
14 | class Crosshair(QtCore.QObject):
15 | """
16 | 此类给pg.PlotWidget()添加crossHair功能,PlotWidget实例需要初始化时传入
17 | """
18 | signal = QtCore.pyqtSignal(type(tuple([])))
19 | signalInfo = QtCore.pyqtSignal(float,float)
20 | #----------------------------------------------------------------------
21 | def __init__(self,parent,master):
22 | """Constructor"""
23 | self.__view = parent
24 | self.master = master
25 | super(Crosshair, self).__init__()
26 |
27 | self.xAxis = 0
28 | self.yAxis = 0
29 |
30 | self.datas = None
31 |
32 | self.yAxises = [0 for i in range(3)]
33 | self.leftX = [0 for i in range(3)]
34 | self.showHLine = [False for i in range(3)]
35 | self.textPrices = [pg.TextItem('',anchor=(1,1)) for i in range(3)]
36 | self.views = [parent.centralWidget.getItem(i+1,0) for i in range(3)]
37 | self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
38 | self.vLines = [pg.InfiniteLine(angle=90, movable=False) for i in range(3)]
39 | self.hLines = [pg.InfiniteLine(angle=0, movable=False) for i in range(3)]
40 |
41 | #mid 在y轴动态跟随最新价显示最新价和最新时间
42 | self.__textDate = pg.TextItem('date',anchor=(1,1))
43 | self.__textInfo = pg.TextItem('lastBarInfo')
44 | self.__textSig = pg.TextItem('lastSigInfo',anchor=(1,0))
45 | self.__textSubSig = pg.TextItem('lastSubSigInfo',anchor=(1,0))
46 | self.__textVolume = pg.TextItem('lastBarVolume',anchor=(1,0))
47 |
48 | self.__textDate.setZValue(2)
49 | self.__textInfo.setZValue(2)
50 | self.__textSig.setZValue(2)
51 | self.__textSubSig.setZValue(2)
52 | self.__textVolume.setZValue(2)
53 | self.__textInfo.border = pg.mkPen(color=(230, 255, 0, 255), width=1.2)
54 |
55 | for i in range(3):
56 | self.textPrices[i].setZValue(2)
57 | self.vLines[i].setPos(0)
58 | self.hLines[i].setPos(0)
59 | self.vLines[i].setZValue(0)
60 | self.hLines[i].setZValue(0)
61 | self.views[i].addItem(self.vLines[i])
62 | self.views[i].addItem(self.hLines[i])
63 | self.views[i].addItem(self.textPrices[i])
64 |
65 | self.views[0].addItem(self.__textInfo, ignoreBounds=True)
66 | self.views[0].addItem(self.__textSig, ignoreBounds=True)
67 | self.views[1].addItem(self.__textVolume, ignoreBounds=True)
68 | self.views[2].addItem(self.__textDate, ignoreBounds=True)
69 | self.views[2].addItem(self.__textSubSig, ignoreBounds=True)
70 | self.proxy = pg.SignalProxy(self.__view.scene().sigMouseMoved, rateLimit=360, slot=self.__mouseMoved)
71 | # 跨线程刷新界面支持
72 | self.signal.connect(self.update)
73 | self.signalInfo.connect(self.plotInfo)
74 |
75 | #----------------------------------------------------------------------
76 | def update(self,pos):
77 | """刷新界面显示"""
78 | xAxis,yAxis = pos
79 | xAxis,yAxis = (self.xAxis,self.yAxis) if xAxis is None else (xAxis,yAxis)
80 | self.moveTo(xAxis,yAxis)
81 |
82 | #----------------------------------------------------------------------
83 | def __mouseMoved(self,evt):
84 | """鼠标移动回调"""
85 | pos = evt[0]
86 | self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
87 | for i in range(3):
88 | self.showHLine[i] = False
89 | if self.rects[i].contains(pos):
90 | mousePoint = self.views[i].vb.mapSceneToView(pos)
91 | xAxis = mousePoint.x()
92 | yAxis = mousePoint.y()
93 | self.yAxises[i] = yAxis
94 | self.showHLine[i] = True
95 | self.moveTo(xAxis,yAxis)
96 |
97 | #----------------------------------------------------------------------
98 | def moveTo(self,xAxis,yAxis):
99 | xAxis,yAxis = (self.xAxis,self.yAxis) if xAxis is None else (int(xAxis),yAxis)
100 | self.rects = [self.views[i].sceneBoundingRect() for i in range(3)]
101 | if not xAxis or not yAxis:
102 | return
103 | self.xAxis = xAxis
104 | self.yAxis = yAxis
105 | self.vhLinesSetXY(xAxis,yAxis)
106 | self.plotInfo(xAxis,yAxis)
107 | self.master.volume.update()
108 |
109 | #----------------------------------------------------------------------
110 | def vhLinesSetXY(self,xAxis,yAxis):
111 | """水平和竖线位置设置"""
112 | for i in range(3):
113 | self.vLines[i].setPos(xAxis)
114 | if self.showHLine[i]:
115 | self.hLines[i].setPos(yAxis if i==0 else self.yAxises[i])
116 | self.hLines[i].show()
117 | else:
118 | self.hLines[i].hide()
119 |
120 | #----------------------------------------------------------------------
121 | def plotInfo(self,xAxis,yAxis):
122 | """
123 | 被嵌入的plotWidget在需要的时候通过调用此方法显示K线信息
124 | """
125 | if self.datas is None:
126 | return
127 | try:
128 | # 获取K线数据
129 | data = self.datas[xAxis]
130 | lastdata = self.datas[xAxis-1]
131 | tickDatetime = data['datetime']
132 | openPrice = data['open']
133 | closePrice = data['close']
134 | lowPrice = data['low']
135 | highPrice = data['high']
136 | volume = int(data['volume'])
137 | openInterest = int(data['openInterest'])
138 | preClosePrice = lastdata['close']
139 | tradePrice = abs(self.master.listSig[xAxis])
140 | except Exception, e:
141 | return
142 |
143 | if(isinstance(tickDatetime,dt.datetime)):
144 | datetimeText = dt.datetime.strftime(tickDatetime,'%Y-%m-%d %H:%M:%S')
145 | dateText = dt.datetime.strftime(tickDatetime,'%Y-%m-%d')
146 | timeText = dt.datetime.strftime(tickDatetime,'%H:%M:%S')
147 | else:
148 | datetimeText = ""
149 | dateText = ""
150 | timeText = ""
151 |
152 | # 显示所有的主图技术指标
153 | html = u'
'
154 | for sig in self.master.sigData:
155 | val = self.master.sigData[sig][xAxis]
156 | col = self.master.sigColor[sig]
157 | html+= u' %s:%.2f' %(col,sig,val)
158 | html+=u'
'
159 | self.__textSig.setHtml(html)
160 |
161 | # 显示所有的主图技术指标
162 | html = u''
163 | for sig in self.master.subSigData:
164 | val = self.master.subSigData[sig][xAxis]
165 | col = self.master.subSigColor[sig]
166 | html+= u' %s:%.2f' %(col,sig,val)
167 | html+=u'
'
168 | self.__textSubSig.setHtml(html)
169 |
170 |
171 | # 和上一个收盘价比较,决定K线信息的字符颜色
172 | cOpen = 'red' if openPrice > preClosePrice else 'green'
173 | cClose = 'red' if closePrice > preClosePrice else 'green'
174 | cHigh = 'red' if highPrice > preClosePrice else 'green'
175 | cLow = 'red' if lowPrice > preClosePrice else 'green'
176 |
177 | self.__textInfo.setHtml(
178 | u'\
179 | 日期
\
180 | %s
\
181 | 时间
\
182 | %s
\
183 | 价格
\
184 | (开) %.3f
\
185 | (高) %.3f
\
186 | (低) %.3f
\
187 | (收) %.3f
\
188 | 成交量
\
189 | (量) %d
\
190 | 成交价
\
191 | (价) %.3f
\
192 |
'\
193 | % (dateText,timeText,cOpen,openPrice,cHigh,highPrice,\
194 | cLow,lowPrice,cClose,closePrice,volume,tradePrice))
195 | self.__textDate.setHtml(
196 | '\
197 | %s\
198 |
'\
199 | % (datetimeText))
200 |
201 | self.__textVolume.setHtml(
202 | '\
203 | VOL : %.3f\
204 |
'\
205 | % (volume))
206 | # 坐标轴宽度
207 | rightAxisWidth = self.views[0].getAxis('right').width()
208 | bottomAxisHeight = self.views[2].getAxis('bottom').height()
209 | offset = QtCore.QPointF(rightAxisWidth,bottomAxisHeight)
210 |
211 | # 各个顶点
212 | tl = [self.views[i].vb.mapSceneToView(self.rects[i].topLeft()) for i in range(3)]
213 | br = [self.views[i].vb.mapSceneToView(self.rects[i].bottomRight()-offset) for i in range(3)]
214 |
215 | # 显示价格
216 | for i in range(3):
217 | if self.showHLine[i]:
218 | self.textPrices[i].setHtml(
219 | '\
220 | \
221 | %0.3f\
222 | \
223 |
'\
224 | % (yAxis if i==0 else self.yAxises[i]))
225 | self.textPrices[i].setPos(br[i].x(),yAxis if i==0 else self.yAxises[i])
226 | self.textPrices[i].show()
227 | else:
228 | self.textPrices[i].hide()
229 |
230 |
231 | # 设置坐标
232 | self.__textInfo.setPos(tl[0])
233 | self.__textSig.setPos(br[0].x(),tl[0].y())
234 | self.__textSubSig.setPos(br[2].x(),tl[2].y())
235 | self.__textVolume.setPos(br[1].x(),tl[1].y())
236 |
237 | # 修改对称方式防止遮挡
238 | self.__textDate.anchor = Point((1,1)) if xAxis > self.master.index else Point((0,1))
239 | self.__textDate.setPos(xAxis,br[2].y())
240 |
--------------------------------------------------------------------------------
/uiCtaKLine.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from uiBasicIO import uiBasicIO
3 | from uiKLine import KLineWidget
4 | # PyQt
5 | from qtpy.QtGui import *
6 | from qtpy.QtCore import *
7 | from qtpy.QtWidgets import *
8 | from qtpy import QtGui,QtCore
9 |
10 | from collections import deque
11 |
12 | import pandas as pd
13 |
14 |
15 |
16 | ########################################################################
17 | class ctaKLine(uiBasicIO):
18 |
19 | """K线向量化回测工具"""
20 |
21 | dbClient = None
22 | signal = QtCore.Signal(type({}))
23 |
24 | # ----------------------------------------------------------------------
25 | def __init__(self, ctaEngine, eventEngine, parent=None):
26 | """初始化函数"""
27 | import ctaBase
28 | super(ctaKLine,self).__init__(parent,\
29 | 'json\\uiCtaKLine_input.json',\
30 | 'json\\uiCtaKLine_button.json') # 输入配置文件,按钮配置文件
31 |
32 | # 用于计算因子的数据
33 | self.bars = deque([])
34 | self.pdBars = pd.DataFrame()
35 | self.signals = deque([])
36 | self.signalsOpen = deque([])
37 | self.states = []
38 | self.pnl = []
39 | self.stateData = {}
40 |
41 | self.spdData = pd.DataFrame()
42 |
43 | self.datas = None
44 | self.vtSymbol = ""
45 | self.vtSymbol1 = ""
46 | self.mode = 'deal'
47 |
48 | self.canvas = KLineWidget(self)
49 | self.signal.connect(self.loadData)
50 |
51 | self.initUi()
52 |
53 | # -----------------------------------------------
54 | def loadData(self,data):
55 | """载入所有数据"""
56 | self.bars = data['bar']
57 | self.states = data['state']
58 | self.pnl = data['pnl']
59 | self.signals = data['deal']
60 | self.signalsOpen = data['dealOpen']
61 | kTool = self.canvas
62 | for sig in kTool.sigPlots:
63 | kTool.pwKL.removeItem(kTool.sigPlots[sig])
64 | kTool.sigData = {}
65 | kTool.sigPlots = {}
66 | for sig in kTool.subSigPlots:
67 | kTool.pwOI.removeItem(kTool.subSigPlots[sig])
68 | kTool.subSigData = {}
69 | kTool.subSigPlots = {}
70 | print u'正在准备回测结果数据....'
71 | self.canvas.clearData()
72 | self.pdBars = pd.DataFrame(list(self.bars))
73 | self.pdBars = self.pdBars[['datetime','open','close','low','high','volume','openInterest']].set_index('datetime')
74 | self.canvas.loadData(self.pdBars)
75 | self.canvas.updateSig(self.signals,self.signalsOpen)
76 | self.spdData = pd.DataFrame(self.states)
77 | self.stateData = self.spdData.to_records()
78 | allState = filter(lambda s:s not in ['trading','inited','pos'],self.spdData.columns)
79 | allState = filter(lambda s:isinstance(self.spdData[s][0], (int, long, float)),allState)
80 | self.spdData['pnl'] = self.pnl
81 | self.spdData = self.spdData[self.spdData['pnl']!=0][allState+['pnl']]
82 | #self.spdData['pnl'] = ['p' if p > 0 else 'l' for p in self.pnl]
83 | self.editDict['signalName'].clear()
84 | self.editDict['signalName'].addItems(allState)
85 | print u'数据准备完成!'
86 |
87 | #-----------------------------------------------
88 | def loadSig(self,data):
89 | """载入K线信号"""
90 | self.states = data['state']
91 | self.pnl = data['pnl']
92 | self.signals = data['deal']
93 | self.signalsOpen = data['dealOpen']
94 | kTool = self.canvas
95 | for sig in kTool.sigPlots:
96 | kTool.pwKL.removeItem(kTool.sigPlots[sig])
97 | kTool.sigData = {}
98 | kTool.sigPlots = {}
99 | for sig in kTool.subSigPlots:
100 | kTool.pwOI.removeItem(kTool.subSigPlots[sig])
101 | kTool.subSigData = {}
102 | kTool.subSigPlots = {}
103 | self.canvas.updateSig(self.signals)
104 | self.spdData = pd.DataFrame(self.states)
105 | self.stateData = self.spdData.to_records()
106 | allState = filter(lambda s:s not in ['trading','inited','pos'],self.spdData.columns)
107 | allState = filter(lambda s:isinstance(self.spdData[s][0], (int, long, float)),allState)
108 | self.spdData['pnl'] = self.pnl
109 | self.spdData = self.spdData[self.spdData['pnl']!=0][allState+['pnl']]
110 | #self.spdData['pnl'] = ['p' if p > 0 else 'l' for p in self.pnl]
111 | self.editDict['signalName'].clear()
112 | self.editDict['signalName'].addItems(allState)
113 |
114 | #----------------------------------------------------------------------
115 | def initUi(self):
116 | """初始化界面"""
117 | hbox = QHBoxLayout()
118 | hbox.addWidget(self.groupInput)
119 | hbox.addWidget(self.groupProcess)
120 | vbox = QVBoxLayout()
121 | vbox.addLayout(hbox)
122 | vbox.addWidget(self.canvas)
123 | self.setLayout(vbox)
124 |
125 | #----------------------------------------------------------------------
126 | def setSymbol(self, symbol):
127 | """设置合约信息"""
128 | self.vtSymbol = symbol
129 | self.editDict['symbol'].setText(symbol)
130 | if '-' in symbol:
131 | self.vtSymbol = symbol.split('-')[0]
132 | self.vtSymbol1 = symbol.split('-')[1]
133 | else:
134 | self.vtSymbol1 = None
135 |
136 | ########################################################################
137 | import sys
138 | if __name__ == '__main__':
139 | app = QApplication(sys.argv)
140 | # 界面设置
141 | cfgfile = QtCore.QFile('css.qss')
142 | cfgfile.open(QtCore.QFile.ReadOnly)
143 | styleSheet = cfgfile.readAll()
144 | styleSheet = unicode(styleSheet, encoding='utf8')
145 | app.setStyleSheet(styleSheet)
146 | # K线界面
147 | ui = ctaKLine()
148 | ui.show()
149 | app.exec_()
150 |
--------------------------------------------------------------------------------
/uiCtaTaskWidget.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | '''
3 | CTA回测任务模块相关的GUI控制组件
4 | '''
5 | import ctaTaskPool
6 | import pandas as pd
7 | from eventEngine import *
8 | from uiBasicWidget import QtGui, QtCore, BASIC_FONT, BasicDialog
9 | from qtpy.QtWidgets import *
10 |
11 | STYLESHEET_START = 'background-color: rgb(111,255,244); color: black'
12 | STYLESHEET_STOP = 'background-color: rgb(255,201,111); color: black'
13 |
14 | # 字符串转换
15 | #---------------------------------------------------------------------------------------
16 | try:
17 | _fromUtf8 = QtCore.QString.fromUtf8
18 | except AttributeError:
19 | def _fromUtf8(s):
20 | return s
21 |
22 | kLoader = None
23 | ########################################################################
24 | class TaskActiveButton(QPushButton):
25 | """激活按钮"""
26 | #----------------------------------------------------------------------
27 | def __init__(self, taskName='', parent=None):
28 | """Constructor"""
29 | super(TaskActiveButton, self).__init__(parent)
30 |
31 | self.active = ctaTaskPool.taskPool.getTask(taskName).state
32 | self.taskName = taskName
33 | if not (self.active == u'已完成' or self.active==u'已停止'):
34 | self.setStarted()
35 | else:
36 | self.setStopped()
37 |
38 | self.clicked.connect(self.buttonClicked)
39 |
40 | #----------------------------------------------------------------------
41 | def buttonClicked(self):
42 | """改变运行模式"""
43 | if not (self.active == u'已完成' or self.active==u'已停止'):
44 | self.stop()
45 |
46 | #----------------------------------------------------------------------
47 | def stop(self):
48 | """停止"""
49 | ctaTaskPool.taskPool.stopTask(self.taskName)
50 | self.setStopped()
51 |
52 | #----------------------------------------------------------------------
53 | def start(self):
54 | """启动"""
55 | ctaTaskPool.taskPool.startTask(self.taskName)
56 | self.setStarted()
57 |
58 | #----------------------------------------------------------------------
59 | def setStarted(self):
60 | """算法启动"""
61 | self.setText(self.active)
62 | self.setStyleSheet(STYLESHEET_START)
63 |
64 | #----------------------------------------------------------------------
65 | def setStopped(self):
66 | """算法停止"""
67 | self.active = u'已停止'
68 | self.setText(self.active)
69 | self.setStyleSheet(STYLESHEET_STOP)
70 |
71 | ########################################################################
72 | class TaskDisplayButton(QPushButton):
73 | """显示回测结果"""
74 | #----------------------------------------------------------------------
75 | def __init__(self, taskName='', parent=None):
76 | """Constructor"""
77 | super(TaskDisplayButton, self).__init__(parent)
78 | name = ctaTaskPool.taskPool.getTask(taskName).results.get('capital',0)
79 | self.setText(str(name))
80 | self.setStyleSheet(STYLESHEET_STOP)
81 |
82 | self.active = False
83 | self.taskName = taskName
84 | self.clicked.connect(self.buttonClicked)
85 |
86 | #----------------------------------------------------------------------
87 | def buttonClicked(self):
88 | """显示回测结果"""
89 | ctaTaskPool.taskPool.getTask(self.taskName).show()
90 | datas = ctaTaskPool.taskPool.getTask(self.taskName).results.get('datas')
91 | if datas:
92 | kLoader.loadData(datas)
93 |
94 | ########################################################################
95 | class TaskLogButton(QPushButton):
96 | """显示回测结果"""
97 | #----------------------------------------------------------------------
98 | def __init__(self, taskName='', parent=None):
99 | """Constructor"""
100 | super(TaskLogButton, self).__init__(parent)
101 | name = ctaTaskPool.taskPool.getTask(taskName).results.get('totalResult',0)
102 | self.setText(str(name))
103 | self.setStyleSheet(STYLESHEET_STOP)
104 |
105 | self.active = False
106 | self.taskName = taskName
107 | self.clicked.connect(self.buttonClicked)
108 |
109 | #----------------------------------------------------------------------
110 | def buttonClicked(self):
111 | """显示回测结果"""
112 | ctaTaskPool.taskPool.getTask(self.taskName).log()
113 |
114 | ########################################################################
115 | class TaskParamButton(QPushButton):
116 | """查看参数按钮"""
117 | tp = None
118 | #----------------------------------------------------------------------
119 | def __init__(self, taskName='', parent=None):
120 | """Constructor"""
121 | super(TaskParamButton, self).__init__(parent)
122 | name = ctaTaskPool.taskPool.getTask(taskName).setting.get('symbolList','None')
123 | self.setText(str(name))
124 | self.setStyleSheet(STYLESHEET_STOP)
125 |
126 | self.taskName = taskName
127 | self.clicked.connect(self.buttonClicked)
128 |
129 | #----------------------------------------------------------------------
130 | def buttonClicked(self):
131 | """显示回测结果"""
132 | filterList = ['name','className','mPrice','backtesting']
133 | kvList = ctaTaskPool.taskPool.getTask(self.taskName).setting.items()
134 | param = dict([it for it in kvList if it[0] not in filterList])
135 | self.__class__.tp = TaskParamWidget(param)
136 | self.__class__.tp.show()
137 |
138 | ########################################################################
139 | class TaskTable(QTableWidget):
140 | """任务管理组件"""
141 |
142 | #----------------------------------------------------------------------
143 | def __init__(self, parent=None):
144 | """Constructor"""
145 | super(TaskTable, self).__init__(parent)
146 |
147 | self.buttonActiveDict = {}
148 |
149 | self.initUi()
150 |
151 | #----------------------------------------------------------------------
152 | def initUi(self):
153 | """初始化表格"""
154 | headers = [u'任务名',
155 | u'策略类',
156 | u'耗时',
157 | u'模式',
158 | u'起止时间',
159 | u'状态',
160 | u'参数',
161 | u'日志',
162 | u'结果']
163 |
164 | self.setColumnCount(len(headers))
165 | self.setHorizontalHeaderLabels(headers)
166 | self.horizontalHeader().setResizeMode(QHeaderView.Interactive)
167 | self.setColumnWidth(0, 100)
168 | self.setColumnWidth(1, 150)
169 | self.setColumnWidth(2, 200)
170 | self.setColumnWidth(3, 100)
171 | self.setColumnWidth(4, 350)
172 | self.setColumnWidth(5, 100)
173 | self.setColumnWidth(6, 150)
174 | self.setColumnWidth(7, 150)
175 | self.setColumnWidth(8, 150)
176 |
177 | self.verticalHeader().setVisible(False)
178 | self.setEditTriggers(self.NoEditTriggers)
179 | self.setSortingEnabled(False)
180 | self.setSelectionBehavior(QTableWidget.SelectRows)
181 |
182 | #----------------------------------------------------------------------
183 | def initCells(self):
184 | """初始化单元格"""
185 |
186 | l = ctaTaskPool.taskPool.allTask.items()
187 | self.setRowCount(len(l))
188 | row = 0
189 |
190 | for name, task in l:
191 | cellTaskName = QTableWidgetItem(name)
192 | cellTaskTime = QTableWidgetItem(str(task.runTM))
193 | cellTaskRunM = QTableWidgetItem(task.runmode)
194 | cellStrCls = QTableWidgetItem(task.setting.get('className'))
195 | timeStr = ' ~ '.join([task.setting.get('StartTime'),task.setting.get('EndTime')])
196 | cellStrTime = QTableWidgetItem(timeStr)
197 | buttonActive = TaskActiveButton(name)
198 | buttonParam = TaskParamButton(name)
199 | buttonDisplay = TaskDisplayButton(name)
200 | buttonLog = TaskLogButton(name)
201 |
202 | self.setItem(row, 0, cellTaskName)
203 | self.setItem(row, 1, cellStrCls)
204 | self.setItem(row, 2, cellTaskTime)
205 | self.setItem(row, 3, cellTaskRunM)
206 | self.setItem(row, 4, cellStrTime)
207 | self.setCellWidget(row, 5, buttonActive)
208 | self.setCellWidget(row, 6, buttonParam)
209 | self.setCellWidget(row, 7, buttonLog)
210 | self.setCellWidget(row, 8, buttonDisplay)
211 |
212 | self.buttonActiveDict[name] = buttonActive
213 | row += 1
214 |
215 | #----------------------------------------------------------------------
216 | def stopAll(self):
217 | """停止所有"""
218 | for button in self.buttonActiveDict.values():
219 | button.stop()
220 |
221 | #----------------------------------------------------------------------
222 | def clearAll(self):
223 | """清空所有"""
224 | ctaTaskPool.taskPool.allTask = {}
225 | self.initCells()
226 |
227 | #----------------------------------------------------------------------
228 | def showAll(self):
229 | """合并显示所有"""
230 | from ctaBacktesting import showBtResult
231 | allres = None
232 | def mergeBtRes(d0,d1):
233 | d = {}
234 | cap0 = d0['capitalList'][-1]
235 | ddn0 = d0['drawdownList'][-1]
236 | d['name'] = 'res'
237 | d['stTime'] = d0['stTime']
238 | d['capital'] = d0['capital']+d1['capital']
239 | d['maxCapital'] = max(d0['maxCapital'],cap0+d1['maxCapital'])
240 | d['drawdown'] = d0['drawdown']+d1['drawdown']
241 | d['totalResult'] = d0['totalResult']+d1['totalResult']
242 | d['totalTurnover'] = d0['totalTurnover']+d1['totalTurnover']
243 | d['totalCommission'] = d0['totalCommission']+d1['totalCommission']
244 | d['totalSlippage'] = d0['totalSlippage']+d1['totalSlippage']
245 | d['timeList'] = d0['timeList']+d1['timeList']
246 | d['pnlList'] = d0['pnlList']+d1['pnlList']
247 | d['capitalList'] = d0['capitalList']+[c+cap0 for c in d1['capitalList']]
248 | d['drawdownList'] = d0['drawdownList']+[c+ddn0 for c in d1['drawdownList']]
249 | d['winningRate'] = (d0['winningRate']*d0['totalResult']+d1['winningRate']*d1['totalResult'])/d['totalResult']
250 | d['averageWinning'] = (d0['winningRate']*d0['averageWinning']*d0['totalResult']+d1['winningRate']*d1['averageWinning']*d1['totalResult'])/d['totalResult']
251 | d['averageLosing'] = ((1-d0['winningRate'])*d0['averageLosing']*d0['totalResult']+(1-d1['winningRate'])*d1['averageLosing']*d1['totalResult'])/d['totalResult']
252 | d['profitLossRatio'] = d['averageWinning']/d['averageLosing']
253 | d['resList'] = d0['resList']+d1['resList']
254 | return d
255 | results = [td.results for td in ctaTaskPool.taskPool.allTask.values() if td.results.get('timeList',[])]
256 | allres = reduce(mergeBtRes,sorted(results,key=lambda d:d['stTime']))
257 | if allres: showBtResult(allres)
258 |
259 |
260 | ########################################################################
261 | class TaskManager(QWidget):
262 | """任务管理主界面"""
263 | signal = QtCore.Signal(type(Event()))
264 | #----------------------------------------------------------------------
265 | def __init__(self, ctaEngine, eventEngine, parent=None):
266 | """Constructor"""
267 | super(TaskManager, self).__init__(parent)
268 |
269 | self.taskTab = TaskTable()
270 | self.eventEngine = eventEngine
271 | self.initUi()
272 | self.signal.connect(self.init)
273 | self.eventEngine.register(EVENT_TIMER, self.signal.emit)
274 |
275 | #----------------------------------------------------------------------
276 | def initUi(self):
277 | """初始化界面"""
278 | self.setWindowTitle(u'任务管理')
279 |
280 | buttonStopAll = QPushButton(u'全部停止')
281 | buttonStopAll.setObjectName(_fromUtf8('redButton'))
282 | buttonStopAll.clicked.connect(self.taskTab.stopAll)
283 | buttonClearAll = QPushButton(u'清空所有')
284 | buttonClearAll.setObjectName(_fromUtf8('blueButton'))
285 | buttonClearAll.clicked.connect(self.taskTab.clearAll)
286 | buttonShowAll = QPushButton(u'连续显示')
287 | buttonShowAll.setObjectName(_fromUtf8('greenButton'))
288 | buttonShowAll.clicked.connect(self.taskTab.showAll)
289 | hbox11 = QHBoxLayout()
290 | hbox11.addWidget(buttonStopAll)
291 | hbox11.addWidget(buttonClearAll)
292 | hbox11.addWidget(buttonShowAll)
293 | hbox11.addStretch()
294 |
295 | grid = QVBoxLayout()
296 | grid.addLayout(hbox11)
297 | grid.addWidget(self.taskTab)
298 |
299 | self.setLayout(grid)
300 |
301 | #----------------------------------------------------------------------
302 | def show(self):
303 | """重载显示"""
304 | self.showMaximized()
305 |
306 | #----------------------------------------------------------------------
307 | def init(self):
308 | """初始化"""
309 | self.taskTab.initCells()
310 |
311 | ########################################################################
312 | class TaskParamWidget(BasicDialog):
313 | """策略配置对话框"""
314 |
315 | index = 0
316 | name = ''
317 | paramDict = {}
318 | valueEdit = {}
319 |
320 | #----------------------------------------------------------------------
321 | def __init__(self, paramDict, parent=None):
322 | """Constructor"""
323 | super(TaskParamWidget, self).__init__(parent)
324 | self.valueEdit = {}
325 | self.paramDict = paramDict
326 | self.initUi()
327 |
328 | #----------------------------------------------------------------------
329 | def initUi(self):
330 | """"""
331 | QWidget.__init__(self) # 调用父类初始化方法
332 | self.setWindowTitle(u'设置参数')
333 | self.resize(300,400) # 设置窗口大小
334 | gridlayout = QGridLayout() # 创建布局组件
335 | i = 0
336 | lName = QLabel(u'参数')
337 | lValue = QLabel(u'数值')
338 | gridlayout.addWidget(lName, i, 0 )
339 | gridlayout.addWidget(lValue, i, 1 )
340 | for name in self.paramDict:
341 | i += 1
342 | label = QLabel(name) # 创建单选框
343 | self.valueEdit[name] = QLineEdit()
344 | self.valueEdit[name].setText(str(self.paramDict[name]))
345 | self.valueEdit[name].setFocusPolicy(QtCore.Qt.NoFocus)
346 | gridlayout.addWidget(label, i, 0 ) # 添加文本
347 | gridlayout.addWidget(self.valueEdit[name], i, 1) # 添加文本框
348 |
349 | vbox = QVBoxLayout()
350 | vbox.addLayout(gridlayout)
351 | self.addButton(vbox)
352 | self.setLayout(vbox)
353 |
--------------------------------------------------------------------------------
/vecsig/reverse.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | import talib
3 | import numpy as np
4 |
5 | def reverse(pdBars,rLimit=0.01):
6 | size = len(pdBars.index)
7 | pclose = pdBars['close'].values
8 | phigh = pdBars['high'].values
9 | plow = pdBars['low'].values
10 | popen = pdBars['open'].values
11 | pratio = (pclose-popen)/pclose
12 | hhv = popen + rLimit*pclose
13 | llv = popen - rLimit*pclose
14 | sigOpen = np.zeros(size)
15 | sigOpen[pratio>rLimit] = -1
16 | sigOpen[pratio<-rLimit] = 1
17 | return {"dealOpen":sigOpen,
18 | "deal":sigOpen,
19 | "pnl":np.zeros(size),
20 | "state":{'hhv':hhv,'llv':llv}}
21 |
--------------------------------------------------------------------------------
/vecsig/trends.py:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | import talib
3 | import numpy as np
4 |
5 | def trends(pdBars):
6 | size = len(pdBars.index)
7 | close = pdBars['close'].values
8 | ma10 = talib.SMA(close,5)
9 | ma20 = talib.SMA(close,26)
10 | sigOpen = np.zeros(size)
11 | sigOpen[(ma10ma20,1)] = -1
12 | sigOpen[(ma10>ma20)&np.roll(ma10