• PyQt5_股票K线图相似度工具


    如果对某一股票K线图了解比较深刻,可以通过相似度找到与该股票相似的K线图,那么对这些相似的K线图,可以借鉴已经深刻剖析过的股票的方法,已达到事半功倍的效果。

    K线图相似度的计算方法使用的是该博文中的方法。

    目录

    效果

    计算K线图相似度策略代码

    工具代码

    工具使用

    数据


    效果

    计算K线图相似度策略代码

    前置说明:

    1. 必须以“excute_strategy”为方法名

    2. 三个参数,参数1为股票代码股票名键值对;参数2为股票日数据对应目录;参数3为指数(也可以是个股)日数据文件路径

    3. 策略代码写完后,保存为.py文件,将该py文件保存在一个目录(自定义)下,运行工具后,选择这个文件夹,程序会加载该目录下的所有py文件

    1. def excute_strategy(base_data,data_dir,index_data_dir):
    2. '''
    3. 股票K线图相似度
    4. 计算 2021-06-01 到 2022-01-01 之间的日K线
    5. :param base_data: 股票编码与股票名键值对
    6. :param data_dir: 股票日数据文件夹目录
    7. :param index_data_dir: 指数文件路径
    8. :return:
    9. '''
    10. import pandas as pd
    11. import numpy as np
    12. import os
    13. start_date_str = '2021-06-01'
    14. end_date_str = '2022-01-01'
    15. index_df = pd.read_csv(index_data_dir,encoding='utf-8')
    16. index_df['o_date'] = index_df['tradeDate']
    17. index_df['o_date'] = pd.to_datetime(index_df['o_date'])
    18. index_df = index_df.loc[(index_df['o_date']>=start_date_str) & (index_df['o_date']<=end_date_str)].copy()
    19. index_df = index_df.loc[:,['o_date','openIndex','closeIndex','highestIndex','lowestIndex']].copy()
    20. dailydata_file_list = os.listdir(data_dir)
    21. res_list = []
    22. for item in dailydata_file_list:
    23. item_arr = item.split('.')
    24. ticker = item_arr[0]
    25. secName = base_data[ticker]
    26. file_path = data_dir + item
    27. df = pd.read_csv(file_path,encoding='utf-8')
    28. # 删除停牌的数据
    29. df = df.loc[df['openPrice'] > 0].copy()
    30. df['o_date'] = df['tradeDate']
    31. df['o_date'] = pd.to_datetime(df['o_date'])
    32. df = df.loc[(df['o_date'] >= start_date_str) & (df['o_date']<=end_date_str)].copy()
    33. # 保存未复权收盘价数据
    34. df['close'] = df['closePrice']
    35. # 计算前复权数据
    36. df['openPrice'] = df['openPrice'] * df['accumAdjFactor']
    37. df['closePrice'] = df['closePrice'] * df['accumAdjFactor']
    38. df['highestPrice'] = df['highestPrice'] * df['accumAdjFactor']
    39. df['lowestPrice'] = df['lowestPrice'] * df['accumAdjFactor']
    40. if len(df)<=0:
    41. continue
    42. df_merge = pd.merge(index_df,df,on='o_date',how='inner')
    43. corropen = np.corrcoef(df_merge['openIndex'],df_merge['openPrice'])[0][1]
    44. corrclose = np.corrcoef(df_merge['closeIndex'],df_merge['closePrice'])[0][1]
    45. corrhigh = np.corrcoef(df_merge['highestIndex'],df_merge['highestPrice'])[0][1]
    46. corrlow = np.corrcoef(df_merge['lowestIndex'],df_merge['lowestPrice'])[0][1]
    47. similar_val = (corropen + corrclose + corrhigh + corrlow)/4
    48. # similar_val = corrlow
    49. res_list.append({
    50. 'ticker':ticker,
    51. 'secName':secName,
    52. 'similar_val':similar_val
    53. })
    54. pass
    55. df00 = pd.DataFrame(res_list)
    56. df_similar = df00['similar_val'].value_counts(bins=10,sort=False)
    57. similar_index_list00 = df_similar.index.tolist()
    58. similar_bar_y = df_similar.values.tolist()
    59. similar_bar_xTick = []
    60. similar_bar_x = []
    61. for i,item in enumerate(similar_index_list00):
    62. similar_bar_x.append(i)
    63. similar_bar_xTick.append((i,str(item)))
    64. pass
    65. similar_bar_data = {
    66. 'title_str':'相似度分布',
    67. 'x':similar_bar_x,
    68. 'y':similar_bar_y,
    69. 'xTick':similar_bar_xTick
    70. }
    71. # 指数文件名
    72. index_file_name = os.path.basename(index_data_dir)
    73. results_data = {
    74. 'df':df00,
    75. 'start_date_str':start_date_str,
    76. 'end_date_str':end_date_str,
    77. 'index_file_name':index_file_name,
    78. 'bar_data':similar_bar_data
    79. }
    80. return results_data

    工具代码

    导入需要的包

    1. import sys,json,os,math,time,talib
    2. from threading import Thread
    3. import numpy as np
    4. import pandas as pd
    5. from datetime import datetime
    6. from dateutil.relativedelta import relativedelta
    7. from typing import Dict,Any,List
    8. from PyQt5 import QtCore,QtGui,QtWidgets
    9. from PyQt5.QtCore import Qt
    10. import pyqtgraph as pg
    11. import pyqtgraph.examples
    12. pg.setConfigOption('background','k')
    13. pg.setConfigOption('foreground','w')

    pyqtgraph横坐标日期倾斜显示控件

    1. class RotateAxisItem(pg.AxisItem):
    2. def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):
    3. p.setRenderHint(p.Antialiasing,False)
    4. p.setRenderHint(p.TextAntialiasing,True)
    5. ## draw long line along axis
    6. pen,p1,p2 = axisSpec
    7. p.setPen(pen)
    8. p.drawLine(p1,p2)
    9. p.translate(0.5,0) ## resolves some damn pixel ambiguity
    10. ## draw ticks
    11. for pen,p1,p2 in tickSpecs:
    12. p.setPen(pen)
    13. p.drawLine(p1,p2)
    14. ## draw all text
    15. # if self.tickFont is not None:
    16. # p.setFont(self.tickFont)
    17. p.setPen(self.pen())
    18. for rect,flags,text in textSpecs:
    19. # this is the important part
    20. p.save()
    21. p.translate(rect.x(),rect.y())
    22. p.rotate(-30)
    23. p.drawText(-rect.width(),rect.height(),rect.width(),rect.height(),flags,text)
    24. # restoring the painter is *required*!!!
    25. p.restore()

     K线图控件

    1. class CandlestickItem(pg.GraphicsObject):
    2. def __init__(self, data):
    3. pg.GraphicsObject.__init__(self)
    4. self.data = data ## data must have fields: time, open, close, min, max
    5. self.generatePicture()
    6. def generatePicture(self):
    7. ## pre-computing a QPicture object allows paint() to run much more quickly,
    8. ## rather than re-drawing the shapes every time.
    9. self.picture = QtGui.QPicture()
    10. p = QtGui.QPainter(self.picture)
    11. p.setPen(pg.mkPen('d'))
    12. w = (self.data[1][0] - self.data[0][0]) / 3.
    13. for (t, open, close, min, max) in self.data:
    14. p.drawLine(QtCore.QPointF(t, min), QtCore.QPointF(t, max))
    15. if open < close:
    16. p.setBrush(pg.mkBrush('r'))
    17. else:
    18. p.setBrush(pg.mkBrush('g'))
    19. p.drawRect(QtCore.QRectF(t-w, open, w * 2, close - open))
    20. p.end()
    21. def paint(self, p, *args):
    22. p.drawPicture(0, 0, self.picture)
    23. def boundingRect(self):
    24. ## boundingRect _must_ indicate the entire area that will be drawn on
    25. ## or else we will get artifacts and possibly crashing.
    26. ## (in this case, QPicture does all the work of computing the bouning rect for us)
    27. return QtCore.QRectF(self.picture.boundingRect())

    条形图控件

    1. class PyQtGraphHistWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.init_data()
    5. self.init_ui()
    6. def init_data(self):
    7. self.color_bar = ()
    8. pass
    9. def init_ui(self):
    10. self.title_label = QtWidgets.QLabel('均线排列状态')
    11. self.title_label.setAlignment(Qt.AlignCenter)
    12. xax = RotateAxisItem(orientation='bottom')
    13. xax.setHeight(h=50)
    14. self.pw = pg.PlotWidget(axisItems={'bottom': xax})
    15. self.pw.setMouseEnabled(x=True, y=False)
    16. # self.pw.enableAutoRange(x=False,y=True)
    17. self.pw.setAutoVisible(x=False, y=True)
    18. layout = QtWidgets.QVBoxLayout()
    19. layout.addWidget(self.title_label)
    20. layout.addWidget(self.pw)
    21. self.setLayout(layout)
    22. pass
    23. def set_data(self,data:Dict[str,Any]):
    24. title_str = data['title_str']
    25. x = data['x']
    26. y = data['y']
    27. xTick = [data['xTick']]
    28. self.y_datas = y
    29. self.x_data = xTick
    30. self.x_Tick = data['xTick']
    31. self.title_label.setText(title_str)
    32. xax = self.pw.getAxis('bottom')
    33. xax.setTicks(xTick)
    34. barItem = pg.BarGraphItem(x=x,height=y,width=0.8,brush=(107,200,224))
    35. self.pw.addItem(barItem)
    36. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    37. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    38. self.label = pg.TextItem()
    39. self.pw.addItem(self.vLine, ignoreBounds=True)
    40. self.pw.addItem(self.hLine, ignoreBounds=True)
    41. self.pw.addItem(self.label, ignoreBounds=True)
    42. self.vb = self.pw.getViewBox()
    43. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    44. # 显示整条折线图
    45. self.pw.enableAutoRange()
    46. pass
    47. def mouseMoved(self,evt):
    48. pos = evt[0]
    49. if self.pw.sceneBoundingRect().contains(pos):
    50. mousePoint = self.vb.mapSceneToView(pos)
    51. index = int(mousePoint.x())
    52. if index >= 0 and index < len(self.y_datas):
    53. x_str = str(self.x_Tick[index][1])
    54. y_str_html = ''
    55. y_str = str(self.y_datas[index])
    56. y_str_html += ' ' + y_str
    57. html_str = '

       ' + x_str +'
       '
      +y_str_html+ '

      '
    58. self.label.setHtml(html_str)
    59. self.label.setPos(mousePoint.x(), mousePoint.y())
    60. self.vLine.setPos(mousePoint.x())
    61. self.hLine.setPos(mousePoint.y())
    62. pass
    63. pass

     K线图控件

    1. class PyQtGraphKWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.init_data()
    5. self.init_ui()
    6. def init_data(self):
    7. # https://www.sioe.cn/yingyong/yanse-rgb-16/
    8. self.color_line = (30, 144, 255)
    9. # 0 幽灵的白色; 1 纯黄; 2 紫红色; 3 纯绿; 4 道奇蓝
    10. self.color_list = [(248, 248, 255), (255, 255, 0), (255, 0, 255), (0, 128, 0), (30, 144, 255)]
    11. self.main_fixed_target_list = [] # 主体固定曲线,不能被删除
    12. self.whole_df = None
    13. self.whole_header = None
    14. self.whole_pd_header = None
    15. self.current_whole_data = None
    16. self.current_whole_df = None
    17. pass
    18. def init_ui(self):
    19. self.whole_duration_label = QtWidgets.QLabel('左边界~右边界')
    20. self.title_label = QtWidgets.QLabel('执行过程查看')
    21. self.title_label.setAlignment(Qt.AlignCenter)
    22. self.title_label.setStyleSheet('QLabel{font-size:18px;font-weight:bold}')
    23. xax = RotateAxisItem(orientation='bottom')
    24. xax.setHeight(h=80)
    25. self.pw = pg.PlotWidget(axisItems={'bottom': xax})
    26. self.pw.setMouseEnabled(x=True, y=True)
    27. # self.pw.enableAutoRange(x=False,y=True)
    28. self.pw.setAutoVisible(x=False, y=True)
    29. layout_right = QtWidgets.QVBoxLayout()
    30. layout_right.addWidget(self.title_label)
    31. layout_right.addWidget(self.whole_duration_label)
    32. layout_right.addWidget(self.pw)
    33. self.setLayout(layout_right)
    34. pass
    35. def set_data(self, data: Dict[str, Any]):
    36. title_str = data['title_str']
    37. whole_header = data['whole_header']
    38. whole_df = data['whole_df']
    39. whole_pd_header = data['whole_pd_header']
    40. self.whole_header = whole_header
    41. self.whole_df = whole_df
    42. self.whole_pd_header = whole_pd_header
    43. self.title_label.setText(title_str)
    44. self.whole_duration_label.setText(f"{self.whole_df.iloc[0]['tradeDate']}~{self.whole_df.iloc[-1]['tradeDate']}")
    45. self.current_whole_df = self.whole_df.copy()
    46. self.caculate_and_show_data()
    47. pass
    48. def caculate_and_show_data(self):
    49. df = self.current_whole_df.copy()
    50. df.reset_index(inplace=True)
    51. df['i_count'] = [i for i in range(len(df))]
    52. tradeDate_list = df['tradeDate'].values.tolist()
    53. x = range(len(df))
    54. xTick_show = []
    55. x_dur = math.ceil(len(df) / 20)
    56. for i in range(0, len(df), x_dur):
    57. xTick_show.append((i, tradeDate_list[i]))
    58. if len(df) % 20 != 0:
    59. xTick_show.append((len(df) - 1, tradeDate_list[-1]))
    60. candle_data = []
    61. for i, row in df.iterrows():
    62. candle_data.append(
    63. (row['i_count'], row['openPrice'], row['closePrice'], row['lowestPrice'], row['highestPrice']))
    64. self.current_whole_data = df.loc[:, self.whole_pd_header].values.tolist()
    65. # 开始配置显示的内容
    66. self.pw.clear()
    67. xax = self.pw.getAxis('bottom')
    68. xax.setTicks([xTick_show])
    69. candle_fixed_target = CandlestickItem(candle_data)
    70. self.main_fixed_target_list.append(candle_fixed_target)
    71. self.pw.addItem(candle_fixed_target)
    72. self.vLine = pg.InfiniteLine(angle=90, movable=False)
    73. self.hLine = pg.InfiniteLine(angle=0, movable=False)
    74. self.label = pg.TextItem()
    75. self.pw.addItem(self.vLine, ignoreBounds=True)
    76. self.pw.addItem(self.hLine, ignoreBounds=True)
    77. self.pw.addItem(self.label, ignoreBounds=True)
    78. self.vb = self.pw.getViewBox()
    79. self.proxy = pg.SignalProxy(self.pw.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
    80. self.pw.enableAutoRange()
    81. pass
    82. def mouseMoved(self, evt):
    83. pos = evt[0]
    84. if self.pw.sceneBoundingRect().contains(pos):
    85. mousePoint = self.vb.mapSceneToView(pos)
    86. index = int(mousePoint.x())
    87. if index >= 0 and index < len(self.current_whole_data):
    88. target_data = self.current_whole_data[index]
    89. html_str = ''
    90. for i, item in enumerate(self.whole_header):
    91. html_str += f"
      {item}:{target_data[i]}"
    92. self.label.setHtml(html_str)
    93. self.label.setPos(mousePoint.x(), mousePoint.y())
    94. self.vLine.setPos(mousePoint.x())
    95. self.hLine.setPos(mousePoint.y())
    96. pass
    97. def mouseClicked(self, evt):
    98. pass
    99. def updateViews(self):
    100. pass
    101. pass

    分页表格控件

    1. class PageTableWidget(QtWidgets.QWidget):
    2. output_signal = QtCore.pyqtSignal(object)
    3. def __init__(self):
    4. super().__init__()
    5. self.init_data()
    6. self.init_ui()
    7. pass
    8. def init_data(self):
    9. # 表格全数据 二维数组
    10. self.table_full_data: List[Any] = []
    11. # 表格右键菜单 {菜单名:索引指向}
    12. self.table_right_menus: Dict[str, str] = {}
    13. self.total_page_count: int = 0
    14. self.total_rows_count: int = 0
    15. self.current_page: int = 1
    16. self.single_page_rows: int = 50
    17. pass
    18. def init_ui(self):
    19. pre_page_btn = QtWidgets.QPushButton('上一页')
    20. pre_page_btn.clicked.connect(self.pre_page_btn_clicked)
    21. next_page_btn = QtWidgets.QPushButton('下一页')
    22. next_page_btn.clicked.connect(self.next_page_btn_clicked)
    23. tip_label_0 = QtWidgets.QLabel('第')
    24. self.witch_page_lineedit = QtWidgets.QLineEdit()
    25. self.int_validator = QtGui.QIntValidator()
    26. self.witch_page_lineedit.setValidator(self.int_validator)
    27. self.witch_page_lineedit.setMaximumWidth(20)
    28. tip_label_1 = QtWidgets.QLabel('页')
    29. go_page_btn = QtWidgets.QPushButton('前往')
    30. go_page_btn.clicked.connect(self.go_page_btn_clicked)
    31. layout_witch_page = QtWidgets.QHBoxLayout()
    32. layout_witch_page.addWidget(tip_label_0)
    33. layout_witch_page.addWidget(self.witch_page_lineedit)
    34. layout_witch_page.addWidget(tip_label_1)
    35. layout_witch_page.addWidget(go_page_btn)
    36. layout_witch_page.addStretch(1)
    37. layout_pagechange = QtWidgets.QHBoxLayout()
    38. layout_pagechange.addWidget(pre_page_btn)
    39. layout_pagechange.addWidget(next_page_btn)
    40. layout_pagechange.addLayout(layout_witch_page)
    41. self.total_page_count_label = QtWidgets.QLabel(f"共0页")
    42. self.total_rows_count_label = QtWidgets.QLabel(f"共0行")
    43. self.current_page_label = QtWidgets.QLabel(f"当前第0页")
    44. layout_pagestatus = QtWidgets.QHBoxLayout()
    45. layout_pagestatus.addWidget(self.total_page_count_label)
    46. layout_pagestatus.addWidget(self.total_rows_count_label)
    47. layout_pagestatus.addWidget(self.current_page_label)
    48. layout_pagestatus.addStretch(1)
    49. layout_top = QtWidgets.QVBoxLayout()
    50. layout_top.addLayout(layout_pagechange)
    51. layout_top.addLayout(layout_pagestatus)
    52. self.target_table = QtWidgets.QTableWidget()
    53. self.target_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
    54. self.target_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
    55. self.target_table.clicked.connect(self.target_table_clicked)
    56. layout = QtWidgets.QVBoxLayout()
    57. layout.addLayout(layout_top)
    58. layout.addWidget(self.target_table)
    59. self.setLayout(layout)
    60. pass
    61. def set_table_init_data(self, data: Dict[str, Any]):
    62. '''设置表头、右键菜单等初始化内容'''
    63. headers: List[str] = data['headers']
    64. self.target_table.setColumnCount(len(headers))
    65. self.target_table.setHorizontalHeaderLabels(headers)
    66. pass
    67. def set_table_full_data(self, data: List[Any]):
    68. '''表格全数据'''
    69. self.table_full_data = data
    70. self.total_rows_count = len(data)
    71. self.total_page_count = math.ceil(self.total_rows_count / self.single_page_rows)
    72. self.current_page = 1
    73. self.int_validator.setRange(1, self.total_page_count)
    74. self.total_page_count_label.setText(f"共{self.total_page_count}页")
    75. self.total_rows_count_label.setText(f"共{self.total_rows_count}行")
    76. self.caculate_current_show_data()
    77. pass
    78. def setting_current_pagestatus_label(self):
    79. self.current_page_label.setText(f"当前第{self.current_page}页")
    80. pass
    81. def caculate_current_show_data(self):
    82. '''计算当前要显示的数据'''
    83. start_dot = (self.current_page - 1) * self.single_page_rows
    84. end_dot = start_dot + self.single_page_rows
    85. current_data = self.table_full_data[start_dot:end_dot]
    86. self.fill_table_content(current_data)
    87. self.setting_current_pagestatus_label()
    88. pass
    89. def fill_table_content(self, data: List[Any]):
    90. self.target_table.clearContents()
    91. self.target_table.setRowCount(len(data))
    92. for r_i, r_v in enumerate(data):
    93. for c_i, c_v in enumerate(r_v):
    94. cell = QtWidgets.QTableWidgetItem(str(c_v))
    95. self.target_table.setItem(r_i, c_i, cell)
    96. pass
    97. pass
    98. self.target_table.resizeColumnsToContents()
    99. pass
    100. def go_page_btn_clicked(self):
    101. '''前往按钮点击'''
    102. input_page = self.witch_page_lineedit.text()
    103. input_page = input_page.strip()
    104. if not input_page:
    105. QtWidgets.QMessageBox.information(
    106. self,
    107. '提示',
    108. '请输入要跳转的页码',
    109. QtWidgets.QMessageBox.Yes
    110. )
    111. return
    112. input_page = int(input_page)
    113. if input_page < 0 or input_page > self.total_page_count:
    114. QtWidgets.QMessageBox.information(
    115. self,
    116. '提示',
    117. '输入的页码超出范围',
    118. QtWidgets.QMessageBox.Yes
    119. )
    120. return
    121. self.current_page = input_page
    122. self.caculate_current_show_data()
    123. pass
    124. def pre_page_btn_clicked(self):
    125. '''上一页按钮点击'''
    126. if self.current_page <= 1:
    127. QtWidgets.QMessageBox.information(
    128. self,
    129. '提示',
    130. '已经是首页',
    131. QtWidgets.QMessageBox.Yes
    132. )
    133. return
    134. self.current_page -= 1
    135. self.caculate_current_show_data()
    136. pass
    137. def next_page_btn_clicked(self):
    138. '''下一页按钮点击'''
    139. if self.current_page >= self.total_page_count:
    140. QtWidgets.QMessageBox.information(
    141. self,
    142. '提示',
    143. '已经是最后一页',
    144. QtWidgets.QMessageBox.Yes
    145. )
    146. return
    147. self.current_page += 1
    148. self.caculate_current_show_data()
    149. pass
    150. def target_table_clicked(self):
    151. selectedItems = self.target_table.selectedItems()
    152. if len(selectedItems)<=0:
    153. return
    154. res_list = []
    155. for item in selectedItems:
    156. res_list.append(item.text())
    157. self.output_signal.emit(res_list)
    158. pass
    159. pass

     执行结果显示控件

    1. class PyQtGraphRunningWidget(QtWidgets.QWidget):
    2. def __init__(self):
    3. super().__init__()
    4. self.init_data()
    5. self.init_ui()
    6. pass
    7. def init_data(self):
    8. self.pre_output_dir = './'
    9. self.results_output_dir = './results_output/'
    10. self.json_file_name_list = []
    11. self.dailydata_path: str = ''
    12. self.indexdata_path: str = ''
    13. self.table_header = ['股票编码','股票简称','相似度']
    14. self.please_select_str = '---请选择---'
    15. self.num_sort_map = {
    16. '升序':True,
    17. '降序':False
    18. }
    19. self.table_pd_header: List[str] = ['ticker','secName','similar_val']
    20. self.table_df = None
    21. self.current_table_df = None
    22. pass
    23. def init_ui(self):
    24. tip_5 = QtWidgets.QLabel('指数日数据文件夹:')
    25. self.indexdata_dir_lineedit = QtWidgets.QLineEdit()
    26. self.indexdata_dir_lineedit.setReadOnly(True)
    27. indexdata_choice_btn = QtWidgets.QPushButton('选择文件夹')
    28. indexdata_choice_btn.clicked.connect(self.indexdata_choice_btn_clicked)
    29. tip_0 = QtWidgets.QLabel('股票日数据文件夹:')
    30. self.dailydata_dir_lineedit = QtWidgets.QLineEdit()
    31. self.dailydata_dir_lineedit.setReadOnly(True)
    32. dailydata_choice_btn = QtWidgets.QPushButton('选择文件夹')
    33. dailydata_choice_btn.clicked.connect(self.dailydata_choice_btn_clicked)
    34. tip_1 = QtWidgets.QLabel('Json结果文件选择:')
    35. self.json_combox = QtWidgets.QComboBox()
    36. self.json_combox.addItem(self.please_select_str)
    37. self.json_combox.currentIndexChanged.connect(self.json_combox_currentIndexChanged)
    38. refresh_json_btn = QtWidgets.QPushButton('刷新结果下拉表')
    39. refresh_json_btn.clicked.connect(self.refresh_json_btn_clicked)
    40. layout_top = QtWidgets.QGridLayout()
    41. layout_top.addWidget(tip_5, 0, 0, 1, 1)
    42. layout_top.addWidget(self.indexdata_dir_lineedit, 0, 1, 1, 3)
    43. layout_top.addWidget(indexdata_choice_btn, 0, 4, 1, 1)
    44. layout_top.addWidget(tip_0,1,0,1,1)
    45. layout_top.addWidget(self.dailydata_dir_lineedit,1,1,1,3)
    46. layout_top.addWidget(dailydata_choice_btn,1,4,1,1)
    47. layout_top.addWidget(tip_1,2,0,1,1)
    48. layout_top.addWidget(self.json_combox,2,1,1,3)
    49. layout_top.addWidget(refresh_json_btn,2,4,1,1)
    50. tip_3 = QtWidgets.QLabel('股票名模糊查询')
    51. self.query_lineedit = QtWidgets.QLineEdit()
    52. query_btn = QtWidgets.QPushButton('查询')
    53. query_btn.clicked.connect(self.query_btn_clicked)
    54. reset_btn = QtWidgets.QPushButton('重置')
    55. reset_btn.clicked.connect(self.reset_btn_clicked)
    56. tip_4 = QtWidgets.QLabel('持续时间排序:')
    57. self.num_combox = QtWidgets.QComboBox()
    58. self.num_combox.addItem(self.please_select_str)
    59. self.num_combox.addItems(list(self.num_sort_map.keys()))
    60. self.num_combox.currentIndexChanged.connect(self.num_combox_currentIndexChanged)
    61. layout_query = QtWidgets.QGridLayout()
    62. layout_query.addWidget(tip_3,0,0,1,1)
    63. layout_query.addWidget(self.query_lineedit,0,1,1,3)
    64. layout_query.addWidget(query_btn,0,4,1,1)
    65. layout_query.addWidget(reset_btn,0,5,1,1)
    66. layout_query.addWidget(tip_4,1,0,1,1)
    67. layout_query.addWidget(self.num_combox,1,4,1,2)
    68. self.table = PageTableWidget()
    69. self.table.set_table_init_data({'headers': self.table_header})
    70. self.table.output_signal.connect(self.table_output_signal_emit)
    71. self.bar_widget = PyQtGraphHistWidget()
    72. layout_left_up = QtWidgets.QVBoxLayout()
    73. layout_left_up.addLayout(layout_query)
    74. layout_left_up.addWidget(self.table)
    75. layout_left = QtWidgets.QVBoxLayout()
    76. layout_left.addLayout(layout_left_up,3)
    77. layout_left.addWidget(self.bar_widget,1)
    78. self.index_k_widget = PyQtGraphKWidget()
    79. self.other_k_widget = PyQtGraphKWidget()
    80. layout_right = QtWidgets.QVBoxLayout()
    81. layout_right.addWidget(self.other_k_widget)
    82. layout_right.addWidget(self.index_k_widget)
    83. layout_down = QtWidgets.QHBoxLayout()
    84. layout_down.addLayout(layout_left,1)
    85. layout_down.addSpacing(30)
    86. layout_down.addLayout(layout_right,2)
    87. layout = QtWidgets.QVBoxLayout()
    88. layout.addLayout(layout_top)
    89. layout.addLayout(layout_down)
    90. self.setLayout(layout)
    91. pass
    92. def set_json_data(self,data:Dict[str,Any]):
    93. self.table_df = data['df']
    94. self.start_date_str = data['start_date_str']
    95. self.end_date_str = data['end_date_str']
    96. self.current_table_df = self.table_df.copy()
    97. self.fill_table_data()
    98. bar_data = data['bar_data']
    99. self.bar_widget.set_data(bar_data)
    100. pass
    101. def fill_table_data(self):
    102. table_data = self.current_table_df.loc[:, self.table_pd_header].values.tolist()
    103. self.table.set_table_full_data(table_data)
    104. pass
    105. def table_output_signal_emit(self,data:List):
    106. # ticker secName status_name status_num
    107. dailydata_dir = self.dailydata_dir_lineedit.text()
    108. dailydata_dir = dailydata_dir.strip()
    109. if len(dailydata_dir)<=0:
    110. QtWidgets.QMessageBox.information(
    111. self,
    112. '提示',
    113. '请选择股票日数据文件件',
    114. QtWidgets.QMessageBox.Yes
    115. )
    116. return
    117. daily_file_path = dailydata_dir + '/' + data[0] + '.csv'
    118. df = pd.read_csv(daily_file_path,encoding='utf-8')
    119. # 删除停牌的数据
    120. df = df.loc[df['openPrice'] > 0].copy()
    121. df['o_date'] = df['tradeDate']
    122. df['o_date'] = pd.to_datetime(df['o_date'])
    123. df = df.loc[(df['o_date'] >= self.start_date_str) & (df['o_date']<=self.end_date_str)].copy()
    124. # 保存未复权收盘价数据
    125. df['close'] = df['closePrice']
    126. # 计算前复权数据
    127. df['openPrice'] = df['openPrice'] * df['accumAdjFactor']
    128. df['closePrice'] = df['closePrice'] * df['accumAdjFactor']
    129. df['highestPrice'] = df['highestPrice'] * df['accumAdjFactor']
    130. df['lowestPrice'] = df['lowestPrice'] * df['accumAdjFactor']
    131. columns_list = ['日期','收盘价','开盘价','最高价','最低价']
    132. columns_pd_list = ['tradeDate','closePrice','openPrice','highestPrice','lowestPrice']
    133. line_data = {
    134. 'title_str':data[1],
    135. 'whole_header':columns_list,
    136. 'whole_df':df,
    137. 'whole_pd_header':columns_pd_list
    138. }
    139. self.other_k_widget.set_data(line_data)
    140. pass
    141. def indexdata_choice_btn_clicked(self):
    142. path = QtWidgets.QFileDialog.getExistingDirectory(
    143. self,
    144. '打开指数日数据所在文件夹',
    145. self.pre_output_dir
    146. )
    147. if not path:
    148. return
    149. self.indexdata_path = path
    150. self.indexdata_dir_lineedit.setText(path)
    151. pass
    152. def dailydata_choice_btn_clicked(self):
    153. path = QtWidgets.QFileDialog.getExistingDirectory(
    154. self,
    155. '打开股票日数据所在文件夹',
    156. self.pre_output_dir
    157. )
    158. if not path:
    159. return
    160. self.dailydata_path = path
    161. self.dailydata_dir_lineedit.setText(path)
    162. pass
    163. def json_combox_currentIndexChanged(self,cur_i:int):
    164. cur_txt = self.json_combox.currentText()
    165. if not cur_txt or cur_txt == self.please_select_str:
    166. return
    167. current_json_file_path = self.results_output_dir + cur_txt
    168. with open(current_json_file_path,'r',encoding='utf-8') as fr:
    169. obj_json = json.load(fr)
    170. index_file_name = obj_json['index_file_name']
    171. start_date_str = obj_json['start_date_str']
    172. end_date_str = obj_json['end_date_str']
    173. indexdata_dir = self.indexdata_dir_lineedit.text()
    174. indexdata_dir = indexdata_dir.strip()
    175. if len(indexdata_dir) <= 0:
    176. QtWidgets.QMessageBox.information(
    177. self,
    178. '提示',
    179. '请先选择指数日数据所在文件夹',
    180. QtWidgets.QMessageBox.Yes
    181. )
    182. return
    183. indexfile_path = indexdata_dir +'/'+ index_file_name
    184. if not os.path.exists(indexfile_path):
    185. QtWidgets.QMessageBox.information(
    186. self,
    187. '提示',
    188. '不存在,'+index_file_name,
    189. QtWidgets.QMessageBox.Yes
    190. )
    191. return
    192. # 显示指数K线图
    193. df_index = pd.read_csv(indexfile_path,encoding='utf-8')
    194. df_index['o_date'] = df_index['tradeDate']
    195. df_index['o_date'] = pd.to_datetime(df_index['o_date'])
    196. df_index = df_index.loc[(df_index['o_date']>=start_date_str) & (df_index['o_date']<=end_date_str)].copy()
    197. df_index['openPrice'] = df_index['openIndex']
    198. df_index['closePrice'] = df_index['closeIndex']
    199. df_index['highestPrice'] = df_index['highestIndex']
    200. df_index['lowestPrice'] = df_index['lowestIndex']
    201. title_str = index_file_name.split('.')[0]
    202. whole_header = ['日期','收','开','高','低']
    203. whole_df = df_index
    204. whole_pd_header = ['tradeDate','closePrice','openPrice','highestPrice','lowestPrice']
    205. index_line_data = {
    206. 'title_str':title_str,
    207. 'whole_header':whole_header,
    208. 'whole_df':whole_df,
    209. 'whole_pd_header':whole_pd_header
    210. }
    211. self.index_k_widget.set_data(index_line_data)
    212. df = pd.DataFrame(obj_json['df_json'])
    213. obj_json['df'] = df
    214. self.set_json_data(obj_json)
    215. pass
    216. def query_btn_clicked(self):
    217. query_str = self.query_lineedit.text()
    218. query_str = query_str.strip()
    219. if len(query_str)<=0:
    220. QtWidgets.QMessageBox.information(
    221. self,
    222. '提示',
    223. '请输入要查询的内容',
    224. QtWidgets.QMessageBox.Yes
    225. )
    226. return
    227. self.status_combox.setCurrentText(self.please_select_str)
    228. df = self.table_df.copy()
    229. self.current_table_df = df.loc[df['secName'].str.contains(query_str)].copy()
    230. self.fill_table_data()
    231. pass
    232. def reset_btn_clicked(self):
    233. self.query_lineedit.setText('')
    234. self.status_combox.setCurrentText(self.please_select_str)
    235. self.current_table_df = self.table_df.copy()
    236. self.fill_table_data()
    237. pass
    238. def num_combox_currentIndexChanged(self,cur_i:int):
    239. cur_txt = self.num_combox.currentText()
    240. if cur_txt == self.please_select_str:
    241. return
    242. self.current_table_df.sort_values(by='similar_val',ascending=self.num_sort_map[cur_txt],inplace=True)
    243. self.fill_table_data()
    244. pass
    245. def refresh_json_btn_clicked(self):
    246. indexdata_dir = self.indexdata_dir_lineedit.text()
    247. indexdata_dir = indexdata_dir.strip()
    248. if len(indexdata_dir)<=0:
    249. QtWidgets.QMessageBox.information(
    250. self,
    251. '提示',
    252. '请先选择指数日数据所在文件夹',
    253. QtWidgets.QMessageBox.Yes
    254. )
    255. return
    256. file_list = os.listdir(self.results_output_dir)
    257. json_file_list = []
    258. for item in file_list:
    259. if item.endswith('.json'):
    260. json_file_list.append(item)
    261. self.json_file_name_list.extend(json_file_list)
    262. json_file_set = set(self.json_file_name_list)
    263. self.json_file_name_list = list(json_file_set)
    264. self.json_combox.clear()
    265. self.json_combox.addItem(self.please_select_str)
    266. self.json_combox.addItems(self.json_file_name_list)
    267. pass
    268. pass

    策略操作运行控件

     

    1. class StrategeMainWidget(QtWidgets.QWidget):
    2. signal_runcode = QtCore.pyqtSignal(object)
    3. signal_time = QtCore.pyqtSignal(object)
    4. def __init__(self):
    5. super().__init__()
    6. self.thread_run: Thread = None
    7. self.thread_time: Thread = None
    8. self.running_graph_widget: QtWidgets.QWidget = None
    9. self.init_data()
    10. self.init_ui()
    11. self.register_event()
    12. pass
    13. def init_data(self):
    14. self.pre_output_dir = './'
    15. self.results_output_dir = './results_output/'
    16. self.secID_name_file_name = 'secID_name.csv'
    17. self.please_select_str: str = '--请选择--'
    18. self.stratege_name_list: List = []
    19. self.tip_msg_0: str = '1.选择策略所在文件夹;2.选择策略;3.点击运行。'
    20. self.stratege_path: str = ''
    21. self.stratege_run_start_time = None
    22. self.stratege_start = False
    23. self.current_stratege_py_str: str = ''
    24. self.dailydata_path:str = ''
    25. pass
    26. def init_ui(self):
    27. self.setWindowTitle('股票K线图相似度工具')
    28. tip_3 = QtWidgets.QLabel('指数日数据文件:')
    29. self.indexfilepath_lineedit = QtWidgets.QLineEdit()
    30. self.indexfilepath_lineedit.setReadOnly(True)
    31. indexfilepath_choice_btn = QtWidgets.QPushButton('选择文件')
    32. indexfilepath_choice_btn.clicked.connect(self.indexfilepath_choice_btn_clicked)
    33. tip_2 = QtWidgets.QLabel('股票日数据文件夹:')
    34. self.dailydata_dir_lineedit = QtWidgets.QLineEdit()
    35. self.dailydata_dir_lineedit.setReadOnly(True)
    36. dailydata_choice_btn = QtWidgets.QPushButton('选择文件夹')
    37. dailydata_choice_btn.clicked.connect(self.dailydata_choice_btn_clicked)
    38. tip_0 = QtWidgets.QLabel('选择策略所在文件夹:')
    39. self.stratege_dir_lineedit = QtWidgets.QLineEdit()
    40. self.stratege_dir_lineedit.setReadOnly(True)
    41. stratege_choice_btn = QtWidgets.QPushButton('选择文件夹')
    42. stratege_choice_btn.clicked.connect(self.stratege_choice_btn_clicked)
    43. tip_1 = QtWidgets.QLabel('策略:')
    44. self.stratege_combox = QtWidgets.QComboBox()
    45. self.stratege_combox.addItem(self.please_select_str)
    46. self.stratege_combox.currentIndexChanged.connect(self.stratege_combox_currentIndexChanged)
    47. self.run_btn = QtWidgets.QPushButton('运行')
    48. self.run_btn.clicked.connect(self.run_btn_clicked)
    49. self.force_stop_btn = QtWidgets.QPushButton('强制停止')
    50. self.force_stop_btn.clicked.connect(self.force_stop_btn_clicked)
    51. layout_top_left = QtWidgets.QGridLayout()
    52. layout_top_left.addWidget(tip_3, 0, 0, 1, 1)
    53. layout_top_left.addWidget(self.indexfilepath_lineedit, 0, 1, 1, 3)
    54. layout_top_left.addWidget(indexfilepath_choice_btn, 0, 4, 1, 1)
    55. layout_top_left.addWidget(tip_2,1,0,1,1)
    56. layout_top_left.addWidget(self.dailydata_dir_lineedit,1,1,1,3)
    57. layout_top_left.addWidget(dailydata_choice_btn,1,4,1,1)
    58. layout_top_left.addWidget(tip_0,2,0,1,1)
    59. layout_top_left.addWidget(self.stratege_dir_lineedit,2,1,1,3)
    60. layout_top_left.addWidget(stratege_choice_btn,2,4,1,1)
    61. layout_top_left.addWidget(tip_1,3,0,1,1)
    62. layout_top_left.addWidget(self.stratege_combox,3,1,1,2)
    63. layout_top_left.addWidget(self.run_btn,3,3,1,1)
    64. layout_top_left.addWidget(self.force_stop_btn,3,4,1,1)
    65. self.tip_msg_label = QtWidgets.QLabel()
    66. self.tip_msg_label.setWordWrap(True)
    67. self.tip_msg_label.setText(self.tip_msg_0)
    68. results_output_look_btn = QtWidgets.QPushButton('结果查看')
    69. results_output_look_btn.clicked.connect(self.results_output_look_btn_clicked)
    70. layout_top_right = QtWidgets.QHBoxLayout()
    71. layout_top_right.addWidget(self.tip_msg_label)
    72. layout_top_right.addWidget(results_output_look_btn)
    73. layout_top = QtWidgets.QHBoxLayout()
    74. layout_top.addLayout(layout_top_left,3)
    75. layout_top.addSpacing(30)
    76. layout_top.addLayout(layout_top_right,1)
    77. self.code_textedit = QtWidgets.QTextEdit()
    78. self.code_textedit.setReadOnly(True)
    79. layout = QtWidgets.QVBoxLayout()
    80. layout.addLayout(layout_top)
    81. layout.addWidget(self.code_textedit)
    82. self.setLayout(layout)
    83. pass
    84. def register_event(self):
    85. self.signal_runcode.connect(self.thread_run_excuted)
    86. self.signal_time.connect(self.thread_time_excuted)
    87. pass
    88. def results_output_look_btn_clicked(self):
    89. '''策略运行结果查看'''
    90. if not self.running_graph_widget:
    91. self.running_graph_widget = PyQtGraphRunningWidget()
    92. self.running_graph_widget.showMaximized()
    93. pass
    94. def indexfilepath_choice_btn_clicked(self):
    95. path, _ = QtWidgets.QFileDialog.getOpenFileName(
    96. self,
    97. '打开指数日数据文件',
    98. self.pre_output_dir
    99. )
    100. if not path:
    101. return
    102. self.indexfilepath_lineedit.setText(path)
    103. pass
    104. def dailydata_choice_btn_clicked(self):
    105. path = QtWidgets.QFileDialog.getExistingDirectory(
    106. self,
    107. '打开股票日数据所在文件夹',
    108. self.pre_output_dir
    109. )
    110. if not path:
    111. return
    112. self.dailydata_path = path+'/'
    113. self.dailydata_dir_lineedit.setText(path)
    114. pass
    115. def stratege_choice_btn_clicked(self):
    116. '''选择策略所在文件夹'''
    117. path = QtWidgets.QFileDialog.getExistingDirectory(
    118. self,
    119. '打开策略所在文件夹',
    120. self.pre_output_dir
    121. )
    122. if not path:
    123. return
    124. self.stratege_path = path
    125. self.stratege_dir_lineedit.setText(path)
    126. file_list = os.listdir(path)
    127. temp_file_list = set(self.stratege_name_list)
    128. for item in file_list:
    129. if item.endswith('.py'):
    130. temp_file_list.add(item)
    131. self.stratege_name_list = list(temp_file_list)
    132. self.stratege_combox.clear()
    133. self.stratege_combox.addItem(self.please_select_str)
    134. self.stratege_combox.addItems(self.stratege_name_list)
    135. pass
    136. def stratege_combox_currentIndexChanged(self,cur_i:int):
    137. cur_txt = self.stratege_combox.currentText()
    138. if not cur_txt or cur_txt == self.please_select_str:
    139. self.code_textedit.clear()
    140. return
    141. file_path = self.stratege_path + os.path.sep + cur_txt
    142. with open(file_path,'r',encoding='utf-8') as fr:
    143. code_txt = fr.read()
    144. self.code_textedit.setPlainText(code_txt)
    145. pass
    146. def run_btn_clicked(self):
    147. '''运行按钮'''
    148. # 检查指数日数据文件
    149. indexfilepath = self.indexfilepath_lineedit.text()
    150. indexfilepath = indexfilepath.strip()
    151. if len(indexfilepath)<=0:
    152. QtWidgets.QMessageBox.information(
    153. self,
    154. '提示',
    155. '请选择指数日数据文件',
    156. QtWidgets.QMessageBox.Yes
    157. )
    158. return
    159. # 检查股票日数据文件夹
    160. dailydata_dir = self.dailydata_dir_lineedit.text()
    161. dailydata_dir = dailydata_dir.strip()
    162. if len(dailydata_dir)<=0:
    163. QtWidgets.QMessageBox.information(
    164. self,
    165. '提示',
    166. '请选择股票日数据文件夹',
    167. QtWidgets.QMessageBox.Yes
    168. )
    169. return
    170. dailydata_file_list = os.listdir(dailydata_dir)
    171. if len(dailydata_file_list)<=0:
    172. QtWidgets.QMessageBox.information(
    173. self,
    174. '提示',
    175. '股票日数据文件夹中没有文件',
    176. QtWidgets.QMessageBox.Yes
    177. )
    178. return
    179. secID_name_file = self.results_output_dir + self.secID_name_file_name
    180. if not os.path.exists(secID_name_file):
    181. QtWidgets.QMessageBox.information(
    182. self,
    183. '提示',
    184. '股票码与股票名基础文件不存在',
    185. QtWidgets.QMessageBox.Yes
    186. )
    187. return
    188. py_str = self.code_textedit.toPlainText()
    189. if len(py_str)<10:
    190. QtWidgets.QMessageBox.information(
    191. self,
    192. '提示',
    193. '请选择要执行的策略',
    194. QtWidgets.QMessageBox.Yes
    195. )
    196. return
    197. self.current_stratege_py_str = py_str
    198. base_data = {}
    199. base_df = pd.read_csv(secID_name_file,encoding='utf-8')
    200. for i,row in base_df.iterrows():
    201. secID = row['secID']
    202. secID_arr = secID.split('.')
    203. base_data[secID_arr[0]] = row['secShortName']
    204. pass
    205. self.run_btn.setDisabled(True)
    206. self.stratege_combox.setDisabled(True)
    207. self.stratege_run_start_time = datetime.now()
    208. self.stratege_start = True
    209. if self.thread_run:
    210. QtWidgets.QMessageBox.information(
    211. self,
    212. '提示',
    213. '有策略正在运行',
    214. QtWidgets.QMessageBox.Yes
    215. )
    216. return
    217. pre_data = {
    218. 'py_str':py_str,
    219. 'base_data':base_data,
    220. 'indexfilepath':indexfilepath
    221. }
    222. self.thread_run = Thread(
    223. target=self.running_run_thread,
    224. args=(pre_data,)
    225. )
    226. self.thread_run.start()
    227. self.thread_time = Thread(
    228. target=self.running_time_thread
    229. )
    230. self.thread_time.start()
    231. pass
    232. def force_stop_btn_clicked(self):
    233. '''强制停止按钮'''
    234. self.thread_run = None
    235. self.thread_time = None
    236. self.run_btn.setDisabled(False)
    237. self.stratege_combox.setDisabled(False)
    238. self.stratege_start = False
    239. pass
    240. def running_run_thread(self,data:Dict[str,Any]):
    241. '''执行代码线程'''
    242. py_str = data['py_str']
    243. base_data = data['base_data']
    244. indexfilepath = data['indexfilepath']
    245. namespace = {}
    246. fun_stragegy = compile(py_str,'','exec')
    247. exec(fun_stragegy,namespace)
    248. ret = namespace['excute_strategy'](base_data,self.dailydata_path,indexfilepath)
    249. self.signal_runcode.emit(ret)
    250. pass
    251. def running_time_thread(self):
    252. '''计时线程'''
    253. while self.stratege_start:
    254. now = datetime.now()
    255. interval_time = (now-self.stratege_run_start_time).seconds
    256. res_map = {'res':interval_time}
    257. self.signal_time.emit(res_map)
    258. time.sleep(1)
    259. pass
    260. def thread_run_excuted(self,data:Dict):
    261. '''策略代码执行返回结果'''
    262. self.run_btn.setDisabled(False)
    263. self.stratege_combox.setDisabled(False)
    264. # 保存结果文件
    265. now_datetime_str = datetime.now().strftime('%Y%m%d%H%M%S')
    266. df = data['df']
    267. df_json = df.to_dict(orient='records')
    268. pre_save_data = {
    269. 'df_json':df_json,
    270. 'end_date_str':data['end_date_str'],
    271. 'start_date_str':data['start_date_str'],
    272. 'index_file_name':data['index_file_name'],
    273. 'bar_data':data['bar_data']
    274. }
    275. with open(self.results_output_dir + now_datetime_str + '.json','w',encoding='utf-8') as fw:
    276. json.dump(pre_save_data,fw)
    277. if not self.running_graph_widget:
    278. self.running_graph_widget = PyQtGraphRunningWidget()
    279. self.running_graph_widget.showMaximized()
    280. self.thread_run = None
    281. self.thread_time = None
    282. self.stratege_start = False
    283. QtWidgets.QMessageBox.information(
    284. self,
    285. '提示',
    286. '当前策略运行完毕',
    287. QtWidgets.QMessageBox.Yes
    288. )
    289. pass
    290. def thread_time_excuted(self,data:Dict):
    291. '''计时返回结果'''
    292. res = data['res']
    293. self.tip_msg_label.setText(f"{res}s")
    294. pass
    295. def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
    296. if self.thread_time:
    297. self.thread_time.join()
    298. if self.thread_run:
    299. self.thread_run.join()
    300. if self.running_graph_widget:
    301. self.running_graph_widget.close()
    302. self.close()

    工具使用

    1. if __name__ == '__main__':
    2. QtCore.QCoreApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    3. app = QtWidgets.QApplication(sys.argv)
    4. t_win = StrategeMainWidget()
    5. t_win.showMaximized()
    6. app.exec()
    7. pass

    1. 在入口代码对应的py文件的同一目录下创建 results_output 目录

    2. 下载数据,将 secID_name.csv 文件放入 results_output 目录里

    3. 程序运行策略后,会把执行结果放入 results_output 目录中,以json文件存储

     

    数据

    链接:https://pan.baidu.com/s/1acghH4GQyu_OaOUmEjPdHw 
    提取码:6toe

  • 相关阅读:
    go语言面试
    Java练习题-输出二维数组对角线元素和
    新建一个ARXML数据库文件
    网络安全总结
    二十三种设计模式全面解析-迭代器模式进阶篇:探索变体与扩展
    C++中的文件输入/输出
    java计算机毕业设计酒店预订管理系统源码+mysql数据库+系统+lw文档+部署
    HFSS笔记——求解器和求解分析
    自学WEB后端01-安装Express+Node.js框架完成Hello World!
    网络协议--ARP:地址解析协议
  • 原文地址:https://blog.csdn.net/m0_37967652/article/details/127659195