金融科技之交易:動量效應選股策略

這是本蒟蒻在實習時做的一個小項目,主要是篩選與大盤偏離的股票,並製作成帶有UI界面的exe文件,供基金經理使用。在徵得公司同意後將其整理髮布,記錄自己的學習歷程。

策略內容:

動量效應:動量效應一般又稱“慣性效應”。是指股票的收益率有延續原來的運動方向的趨勢,即過去一段時間收益率較高的股票在未來獲得的收益率仍會高於過去收益率較低的股票。

在這裏,我們用股票相對於上證綜指的超額收益來表示收益率。我們希望篩選出在過去的一段時間內超額收益在特定範圍的股票。
但是超額收益作爲一個數值對於大部分的人來說並不直觀,所以我們可以用股票的價格線與指數的價格線之間的夾角的角度來直觀地感受超額收益。

而價格線的繪製可以有兩種方法:1.邏輯迴歸線 2.第一天和最後一天的價格點的連線。同時價格要轉換成價格相較於第一天的增幅,這樣可以將股票和指數的兩個起始點放在同一個點上。
如下圖:
在這裏插入圖片描述
在這裏插入圖片描述

同時要注意數據的標準化,將角度的計算放在我們熟悉的單位長度相等的笛卡爾座標系下,這樣計算出來的結果才更接近我們直觀觀察的角度。

代碼整理

因爲本蒟蒻水平有限,接觸編程的時間不長。如果有代碼不規範、語句冗餘等問題還希望不吝賜教。

同時因爲源碼細節較多,這裏只給出核心的代碼。

這裏先給出效果圖:

圖片圖片圖片

圖片

角度計算

標準化處理

因爲參與運算的數據氛圍不一致。價格變化率的絕對值一般不超過(-5,5)。而日期時間的序號是從0到數十或數百(由選擇的時間範圍覺得)。這樣計算出的直線的斜率都是很小的值。所以將數據進行標準化處理,這樣計算出的斜率才和觀察的相近。因爲我們估算直線斜率一般會把看到的直線默認爲是在兩個座標軸的單位長度和壓縮比率相同的座標系中。

這裏使用最大最小標準化處理,將價格變化率和日期序列都映射到【0,1】。

  def MaxMinNormalization(self,x, min, max):
      """[0,1] normaliaztion"""
      x = (x - min) / (max - min)
      return x

數據準備

將tushare中獲取的數據進行初步處理。包括計算價格漲幅、標準化處理等

#這裏的st、index都是從tushare中獲取的數據。
        st_x = array([i for i in range(0, len(st['close']))])
      #日期轉換成序號
        st_x = self.MaxMinNormalization(st_x, 0, len(st['close']) - 1)
        #標準化處理
        st_x = array([st_x]).T
       # T爲矩陣轉置把1xn變成nx1

        index_x = array([i for i in range(0, len(index['close']))])
        index_x = self.MaxMinNormalization(index_x, 0, len(index['close']) - 1)        
        index_x = array([index_x]).T
      #計算漲幅
        st_y = array([(close - st['close'][0]) / st['close'][0] for close in st['close']])
        index_y = array([(close - index['close'][0]) / index['close'][0] for close in index['close']])
        min = hstack((st_y, index_y)).min()
        max = hstack((st_y, index_y)).max()
        #因爲兩條直線要畫在同一個圖中,所以要一起進行標準化處理,最大和最小值是股票和指數共同的最大值和最小值。
        st_y = self.MaxMinNormalization(st_y, min, max)
        st_y = array([st_y]).T
        index_y = self.MaxMinNormalization(index_y, min, max)
        index_y = array([index_y]).T
        #標準化處理

迴歸線的斜率

#線性迴歸
regr_st = LinearRegression().fit(st_x, st_y) #訓練,得到股票的迴歸線
regr_index = LinearRegression().fit(index_x, index_y)#得到指數的迴歸線
st_coef=regr_st.coef_[0][0],
index_coef =regr_index.coef_[0][0])
#分別是股票和指數迴歸線的斜率

兩點連線的斜率

st_coef = list(st_y)[-1][0]-list(st_y)[0][0])/list(st_x)[-1][0]
index_coef =list(index_y)[-1][0]-list(index_y)[0][0])/list(index_x)[-1][0]

由斜率計算角度

x = array([1, st_coef])  # 方向向量
y = array([1, index])

Lx = sqrt(x.dot(x))
Ly = sqrt(y.dot(y))
# 求得斜線的長度
cos_angle = x.dot(y) / (Lx * Ly)
# 求得cos_sita的值再反過來計算,絕對長度乘以cos角度爲矢量長度
angle = arccos(cos_angle) * 360 / 2 / 3.1415926

計算模塊的整合

將上面介紹的整合成一個類

import tushare as ts
import datetime
from sklearn.linear_model import LinearRegression
from numpy import array,hstack,sqrt,arccos

class clac():
   #數據標準化處理函數
    def MaxMinNormalization(self,x, min, max):
        """[0,1] normaliaztion"""
        x = (x - min) / (max - min)
        return x
   #得到兩個直線的斜率
    def get_coef(self,st, index,type):
        st_x = array([i for i in range(0, len(st['close']))])
        st_x = self.MaxMinNormalization(st_x, 0, len(st['close']) - 1)
        # T爲矩陣轉置把1xn變成nx1
        st_x = array([st_x]).T
        index_x = array([i for i in range(0, len(index['close']))])
        index_x = self.MaxMinNormalization(index_x, 0, len(index['close']) - 1)
        # T爲矩陣轉置把1xn變成nx1
        index_x = array([index_x]).T
        st_y = array([(close - st['close'][0]) / st['close'][0] for close in st['close']])
        index_y = array([(close - index['close'][0]) / index['close'][0] for close in index['close']])
        min = hstack((st_y, index_y)).min()
        max = hstack((st_y, index_y)).max()
        st_y = self.MaxMinNormalization(st_y, min, max)
        st_y = array([st_y]).T
        index_y = self.MaxMinNormalization(index_y, min, max)
        index_y = array([index_y]).T
        if type == '迴歸線':  # 是迴歸線版本
            # regr爲迴歸過程,fit(x,y)進行迴歸
            regr_st = LinearRegression().fit(st_x, st_y)
            regr_index = LinearRegression().fit(index_x, index_y)
            return regr_st.coef_[0][0], regr_index.coef_[0][0]
        else:#是兩點連線
            return (list(st_y)[-1][0]-list(st_y)[0][0])/list(st_x)[-1][0],(list(index_y)[-1][0]-list(index_y)[0][0])/list(index_x)[-1][0]

    def get_angle(self,x, y):#計算兩個直線的角度
        # x和y是方向向量
        Lx = sqrt(x.dot(x))
        Ly = sqrt(y.dot(y))
        # 相當於勾股定理,求得斜線的長度
        cos_angle = x.dot(y) / (Lx * Ly)
        # 求得cos_sita的值再反過來計算,絕對長度乘以cos角度爲矢量長度
        angle = arccos(cos_angle) * 360 / 2 / 3.1415926
        return angle
    def angle(self,st_code,months):#最終的包裝,由外部調用
    
     #獲取數據
        pro = ts.pro_api('你的token')
        start_date = datetime.datetime.today() - datetime.timedelta(days=int(30*months))
        start_date = start_date.date().strftime("%Y%m%d")
        st_price = pro.daily(ts_code=st_code, start_date=start_date, fields='ts_code,trade_date,close')
        end_date=st_price['trade_date'][0]
        st_price=st_price.sort_values(["trade_date"], ascending=True).reset_index(drop=True)
        start_date = st_price['trade_date'][0]
        index_close = pro.index_daily(ts_code='000001.SH', start_date=start_date, end_date=end_date,fields='ts_code,trade_date,close')
        index_close = index_close.sort_values(["trade_date"], ascending=True).reset_index(drop=True)
        
    #得到斜率
        st_coef, index_coef = self.get_coef(st_price, index_close,'迴歸線')
        x = array([1, st_coef])  # 迴歸線的方向向量
        y = array([1, index_coef])
        angle0 = self.get_angle(x, y)
        if st_coef < index_coef:
            angle0 = -1 * angle0
        st_coef, index_coef = self.get_coef(st_price, index_close, '兩點連線')
        x = array([1, st_coef])  # 兩點連線方向向量
        y = array([1, index_coef])
        angle1 = self.get_angle(x, y)
        if st_coef < index_coef:
            angle1 = -1 * angle1
        return (angle0,angle1)

繪製疊加圖

在詳細窗口界面展示了兩個圖像,一個是K線圖,一個是指數疊加圖。K線圖是從東方財富網爬取下來的。已經在上面介紹過了。而指數疊加圖需要我們自己繪製。在Pyqt5窗口中展示matplotlib繪製的圖,需要創建畫布。定義一個畫布類:

from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure

class PlotCanvas(FigureCanvas):##畫布
    def __init__(self, parent=None,width=5, height=4, dpi=100):#初始化
        fig = Figure(figsize=(width, height), dpi=dpi, edgecolor="blue")
        plt.rcParams['font.sans-serif'] = ['SimHei']
        plt.rcParams['axes.unicode_minus'] = False
        self.axes = fig.add_subplot(111)
        self.parent=parent
        fig.subplots_adjust(left=0.1, bottom=0.1, top=0.9, right=0.9)
        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,
                                   QSizePolicy.Expanding,
                                   QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)


        self.drawLine(self.parent.ui.comboBox_1.currentText())

    def drawLine(self,type):#繪製圖像
        pro = ts.pro_api('請填入你的token')
        start_date = datetime.datetime.today() - datetime.timedelta(days=int(30 * self.parent.months))
        start_date = start_date.date().strftime("%Y%m%d")

        st = pro.daily(ts_code=self.parent.stCode, start_date=start_date, fields='ts_code,trade_date,close')
        end_date=st['trade_date'][0]
        st = st.sort_values(["trade_date"], ascending=True).reset_index(drop=True)
        st_close = [(close - st['close'][0]) / st['close'][0] for close in st['close']]
        start_date=st['trade_date'][0]

        index = pro.index_daily(ts_code='000001.SH', start_date=start_date,end_date=end_date, fields='ts_code,trade_date,close')
        index = index.sort_values(["trade_date"], ascending=True).reset_index(drop=True)
        index_close = [(close - index['close'][0]) / index['close'][0] for close in index['close']]

        index_date = ['{}-{}-{}'.format(date[:4],date[4:6],date[6:]) for date in index['trade_date']]

        st_x = array([i for i in range(0, len(st['trade_date']))])
        st_x = array([st_x]).T

        index_x = array([i for i in range(0, len(index['trade_date']))])
        index_x = array([index_x]).T

        st_y = array([(close - st['close'][0]) / st['close'][0] for close in st['close']])
        index_y = array([(close - index['close'][0]) / index['close'][0] for close in index['close']])


        st_y = array([st_y]).T
        index_y = array([index_y]).T
        self.st_x=st_x
        self.st_y=st_y
        self.index_x=index_x
        self.index_y=index_y
        self.axes.cla()
        self.axes.set_title("大盤指數疊加")
        
        def to_percent(temp, position):
            return '%1.0f' % (100 * t

        self.axes.yaxis.set_major_formatter(FuncFormatter(to_percent))  # 更改y軸的顯示內容爲百分數
        self.axes.plot(index_x, index_close, color='red', label='上證指數', linewidth=0.5)
        self.axes.plot(st_x, st_close, color='blue', label=self.parent.stCode, linewidth=0.5)
        self.axes.legend(loc='best')
        x=list(index_x[::len(index_x) // 4-1])
        self.axes.set_xticks(x)
        date=index_date[::len(index_date)//4-1]
        self.axes.set_xticklabels(date, rotation=15)
        st_x=self.st_x
        st_y=self.st_y
        index_x=self.index_x
        index_y=self.index_y
        if type=='迴歸線':
            regr_st = LinearRegression().fit(st_x, st_y)
            regr_index = LinearRegression().fit(index_x, index_y)
            self.axes.plot(st_x, regr_st.predict(st_x),color='blue', linestyle='-.',label='股票迴歸', linewidth=1)


            self.axes.plot(index_x, regr_index.predict(index_x), color='red',
                           linestyle='-.', label='指數迴歸', linewidth=1)
            self.axes.legend(loc='best')
        else:
            self.axes.plot([st_x[0],st_x[-1]],[st_y[0],st_y[-1]],color='blue', linestyle='-.',label='股票連線', linewidth=1)
            self.axes.plot([index_x[0], index_x[-1]], [index_y[0], index_y[-1]], color='red', linestyle='-.', label='指數連線',
                           linewidth=1)
            self.axes.legend(loc='best')

UI界面控件:

UI界面的設計使用的是Python的pyqt5並藉助了QT designer工具來輔助設置UI界面。

Pyqt5的內容很多,這裏我們只介紹我們所用到的幾個重要控件的常用方法,如果想了解更多pyqt5的內容,可以去網絡上查找相關的介紹,CSDN中有很多介紹Pyqt5的文章。

QLabel

QLabel是標籤,對應的是展示效果圖中的“角度範圍”、“時間範圍“等文字信息。

QLabel中部分常用的方法是:

text() 獲得Qlabel的文本內容
setText() 設置Qlabel的文本內容
setGeometry() 設置Qlabel在窗口中的位置和大小
setFont() 設置Qlabel中文本的字體
setPixmap() 在QLabel中填充圖片

QLineEdit

QLineEdit是單行文本輸入框,即效果圖中輸入角度和月份數的三個框框。

text() 獲得QLineEdit中的文本內容
setGeometry() 設置QLineEdit在窗口中的位置和大小
setGeometry() 設置Qlabel在窗口中的位置和大小

QPushButton

QPushButton是按鈕,即效果圖中的”選擇目標股票表格“和”開始篩選“兩個按鈕。

setText() 設置按鈕上的文本
setStyleSheet() 設置按鈕的樣式,如背景顏色
setGeometry() 設置Qlabel在窗口中的位置和大小

QComboBox

QComboBox是下拉框,即效果中的選擇(迴歸線/兩點連線/二者任一)的控件。

addItem() 添加選項
currentText() 獲取選中的文本
setGeometry() 設置Qlabel在窗口中的位置和大小

QTableWidget

QTableWidget是表格,即效果圖中展示篩選結果的表格。

setColumnCount() 設置列數
setRowCount() 設置行數
setHorizontalHeaderItem() 設置表頭(一次設置一個)
setHorizontalHeaderLabels() 設置表頭(參數爲列表,一次多個)
tableWidget.setItem() 設置單元格內容
tableWidget.insertRow() 插入一行單元格
tableWidget.setSpan() 合併單元格
setGeometry() 設置Qlabel在窗口中的位置和大小

信號與槽函數

上一個板塊爲UI添加控件,但設計出來的只是靜態的窗口。點擊按鈕、選擇下拉框都沒有任何反應。如果我們希望點擊按鈕或者做其他事情時我們的程序可以執行某些功能。就需要利用信號與槽函數。Pyqt5的控件幾乎都支持綁定信號和槽函數。信號可以理解爲一個事件,比如單擊\雙擊按鈕、單擊表格的文本框、改變下拉選擇框當前的選項等等。槽函數就是與這些信號綁定的函數。當信號發生時,會自動執行與其綁定的槽函數。

下面給出該程序的槽函數與信號:

選擇目標股票表格

有時候,我們並不想篩選全部的A股,而是隻關注特定的若干只股票,有目的性地篩選。
單擊”選擇模板股票表格“按鈕,可彈出一個文件選擇框。選擇含有存儲目標股票代碼的excel文件。
在這裏插入圖片描述

圖片

該功能的槽函數如下:

def set_target_stock(self):
    try:
        filechoose = QFileDialog.getOpenFileName(self);
        filechoosed = filechoose[0]
        self.target_stock=list(pd.read_excel(filechoosed)['ts_code'])
        self.main_ui.label_7.setText(filechoosed)#更改窗口中的目標股票提示內容
    except:
        print(traceback.format_exc())

觸發該槽函數的信號爲單擊該按鈕:

self.main_ui.btn_set_target_stock.clicked.connect(self.set_target_stock)#btn_set_target_stock就是按鈕控件

開始篩選

單擊”開始篩選“按鈕,執行程序。

該函數調用了一個自定義的子線程,該子線程執行篩選操作,其具體的內容會在後面介紹。

def run(self):
    try:
        if self.main_ui.btn_run.text()=='開始篩選':#開始執行
            self.main_ui.btn_run.setText('正在篩選')
            self.main_ui.btn_run.setStyleSheet("background-color: rgb(255, 0, 0);")
            type=self.main_ui.comboBox.currentText()#下拉框的選項
            angle1=int(self.main_ui.angle_range1.text())
            angle2=int(self.main_ui.angle_range2.text())
            months=int(self.main_ui.time_range.text())
            #在窗口中輸入的三個變量,angle_rang1等三個空間都是QLineEdit
            self.thread = thread_run(self.target_stock,angle1,angle2,months,type)
            #這裏創建子線程,並傳入參數
            self.thread._signal.connect(self.update)
            #設置子線程的信號與槽函數,子線程返回篩選結果時會執行update函數.
            self.thread.start()
            #開始執行
          
        else:#停止執行
            self.thread.terminate()#關閉子線程
            self.thread.wait()
            self.main_ui.tableWidget.insertRow(0)
            self.main_ui.tableWidget.setSpan(0, 0, 1, 4)
            redBrush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
            item = QtWidgets.QTableWidgetItem(
                str(datetime.datetime.now().strftime("%H:%M:%S")+':暫停篩選'))
            self.main_ui.tableWidget.setItem(0, 0, item)
            self.main_ui.tableWidget.item(0, 0).setForeground(redBrush)
            #在表格中插入一行,並寫入提示信息。
            self.main_ui.btn_run.setText('開始篩選')
            self.main_ui.btn_run.setStyleSheet("background-color: rgb(255, 255, 255);")
    except:
        print(traceback.format_exc())

信號:

self.main_ui.btn_run.clicked.connect(self.run)

展示篩選結果

子線程會將滿足條件的股票的代碼和角度發送到槽函數update中。

def update(self, msg):#用於更新表格
#msg返回就是線程返回的內容
    if msg=='finish':
        self.main_ui.tableWidget.insertRow(0)  # 插入一行
        self.main_ui.tableWidget.setSpan(0, 0, 1, 4)
        redBrush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        item = QtWidgets.QTableWidgetItem(
            str(datetime.datetime.now().strftime("%H:%M:%S") + ':篩選完成'))
        self.main_ui.tableWidget.setItem(0, 0, item)
        self.main_ui.tableWidget.item(0, 0).setForeground(redBrush)
        self.main_ui.btn_run.setText('開始篩選')
        self.main_ui.btn_run.setStyleSheet("background-color: rgb(255, 255, 255);")
        return
    try:
        st_code=msg.split(',')[0]
        angle0=msg.split(',')[1]#迴歸線角度
        angle1=msg.split(',')[2]#兩點連線角度
        st_code=st_code[-2:].lower()+\
                st_code[:6]
        
        url='http://hq.sinajs.cn/list='+st_code
        data=requests.get(url).text
        data=data.split('"')
        data=data[1].split(',')
        #從新浪財經獲取股票名稱、今開、昨收
        self.main_ui.tableWidget.insertRow(0)#插入一行
        item0 = QtWidgets.QTableWidgetItem(msg.split(',')[0])
        item1 = QtWidgets.QTableWidgetItem(data[0])
        item2 = QtWidgets.QTableWidgetItem(data[1])
        item3 = QtWidgets.QTableWidgetItem(data[2])
        item4 = QtWidgets.QTableWidgetItem(angle0)
        item5 = QtWidgets.QTableWidgetItem(angle1)
        self.main_ui.tableWidget.setItem(0, 0, item0)
        self.main_ui.tableWidget.setItem(0, 1, item1)
        self.main_ui.tableWidget.setItem(0, 2, item2)
        self.main_ui.tableWidget.setItem(0, 3, item3)
        self.main_ui.tableWidget.setItem(0, 4, item4)
        self.main_ui.tableWidget.setItem(0, 5, item5)
        #填入表格
    except:
        print(traceback.format_exc())

該槽函數與信號的綁定是在上面的 run()函數中設置的:

self.thread._signal.connect(self.update)

雙擊單元格打開詳細信息窗口

雙擊篩選結果中表格的某一列,可以彈出新窗口,新窗口中可以展示一支股票更多的信息,比如K線圖和超級疊加圖。

槽函數:

def more_Information(self,x,y):#x和y是信號自動傳送的值,是被雙擊的單元格的位置
    try:
        stCode = self.main_ui.tableWidget.item(x, 0).text()
        stName=self.main_ui.tableWidget.item(x,1).text()
        angle=(self.main_ui.tableWidget.item(x,4).text(),self.main_ui.tableWidget.item(x,5).text())
        type=self.main_ui.comboBox.currentText()
       #被雙擊的單元格對應的股票的信息獲取參數
       
        window = moreInfWindow(stCode,stName,int(self.main_ui.time_range.text()),angle,type=type)
        window.show()
        window.exec_()
        #打開新窗口,這裏的moreInfWindow是一個新的窗口,具體參見源代碼。
    except:
        print(traceback.format_exc())

信號:

self.main_ui.tableWidget.cellDoubleClicked.connect(self.more_Information)

更換展示的K線圖和超級疊加圖

圖片

如上圖,詳細窗口會展示股票的一些數據同時展示該股吧的K線圖和與指數的超級疊加圖,以及對應的迴歸線或者兩點連線。

可以選擇想要展示的K線圖的時間範圍和疊加圖中的直線類型(迴歸線或者兩點連線)。

K線圖槽函數:

def Kpic(self):
    codest={'SH':'1','SZ':'0'}
    Ksize = {'1年':'-3','6月':'-4','3月':'-6','1月':'-8'}
    size=Ksize[self.ui.comboBox.currentText()[:2]]
    #獲取圖片
    stimgUrl='http://webquoteklinepic.eastmoney.com/GetPic.aspx?nid='+codest[self.stCode[-2:]]+'.'+self.stCode[:-3]+'&UnitWidth={}&imageType=KXL&EF=&Formula=RSI&AT=0'.format(size)
    # print(cbimgUrl)
    print(stimgUrl)
    try:
        response = requests.get(stimgUrl)
        wb_data = response.content
        with open('stimg.png', 'wb') as f:
            f.write(wb_data)
        pix = QPixmap('stimg.png')
        self.ui.imagelb.setPixmap(pix)#在標籤中填充圖片
    except:
        pass

K線圖信號:

self.ui.comboBox.currentIndexChanged.connect(self.Kpic)

疊加圖槽函數:(這裏用到了畫布,m是一個畫布,會在後面介紹)

def Lpic(self):
    self.m.axes.cla()#清空重畫
    self.m.drawLine(self.ui.comboBox_1.currentText())
    self.m.draw()

疊加圖信號:

self.ui.comboBox_1.currentIndexChanged.connect(self.Lpic)

雙擊打開東方財富股票行情頁面

若想要了解更多關於某隻股票的信息,可以雙擊詳細信息窗口中的表格,會打開東方財富相應股票的頁面。

圖片

槽函數:

def openUrl(self):
    url="http://quote.eastmoney.com/{}.html".format(self.stCode[-2:].lower()+ self.stCode[0:6])
    QtGui.QDesktopServices.openUrl(QtCore.QUrl(url))

信號:

self.ui.tableWidget.cellDoubleClicked.connect(self.openUrl)

子線程

篩選的任務是在子線程中完成的。因爲如果不使用子線程,在篩選完成之前我們的程序不能做任何操作,也無法暫停。所以我們需要調用子線程,由主線程控制子線程的執行和停止。

主線程的對應函數以及在上面給出,也就是run()函數。這裏再貼一下:

def run(self):
    try:
        if self.main_ui.btn_run.text()=='開始篩選':#開始執行
            self.main_ui.btn_run.setText('正在篩選')
            self.main_ui.btn_run.setStyleSheet("background-color: rgb(255, 0, 0);")
            type=self.main_ui.comboBox.currentText()#下拉框的選項
            angle1=int(self.main_ui.angle_range1.text())
            angle2=int(self.main_ui.angle_range2.text())
            months=int(self.main_ui.time_range.text())
            #在窗口中輸入的三個變量,angle_rang1等三個空間都是QLineEdit          
            
            self.thread = thread_run(self.target_stock,angle1,angle2,months,type)
            #這裏創建子線程,並傳入參數
            self.thread._signal.connect(self.update)
            #設置子線程的信號與槽函數,子線程返回篩選結果時會執行update函數.
            self.thread.start()
            #開始執行,會自動執行thread中的run()
        else:#停止執行
            self.thread.terminate()#關閉子線程
            self.thread.wait()
            
            self.main_ui.tableWidget.insertRow(0)
            self.main_ui.tableWidget.setSpan(0, 0, 1, 4)
            redBrush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
            item = QtWidgets.QTableWidgetItem(
                str(datetime.datetime.now().strftime("%H:%M:%S")+':暫停篩選'))
            self.main_ui.tableWidget.setItem(0, 0, item)
            self.main_ui.tableWidget.item(0, 0).setForeground(redBrush)
            #在表格中插入一行,並寫入提示信息。
            self.main_ui.btn_run.setText('開始篩選')
            self.main_ui.btn_run.setStyleSheet("background-color: rgb(255, 255, 255);")
    except:
        print(traceback.format_exc())

現在給出子線程類:

class thread_run(clac,QtCore.QThread):#clac是自定義的計算角度的類,會在後面介紹。
  
    _signal = pyqtSignal(str)

    def __init__(self,st_list,angle1,angle2,months,type):
        self.st_list=st_list
        if self.st_list==None:#如果沒有設置目標股票,就默認篩選全部A股
            pro=ts.pro_api('你自己的tushare的token')
            self.st_list = list(pro.stock_basic(exchange='', list_status='L', fields='ts_code')['ts_code'])
        self.months=months
        self.angle1=angle1
        self.angle2=angle2
        self.type=type
        QtCore.QThread.__init__(self)
        clac.__init__(self)

    def run(self):
            for st in self.st_list:
                try:
                    angle=self.angle(st,self.months)#clac類的方法被繼承過來。angle是一個元組,第一個值是迴歸線角度,第二個是兩點連線角度
                    print(angle)
                    if  angle[0]>=self.angle1 and angle[0] <=self.angle2 and (self.type=='迴歸線' or self.type=='兩者任一'):
                        self._signal.emit(st+','+str(round(angle[0],4))+','+str(round(angle[1],4)))#篩選出一個滿足條件的股票,將其數據作爲信號發送給update函數
                        time.sleep(0.5)
                        continue

                    if  angle[1]>=self.angle1 and angle[1] <=self.angle2 and (self.type=='兩點連線' or self.type=='兩者任一'):
                        self._signal.emit(st+','+str(round(angle[0],4))+','+str(round(angle[1],4)))
                        time.sleep(0.5)
                        continue
                except:
                    print(traceback.format_exc())
            self._signal.emit('finish')#全部的股票篩選完畢

待完善的工作

1.通過歷史數據回測,得到結果最好的角度範圍
2.第三種角度的計算方法:找在某一時間段內的最大角度。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章