├── Form.py ├── Form.ui ├── RBHot_5m.csv ├── README.md ├── echart_data_visualization.py ├── model1.html ├── model1.jpg ├── model2.html └── model2.jpg /Form.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'Form.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.6 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Damon(object): 12 | def setupUi(self, Damon): 13 | Damon.setObjectName("Damon") 14 | Damon.resize(1553, 925) 15 | self.title = QtWidgets.QLabel(Damon) 16 | self.title.setGeometry(QtCore.QRect(660, 20, 251, 21)) 17 | self.title.setStyleSheet("font: 75 14pt \"宋体\";") 18 | self.title.setObjectName("title") 19 | self.comboBox = QtWidgets.QComboBox(Damon) 20 | self.comboBox.setGeometry(QtCore.QRect(120, 770, 61, 24)) 21 | self.comboBox.setObjectName("comboBox") 22 | self.DataPathL = QtWidgets.QLabel(Damon) 23 | self.DataPathL.setGeometry(QtCore.QRect(20, 720, 181, 18)) 24 | self.DataPathL.setStyleSheet("font: 75 11pt \"新宋体\";") 25 | self.DataPathL.setObjectName("DataPathL") 26 | self.BrowseB1 = QtWidgets.QPushButton(Damon) 27 | self.BrowseB1.setGeometry(QtCore.QRect(680, 720, 71, 34)) 28 | self.BrowseB1.setStyleSheet("font: 75 10pt \"新宋体\";\n" 29 | "") 30 | self.BrowseB1.setObjectName("BrowseB1") 31 | self.DataFramL = QtWidgets.QLabel(Damon) 32 | self.DataFramL.setGeometry(QtCore.QRect(20, 770, 111, 18)) 33 | self.DataFramL.setStyleSheet("font: 75 11pt \"新宋体\";") 34 | self.DataFramL.setObjectName("DataFramL") 35 | self.StartDateL = QtWidgets.QLabel(Damon) 36 | self.StartDateL.setGeometry(QtCore.QRect(270, 770, 131, 18)) 37 | self.StartDateL.setStyleSheet("font: 75 11pt \"新宋体\";") 38 | self.StartDateL.setObjectName("StartDateL") 39 | self.EndDateL = QtWidgets.QLabel(Damon) 40 | self.EndDateL.setGeometry(QtCore.QRect(590, 770, 131, 18)) 41 | self.EndDateL.setStyleSheet("font: 75 11pt \"新宋体\";") 42 | self.EndDateL.setObjectName("EndDateL") 43 | self.StartTimeL = QtWidgets.QLabel(Damon) 44 | self.StartTimeL.setGeometry(QtCore.QRect(270, 820, 131, 18)) 45 | self.StartTimeL.setStyleSheet("font: 75 11pt \"新宋体\";") 46 | self.StartTimeL.setObjectName("StartTimeL") 47 | self.EndTimeL = QtWidgets.QLabel(Damon) 48 | self.EndTimeL.setGeometry(QtCore.QRect(590, 820, 131, 18)) 49 | self.EndTimeL.setStyleSheet("font: 75 11pt \"新宋体\";") 50 | self.EndTimeL.setObjectName("EndTimeL") 51 | self.ChartTypeL = QtWidgets.QLabel(Damon) 52 | self.ChartTypeL.setGeometry(QtCore.QRect(20, 820, 131, 18)) 53 | self.ChartTypeL.setStyleSheet("font: 75 11pt \"新宋体\";") 54 | self.ChartTypeL.setObjectName("ChartTypeL") 55 | self.comboBox_3 = QtWidgets.QComboBox(Damon) 56 | self.comboBox_3.setGeometry(QtCore.QRect(120, 820, 91, 24)) 57 | self.comboBox_3.setObjectName("comboBox_3") 58 | self.dp_le = QtWidgets.QLineEdit(Damon) 59 | self.dp_le.setGeometry(QtCore.QRect(190, 720, 481, 31)) 60 | self.dp_le.setObjectName("dp_le") 61 | self.ed_le = QtWidgets.QLineEdit(Damon) 62 | self.ed_le.setGeometry(QtCore.QRect(680, 770, 161, 31)) 63 | self.ed_le.setObjectName("ed_le") 64 | self.st_le = QtWidgets.QLineEdit(Damon) 65 | self.st_le.setGeometry(QtCore.QRect(370, 820, 161, 31)) 66 | self.st_le.setObjectName("st_le") 67 | self.et_le = QtWidgets.QLineEdit(Damon) 68 | self.et_le.setGeometry(QtCore.QRect(680, 820, 161, 31)) 69 | self.et_le.setObjectName("et_le") 70 | self.ed_le_2 = QtWidgets.QLineEdit(Damon) 71 | self.ed_le_2.setGeometry(QtCore.QRect(370, 770, 161, 31)) 72 | self.ed_le_2.setObjectName("ed_le_2") 73 | self.pushButton = QtWidgets.QPushButton(Damon) 74 | self.pushButton.setGeometry(QtCore.QRect(1270, 720, 271, 131)) 75 | self.pushButton.setObjectName("pushButton") 76 | self.webView = QtWebKitWidgets.QWebView(Damon) 77 | self.webView.setGeometry(QtCore.QRect(20, 60, 1521, 631)) 78 | self.webView.setUrl(QtCore.QUrl("about:blank")) 79 | self.webView.setObjectName("webView") 80 | self.label = QtWidgets.QLabel(Damon) 81 | self.label.setGeometry(QtCore.QRect(1440, 890, 101, 31)) 82 | self.label.setObjectName("label") 83 | self.DataPathL_2 = QtWidgets.QLabel(Damon) 84 | self.DataPathL_2.setGeometry(QtCore.QRect(900, 720, 181, 18)) 85 | self.DataPathL_2.setStyleSheet("font: 75 11pt \"新宋体\";") 86 | self.DataPathL_2.setObjectName("DataPathL_2") 87 | self.ed_le_3 = QtWidgets.QLineEdit(Damon) 88 | self.ed_le_3.setGeometry(QtCore.QRect(1080, 720, 121, 31)) 89 | self.ed_le_3.setObjectName("ed_le_3") 90 | self.DataPathL_3 = QtWidgets.QLabel(Damon) 91 | self.DataPathL_3.setGeometry(QtCore.QRect(900, 770, 101, 20)) 92 | self.DataPathL_3.setStyleSheet("font: 75 11pt \"新宋体\";") 93 | self.DataPathL_3.setObjectName("DataPathL_3") 94 | self.ed_le_4 = QtWidgets.QLineEdit(Damon) 95 | self.ed_le_4.setGeometry(QtCore.QRect(940, 770, 91, 31)) 96 | self.ed_le_4.setObjectName("ed_le_4") 97 | self.DataPathL_4 = QtWidgets.QLabel(Damon) 98 | self.DataPathL_4.setGeometry(QtCore.QRect(1050, 770, 121, 18)) 99 | self.DataPathL_4.setStyleSheet("font: 75 11pt \"新宋体\";") 100 | self.DataPathL_4.setObjectName("DataPathL_4") 101 | self.ed_le_5 = QtWidgets.QLineEdit(Damon) 102 | self.ed_le_5.setGeometry(QtCore.QRect(1110, 770, 91, 31)) 103 | self.ed_le_5.setObjectName("ed_le_5") 104 | self.DataPathL_5 = QtWidgets.QLabel(Damon) 105 | self.DataPathL_5.setGeometry(QtCore.QRect(900, 820, 131, 20)) 106 | self.DataPathL_5.setStyleSheet("font: 75 11pt \"新宋体\";") 107 | self.DataPathL_5.setObjectName("DataPathL_5") 108 | self.ed_le_6 = QtWidgets.QLineEdit(Damon) 109 | self.ed_le_6.setGeometry(QtCore.QRect(1030, 820, 171, 31)) 110 | self.ed_le_6.setObjectName("ed_le_6") 111 | 112 | self.retranslateUi(Damon) 113 | QtCore.QMetaObject.connectSlotsByName(Damon) 114 | 115 | def retranslateUi(self, Damon): 116 | _translate = QtCore.QCoreApplication.translate 117 | Damon.setWindowTitle(_translate("Damon", "Form")) 118 | self.title.setText(_translate("Damon", "Output HTML for Analysis")) 119 | self.DataPathL.setText(_translate("Damon", "*Data Path(CSV/MAT):")) 120 | self.BrowseB1.setText(_translate("Damon", "Browse")) 121 | self.DataFramL.setText(_translate("Damon", "*Data Frame:")) 122 | self.StartDateL.setText(_translate("Damon", "*Start Date:")) 123 | self.EndDateL.setText(_translate("Damon", "*End Date:")) 124 | self.StartTimeL.setText(_translate("Damon", "*Start Time:")) 125 | self.EndTimeL.setText(_translate("Damon", "*End Time:")) 126 | self.ChartTypeL.setText(_translate("Damon", "*Chart Type:")) 127 | self.pushButton.setText(_translate("Damon", "Output")) 128 | self.label.setText(_translate("Damon", "Created by Damon")) 129 | self.DataPathL_2.setText(_translate("Damon", "*Symbol Name(MongoDB):")) 130 | self.DataPathL_3.setText(_translate("Damon", "*IP:")) 131 | self.DataPathL_4.setText(_translate("Damon", "*PORT:")) 132 | self.DataPathL_5.setText(_translate("Damon", "*Database Name:")) 133 | 134 | from PyQt5 import QtWebKitWidgets 135 | -------------------------------------------------------------------------------- /Form.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Damon 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1553 10 | 925 11 | 12 | 13 | 14 | Form 15 | 16 | 17 | 18 | 19 | 660 20 | 20 21 | 251 22 | 21 23 | 24 | 25 | 26 | font: 75 14pt "宋体"; 27 | 28 | 29 | Output HTML for Analysis 30 | 31 | 32 | 33 | 34 | 35 | 120 36 | 770 37 | 61 38 | 24 39 | 40 | 41 | 42 | 43 | 44 | 45 | 20 46 | 720 47 | 181 48 | 18 49 | 50 | 51 | 52 | font: 75 11pt "新宋体"; 53 | 54 | 55 | *Data Path(CSV/MAT): 56 | 57 | 58 | 59 | 60 | 61 | 680 62 | 720 63 | 71 64 | 34 65 | 66 | 67 | 68 | font: 75 10pt "新宋体"; 69 | 70 | 71 | 72 | Browse 73 | 74 | 75 | 76 | 77 | 78 | 20 79 | 770 80 | 111 81 | 18 82 | 83 | 84 | 85 | font: 75 11pt "新宋体"; 86 | 87 | 88 | *Data Frame: 89 | 90 | 91 | 92 | 93 | 94 | 270 95 | 770 96 | 131 97 | 18 98 | 99 | 100 | 101 | font: 75 11pt "新宋体"; 102 | 103 | 104 | *Start Date: 105 | 106 | 107 | 108 | 109 | 110 | 590 111 | 770 112 | 131 113 | 18 114 | 115 | 116 | 117 | font: 75 11pt "新宋体"; 118 | 119 | 120 | *End Date: 121 | 122 | 123 | 124 | 125 | 126 | 270 127 | 820 128 | 131 129 | 18 130 | 131 | 132 | 133 | font: 75 11pt "新宋体"; 134 | 135 | 136 | *Start Time: 137 | 138 | 139 | 140 | 141 | 142 | 590 143 | 820 144 | 131 145 | 18 146 | 147 | 148 | 149 | font: 75 11pt "新宋体"; 150 | 151 | 152 | *End Time: 153 | 154 | 155 | 156 | 157 | 158 | 20 159 | 820 160 | 131 161 | 18 162 | 163 | 164 | 165 | font: 75 11pt "新宋体"; 166 | 167 | 168 | *Chart Type: 169 | 170 | 171 | 172 | 173 | 174 | 120 175 | 820 176 | 91 177 | 24 178 | 179 | 180 | 181 | 182 | 183 | 184 | 190 185 | 720 186 | 481 187 | 31 188 | 189 | 190 | 191 | 192 | 193 | 194 | 680 195 | 770 196 | 161 197 | 31 198 | 199 | 200 | 201 | 202 | 203 | 204 | 370 205 | 820 206 | 161 207 | 31 208 | 209 | 210 | 211 | 212 | 213 | 214 | 680 215 | 820 216 | 161 217 | 31 218 | 219 | 220 | 221 | 222 | 223 | 224 | 370 225 | 770 226 | 161 227 | 31 228 | 229 | 230 | 231 | 232 | 233 | 234 | 1270 235 | 720 236 | 271 237 | 131 238 | 239 | 240 | 241 | Output 242 | 243 | 244 | 245 | 246 | 247 | 20 248 | 60 249 | 1521 250 | 631 251 | 252 | 253 | 254 | 255 | about:blank 256 | 257 | 258 | 259 | 260 | 261 | 262 | 1440 263 | 890 264 | 101 265 | 31 266 | 267 | 268 | 269 | Created by Damon 270 | 271 | 272 | 273 | 274 | 275 | 900 276 | 720 277 | 181 278 | 18 279 | 280 | 281 | 282 | font: 75 11pt "新宋体"; 283 | 284 | 285 | *Symbol Name(MongoDB): 286 | 287 | 288 | 289 | 290 | 291 | 1080 292 | 720 293 | 121 294 | 31 295 | 296 | 297 | 298 | 299 | 300 | 301 | 900 302 | 770 303 | 101 304 | 20 305 | 306 | 307 | 308 | font: 75 11pt "新宋体"; 309 | 310 | 311 | *IP: 312 | 313 | 314 | 315 | 316 | 317 | 940 318 | 770 319 | 91 320 | 31 321 | 322 | 323 | 324 | 325 | 326 | 327 | 1050 328 | 770 329 | 121 330 | 18 331 | 332 | 333 | 334 | font: 75 11pt "新宋体"; 335 | 336 | 337 | *PORT: 338 | 339 | 340 | 341 | 342 | 343 | 1110 344 | 770 345 | 91 346 | 31 347 | 348 | 349 | 350 | 351 | 352 | 353 | 900 354 | 820 355 | 131 356 | 20 357 | 358 | 359 | 360 | font: 75 11pt "新宋体"; 361 | 362 | 363 | *Database Name: 364 | 365 | 366 | 367 | 368 | 369 | 1030 370 | 820 371 | 171 372 | 31 373 | 374 | 375 | 376 | 377 | 378 | 379 | QWebView 380 | QWidget 381 |
QtWebKitWidgets/QWebView
382 |
383 |
384 | 385 | 386 |
387 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于E-Chart的期货数据可视化(K线图) 2 | 3 | 4 | ------------------------------- 5 | 6 | ## * 简介 7 | 8 | 基本步骤可参考本人的微文: 9 | https://mp.weixin.qq.com/s?__biz=MzIyMjE5Njk1Mw==&mid=503763782&idx=1&sn=e88cde5499ee7b9041e5cf0d534c8811&scene=19#wechat_redirect 10 | 11 | 开发环境`Python-v3(3.6)`: 12 | 13 | - pandas==0.20.0 14 | - numpy==1.13.3+mkl 15 | - pymongo==3.6.0 16 | - beautifulsoup4==4.6.0 17 | - PyQt5 18 | 19 | ## * 可视化界面(`echart_data_visualization.py`) 20 | 21 | - conn_mongodb函数,连接数据库,返回一个collection 22 | - extractData函数,返回数据库中特定标签数据,以pandas.DataFrame格式返回 23 | - InputDataFromMongoDB函数,从数据库中查询并抽取所需数据,以numpy.array的格式返回 24 | - InputDatabyPd函数,导入外部数据,如果需要导入自定义格式数据,则需要重载这个函数,或者直接在此py上改写即可 25 | - CheckAnyTimeRangeData函数,将数据转成js列表并保存到txt文件中,存储目录在根目录的data_file文件中 26 | - CreateHTMLForAnalysis函数,用beautifulsoup4来解析EChart的html模板,并把生成的js列表数据插入模板中,生成新的html文件,存储在data_file文件中 27 | - comBoxAct、leAct、openDataFile、closeEvent、msg均是一些PyQt的监听与执行函数 28 | - load函数,即在最终的界面上以网页的形式呈现出K线图 29 | 30 | ## * 用法 31 | 32 | - 按照https://github.com/DemonDamon/tongdaxin-futures-data-clearing-database-operation 处理好数据并存入数据库;或者重载InputDatabyPd函数,导入自定义的CSV或MAT文件 33 | - 启动MongoDB,运行该py脚本后,输入必要的参数,如下图所示: 34 | ![image](https://github.com/DemonDamon/futures-data-visualization-via-EChart/blob/master/model1.jpg) 35 | ![image](https://github.com/DemonDamon/futures-data-visualization-via-EChart/blob/master/model2.jpg) 36 | -------------------------------------------------------------------------------- /echart_data_visualization.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | from sys import path 3 | from PyQt5 import QtWidgets, QtGui 4 | from PyQt5.QtWidgets import QMessageBox, QFileDialog 5 | from PyQt5.QtCore import QUrl 6 | from PyQt5.QtWebKitWidgets import QWebView 7 | from Form import Ui_Damon 8 | from pymongo import MongoClient 9 | from bson.objectid import ObjectId 10 | import pandas as pd 11 | import numpy as np 12 | 13 | class mywindow(QtWidgets.QWidget,Ui_Damon): 14 | def __init__(self): 15 | if not os.path.exists(os.getcwd() + '\\' + 'data_file'): 16 | os.makedirs(os.getcwd() + '\\' + 'data_file') 17 | self.savepath = os.getcwd() + '\\' + 'data_file' 18 | super(mywindow,self).__init__() 19 | self.setupUi(self) 20 | self.BrowseB1.clicked.connect(self.openDataFile) 21 | self.comboBox.addItem('1M') #1mins 22 | self.comboBox.addItem('5M') #5mins 23 | self.comboBox.addItem('D') #Day 24 | self.comboBox_3.addItem('model1') 25 | self.comboBox_3.addItem('model2') 26 | self.comboBox.currentIndexChanged.connect(self.comBoxAct) 27 | self.comboBox_3.currentIndexChanged.connect(self.comBoxAct) 28 | self.pushButton.clicked.connect(self.CreateHTMLFile) 29 | self.dp_le.textChanged.connect(self.leAct) #csv/mat数据路径 30 | self.ed_le_2.textChanged.connect(self.leAct) #起始日期 31 | self.ed_le.textChanged.connect(self.leAct) #结束日期 32 | self.st_le.textChanged.connect(self.leAct) #起始时间 33 | self.et_le.textChanged.connect(self.leAct) #结束时间 34 | self.ed_le_3.textChanged.connect(self.leAct) #mongodb期货品种名称 35 | self.ed_le_4.textChanged.connect(self.leAct) #IP 36 | self.ed_le_5.textChanged.connect(self.leAct) #PORT 37 | self.ed_le_6.textChanged.connect(self.leAct) #database name 38 | self.datapath = '' 39 | self.startDate = 0 40 | self.endDate = 0 41 | self.startTime = 0 42 | self.endTime = 0 43 | self.modelType = self.comboBox_3.currentText() 44 | self.dataFrame = self.comboBox.currentText() 45 | self.savename = '' 46 | self.symbolName = '' 47 | self.setWindowTitle('Demon Chart') 48 | 49 | def conn_mongodb(self,collectionName): 50 | self._Conn = MongoClient(self._IP, self._PORT) #localhost 27017 51 | self._mydb = self._Conn[self._databasename] 52 | collection = self._mydb.get_collection(collectionName) 53 | return collection 54 | 55 | def extractData(self,collection,tag_list): 56 | data = [] 57 | Dict = {} 58 | for tag in tag_list: 59 | exec(tag + " = collection.distinct('" + tag + "')") 60 | exec("data.append(" + tag + ")") 61 | exec("Dict.update({'" + tag + "' : np.array(" + tag + ")})") 62 | dataFrame = pd.DataFrame(Dict,columns=tag_list) 63 | return dataFrame 64 | 65 | def InputDataFromMongoDB(self): 66 | try: 67 | collection = self.conn_mongodb(self.symbolName) 68 | except Exception as e: 69 | if self._IP == '': 70 | self.msg("conn_mongodb Error","请输入IP地址!") 71 | if self._PORT == '': 72 | self.msg("conn_mongodb Error","请输入端口号!") 73 | if self._databasename == '': 74 | self.msg("conn_mongodb Error","请输入数据库名称!") 75 | if self._IP != '' and self._PORT != '' and self._databasename != '': 76 | self.msg("conn_mongodb Error","IP、端口号或数据库名称输入有误,请重新输入!") 77 | cursor_start = [int(cursor['_id']) for cursor in collection.find({"Date":str(self.startDate),"Time":str(self.startTime)})][0] 78 | cursor_end = [int(cursor['_id']) for cursor in collection.find({"Date":str(self.endDate),"Time":str(self.endTime)})][0] 79 | idLst = self.extractData(collection,['_id'])._id 80 | Date = []; Time = []; Open = []; High = []; Low = []; Close = []; Volumn = [] 81 | while cursor_start <= cursor_end: 82 | Date.append(int(collection.find_one({'_id':str(cursor_start)})['Date'])) 83 | Time.append(int(collection.find_one({'_id':str(cursor_start)})['Time'])) 84 | Open.append(collection.find_one({'_id':str(cursor_start)})['Open']) 85 | High.append(collection.find_one({'_id':str(cursor_start)})['High']) 86 | Low.append(collection.find_one({'_id':str(cursor_start)})['Low']) 87 | Close.append(collection.find_one({'_id':str(cursor_start)})['Close']) 88 | Volumn.append(collection.find_one({'_id':str(cursor_start)})['Volumn']) 89 | cursor_start += 1 90 | date = np.array(Date) 91 | time = np.array(Time) 92 | Open = np.double(Open) 93 | High = np.double(High) 94 | Low = np.double(Low) 95 | Close = np.double(Close) 96 | Volumn = np.double(Volumn) 97 | date_day = []; Open_day = []; High_day = []; 98 | Low_day = []; Close_day = []; Volumn_day = [] 99 | idx = 0 100 | for i in range(1,len(date)): 101 | if time[i-1] == 1500: 102 | date_day.append(date[i-1]) 103 | Open_day.append(Open[idx]) 104 | High_day.append(np.max(High[idx:i])) 105 | Low_day.append(np.min(Low[idx:i])) 106 | Close_day.append(Close[i-1]) 107 | Volumn_day.append(np.sum(Volumn[idx:i])) 108 | idx = i 109 | if i == len(time) - 1: 110 | date_day.append(date[-1]) 111 | Open_day.append(Open[idx]) 112 | High_day.append(np.max(High[idx:])) 113 | Low_day.append(np.min(Low[idx:])) 114 | Close_day.append(Close[i]) 115 | Volumn_day.append(np.sum(Volumn[idx:])) 116 | date_day = np.array(date_day) 117 | Open_day = np.array(Open_day) 118 | High_day = np.array(High_day) 119 | Low_day = np.array(Low_day) 120 | Close_day = np.array(Close_day) 121 | Volumn_day = np.array(Volumn_day) 122 | return date,time,Open,High,Low,Close,Volumn,date_day,Open_day,\ 123 | High_day,Low_day,Close_day,Volumn_day 124 | 125 | def InputDatabyPd(self): 126 | import pandas as pd 127 | import numpy as np 128 | data = pd.read_csv(self.datapath) 129 | data_processed = data.loc[:,['time','open','high','low','close','volume']] 130 | start_idx = 0 131 | date = []; open_day = []; high_day = [] 132 | low_day = []; close_day = []; vol_day = [] 133 | for i in range(len(data_processed)): 134 | if str(data_processed.time[i])[8:12] == '1500': 135 | date.append(int(str(data_processed.time[i])[0:8])) 136 | open_day.append(data_processed.open[start_idx]) 137 | high_day.append(np.max(data_processed.high[start_idx:i+1])) 138 | low_day.append(np.min(data_processed.low[start_idx:i+1])) 139 | close_day.append(data_processed.close[i]) 140 | vol_day.append(np.sum(data_processed.volume[start_idx:i+1])) 141 | start_idx = i + 1 142 | data_day = pd.DataFrame({'date' : np.array(date), 143 | 'open' : np.array(open_day), 144 | 'high' : np.array(high_day), 145 | 'low' : np.array(low_day), 146 | 'close' : np.array(close_day), 147 | 'volume' : np.array(vol_day)}, 148 | columns = ['date','open','high','low','close','volume']) 149 | date_day = np.array(data_day.date) 150 | Open_day = np.double(data_day.open) 151 | High_day = np.double(data_day.high) 152 | Low_day = np.double(data_day.low) 153 | Close_day = np.double(data_day.close) 154 | Volumn_day = np.array(data_day.volume) 155 | date = pd.DataFrame([int(str(data_processed.time[i])[0:8]) for i in range(len(data_processed))]) 156 | time = pd.DataFrame([int(str(data_processed.time[i])[8:12]) for i in range(len(data_processed))]) 157 | del data_processed['time'] 158 | data_processed.insert(0,'date',date) 159 | data_processed.insert(1,'time',time) 160 | date = np.array(data_processed.date) 161 | time = np.array(data_processed.time) 162 | Open = np.double(data_processed.open) 163 | High = np.double(data_processed.high) 164 | Low = np.double(data_processed.low) 165 | Close = np.double(data_processed.close) 166 | Volume = np.array(data_processed.volume) 167 | return date,time,Open,High,Low,Close,Volume,\ 168 | date_day,Open_day,High_day,Low_day,Close_day,Volumn_day 169 | 170 | def CheckAnyTimeRangeData(self): 171 | import numpy as np 172 | if self.symbolName != '': 173 | try: 174 | date,time,Open,High,Low,Close,Volumn,\ 175 | date_day,Open_day,High_day,Low_day,Close_day,Volumn_day = self.InputDataFromMongoDB() 176 | except Exception as e: 177 | self.msg("CheckAnyTimeRangeData Error","数据库中不存在该期货品种价量数据!") 178 | elif self.datapath != '' and self.symbolName == '': 179 | try: 180 | date,time,Open,High,Low,Close,Volumn,\ 181 | date_day,Open_day,High_day,Low_day,Close_day,Volumn_day = self.InputDatabyPd() 182 | except Exception as e: 183 | self.msg("CheckAnyTimeRangeData Error","数据路径不存在或数据文件内部格式不对!") 184 | else: 185 | self.msg("CheckAnyTimeRangeData","请输入数据源信息!") 186 | if self.dataFrame == '1M' or self.dataFrame == '5M': 187 | if self.startTime == 0 and self.endTime == 0: 188 | startIdx = np.where(date == self.startDate)[0][0] 189 | endIdx = np.where(date == self.endDate)[0][-1] 190 | else: 191 | startIdx = np.where(date == self.startDate)[0] 192 | startIdx = startIdx[np.where(time[startIdx] == self.startTime)[0][0]] 193 | endIdx = np.where(date == self.endDate)[0] 194 | endIdx = endIdx[np.where(time[endIdx] == self.endTime)[0][0]] 195 | dataK = [] 196 | dateS = [] 197 | DataforJS = [] 198 | if self.modelType == 'model1': 199 | for i in range(startIdx,endIdx+1): 200 | dateS.append(str(date[i])[0:4] + '/' + str(date[i])[4:6] + '/' + str(date[i])[6:]\ 201 | + ' ' + str(time[i])[:-2] + ':' + str(time[i])[-2:]) 202 | dataK.append([Open[i],Close[i],Low[i],High[i]]) 203 | DataforJS.append([dateS[-1],Open[i],Close[i],Low[i],High[i]]) 204 | Path = self.savepath + '\\' + self.savename + '(' + self.dataFrame + ').txt' 205 | with open(Path,'w') as f: 206 | for i in range(len(DataforJS)): 207 | f.write("['" + DataforJS[i][0] + "'," + \ 208 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][2]) + ',' + \ 209 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + '],' + '\n') 210 | if i == len(DataforJS) - 1: 211 | f.write("['" + DataforJS[i][0] + "'," + \ 212 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][3]) + ',' + \ 213 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ']' + '\n') 214 | elif self.modelType == 'model2': 215 | for i in range(startIdx,endIdx+1): 216 | dateS.append(str(date[i])[0:4] + '/' + str(date[i])[4:6] + '/' + str(date[i])[6:]\ 217 | + ' ' + str(time[i])[:-2] + ':' + str(time[i])[-2:]) 218 | dataK.append([Open[i],Close[i],Low[i],High[i],Volumn[i]]) 219 | DataforJS.append([dateS[-1],Open[i],Close[i],Low[i],High[i],Volumn[i]]) 220 | Path = self.savepath + '\\' + self.savename + '(' + self.dataFrame + ').txt' 221 | with open(Path,'w') as f: 222 | for i in range(len(DataforJS)): 223 | f.write("['" + DataforJS[i][0] + "'," + \ 224 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][2]) + ',' + \ 225 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ',' + str(DataforJS[i][5]) + '],' + '\n') 226 | if i == len(DataforJS) - 1: 227 | f.write("['" + DataforJS[i][0] + "'," + \ 228 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][3]) + ',' + \ 229 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ',' + str(DataforJS[i][5]) + ']' + '\n') 230 | elif self.dataFrame == 'D': 231 | startIdx = np.where(date_day == self.startDate)[0][0] 232 | endIdx = np.where(date_day == self.endDate)[0][-1] 233 | dataK = [] 234 | dateS = [] 235 | DataforJS = [] 236 | if self.modelType == 'model1': 237 | for i in range(startIdx,endIdx+1): 238 | dateS.append(str(date_day[i])[0:4] + '/' + str(date_day[i])[4:6] + '/' + str(date_day[i])[6:]) 239 | dataK.append([Open_day[i],Close_day[i],Low_day[i],High_day[i]]) 240 | DataforJS.append([dateS[-1],Open_day[i],Close_day[i],Low_day[i],High_day[i]]) 241 | Path = self.savepath + '\\' + self.savename + '(' + self.dataFrame + ').txt' 242 | with open(Path,'w') as f: 243 | for i in range(len(DataforJS)): 244 | f.write("['" + DataforJS[i][0] + "'," + \ 245 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][2]) + ',' + \ 246 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + '],' + '\n') 247 | if i == len(DataforJS) - 1: 248 | f.write("['" + DataforJS[i][0] + "'," + \ 249 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][3]) + ',' + \ 250 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ']' + '\n') 251 | elif self.modelType == 'model2': 252 | for i in range(startIdx,endIdx+1): 253 | dateS.append(str(date_day[i])[0:4] + '/' + str(date_day[i])[4:6] + '/' + str(date_day[i])[6:]) 254 | dataK.append([Open_day[i],Close_day[i],Low_day[i],High_day[i],Volumn_day[i]]) 255 | DataforJS.append([dateS[-1],Open_day[i],Close_day[i],Low_day[i],High_day[i],Volumn_day[i]]) 256 | Path = self.savepath + '\\' + self.savename + '(' + self.dataFrame + ').txt' 257 | with open(Path,'w') as f: 258 | for i in range(len(DataforJS)): 259 | f.write("['" + DataforJS[i][0] + "'," + \ 260 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][2]) + ',' + \ 261 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ',' + str(DataforJS[i][5]) + '],' + '\n') 262 | if i == len(DataforJS) - 1: 263 | f.write("['" + DataforJS[i][0] + "'," + \ 264 | str(DataforJS[i][1]) + ',' + str(DataforJS[i][3]) + ',' + \ 265 | str(DataforJS[i][3]) + ',' + str(DataforJS[i][4]) + ',' + str(DataforJS[i][5]) + ']' + '\n') 266 | 267 | def CreateHTMLForAnalysis(self): 268 | from bs4 import BeautifulSoup 269 | self.CheckAnyTimeRangeData() 270 | try: 271 | with open(self.modelHTMLPath,'r',encoding='utf-8') as f: 272 | Data = BeautifulSoup(f.read(),"lxml") 273 | jsStr = Data.body.script.string 274 | Data.body.div['style'] = 'width:1600px;height:600px;' #对style属性和内容进行修改 275 | Str = "var data0 = splitData([" 276 | Str2 = "var title_text = " 277 | importdata = [] 278 | for row in open(self.dataPath): 279 | importdata.append(row) 280 | if jsStr.find(Str) != 0: 281 | njsStr = jsStr[jsStr.find(Str):len(Str)+1] + '\n' 282 | for i in range(len(importdata)): 283 | njsStr = njsStr + importdata[i] 284 | njsStr = njsStr + jsStr[len(Str)+1:] 285 | if njsStr.find(Str2) != 0: 286 | if self.dataPath.split('\\')[-1][:-4] == 'MxkOfSglTrdRge': 287 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'单笔最大亏损区间'" \ 288 | + njsStr[njsStr.find(Str2)+len(Str2):] 289 | elif self.dataPath.split('\\')[-1][:-4] == 'MxkRge': 290 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'回测期最大回撤区间'" \ 291 | + njsStr[njsStr.find(Str2)+len(Str2):] 292 | elif self.dataPath.split('\\')[-1][:-4] == 'MxAmntOfCtusWdlRge': 293 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'最大连续回撤次数区间'" \ 294 | + njsStr[njsStr.find(Str2)+len(Str2):] 295 | else: 296 | if self.dataFrame == '1M': 297 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'1分钟K线图'" \ 298 | + njsStr[njsStr.find(Str2)+len(Str2):] 299 | elif self.dataFrame == '5M': 300 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'5分钟K线图'" \ 301 | + njsStr[njsStr.find(Str2)+len(Str2):] 302 | elif self.dataFrame == 'D': 303 | njsStr1 = njsStr[:njsStr.find(Str2)+len(Str2)] + "'日线K线图'" \ 304 | + njsStr[njsStr.find(Str2)+len(Str2):] 305 | Data.body.script.string = njsStr1 306 | html = open(self.newHTMLPath, "w", encoding="utf-8"); 307 | html.write(str(Data)); 308 | html.close(); 309 | except Exception as e: 310 | self.msg("CreateHTMLForAnalysis Error","创建HTML分析出错,请检查!") 311 | 312 | def comBoxAct(self): 313 | self.dataFrame = self.comboBox.currentText() 314 | if self.comboBox.currentText() == 'D': 315 | self.st_le.setEnabled(False) 316 | self.et_le.setEnabled(False) 317 | self.startTime = 0 318 | self.endTime = 0 319 | else: 320 | self.st_le.setEnabled(True) 321 | self.et_le.setEnabled(True) 322 | self.modelType = self.comboBox_3.currentText() 323 | 324 | def leAct(self): 325 | if len(self.dp_le.text()) != 0: 326 | self.datapath = self.dp_le.text() 327 | if self.ed_le_2.text() != '': 328 | self.startDate = int(self.ed_le_2.text()) 329 | if self.ed_le.text() != '': 330 | self.endDate = int(self.ed_le.text()) 331 | if self.st_le.text() != '': 332 | self.startTime = int(self.st_le.text()) 333 | if self.et_le.text() != '': 334 | self.endTime = int(self.et_le.text()) 335 | if self.ed_le_3.text() != '': 336 | self.symbolName = self.ed_le_3.text().upper() 337 | if self.ed_le_4.text() != '': 338 | self._IP = self.ed_le_4.text() 339 | if self.ed_le_5.text() != '': 340 | self._PORT = int(self.ed_le_5.text()) 341 | if self.ed_le_6.text() != '': 342 | self._databasename = self.ed_le_6.text() 343 | 344 | def openDataFile(self): 345 | filename, _ = QFileDialog.getOpenFileName(self, 'Open File', os.getenv('HOME')) 346 | if filename: 347 | self.dp_le.setText(filename) 348 | 349 | def closeEvent(self, event): 350 | reply = QMessageBox.question(self, "Message", "Are you sure to quit?", QMessageBox.Yes | 351 | QMessageBox.No, QMessageBox.No) 352 | if reply == QMessageBox.Yes: 353 | event.accept() 354 | else: 355 | event.ignore() 356 | 357 | def msg(self,string1,string2): 358 | reply = QMessageBox.critical(self,string1,string2) 359 | 360 | def load(self): 361 | try: 362 | self.webView.load(QUrl.fromUserInput(self.newHTMLPath)) 363 | self.webView.show() 364 | except Exception as e: 365 | self.msg("load Error","载入窗口部分有误,请检查!") 366 | 367 | def CreateHTMLFile(self): 368 | if self.dataFrame == '1M' or self.dataFrame == '5M': 369 | if self.startTime == 0 and self.endTime == 0: 370 | self.savename = str(self.startDate) + "-" + str(self.endDate) 371 | self.newHTMLPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').html' 372 | self.dataPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').txt' 373 | else: 374 | self.savename = str(self.startDate) + str(self.startTime) + "-" + str(self.endDate) + str(self.endTime) 375 | self.newHTMLPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').html' 376 | self.dataPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').txt' 377 | elif self.dataFrame =='D': 378 | self.savename = str(self.startDate) + "-" + str(self.endDate) 379 | self.newHTMLPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').html' 380 | self.dataPath = self.savepath + "\\" + self.savename + '(' + self.dataFrame + ').txt' 381 | self.modelHTMLPath = os.getcwd() + "\\" + self.modelType + ".html" 382 | try: 383 | self.CreateHTMLForAnalysis() 384 | self.load() 385 | except Exception as e: 386 | self.msg("CreateHTMLFile Error","该日期时间对应的数据不存在,请重新输入!") 387 | 388 | 389 | if __name__ == '__main__': 390 | app = QtWidgets.QApplication(sys.argv) 391 | window = mywindow() 392 | window.show() 393 | sys.exit(app.exec_()) 394 | -------------------------------------------------------------------------------- /model1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonDamon/futures-data-visualization-via-EChart/864f42477e33201f52f8e35a0ab6d39f68a5df52/model1.jpg -------------------------------------------------------------------------------- /model2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DemonDamon/futures-data-visualization-via-EChart/864f42477e33201f52f8e35a0ab6d39f68a5df52/model2.jpg --------------------------------------------------------------------------------