用stockquant實現股票量化交易


視頻地址 https://www.bilibili.com/video/BV1zK411u7uG?p=2&spm_id_from=pageDriver

1、StockQuant的安裝


1)macbook的安裝

pip install stockquant-simple  # 依賴庫比較少,圖形界面取消
conda install -c conda-forge ta-lib  # 安裝ta-lib庫

2)windows下的安裝

2.1 python的安裝

用anaconda安裝,到清華鏡像站,選擇anaconda3-5.3.1-windows-x86_64.exe 2018-11-20 . python版本3.7.0。安裝的時候把環境變量還有默認解釋器的設置需要打勾的地方都選中。

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple  # 永久配置國內鏡像源。執行后,會寫入pip.ini文件,把清華pipy網站寫入,下次pip會自動從中下載。
pip install stockquant
pip install Ta-Lib  # 失敗,解決見下面下載安裝文件后安裝

python各種版本包下載地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/,到這里下載cp37對應的Ta-Lib 64位版本,文件名后綴是whl。下載好后,進入文件所在的目錄,按住shift,點擊鼠標右鍵打開powershell,pip install 文件名.whl (可以輸入文件名第一個字母后,按tab鍵補全)

2.2 測試

進入python命令行

from stockquant.quant import *  # 如果遇到和pandas,numpy有關的報錯信息 pip install -U pandas

如果沒有錯誤就ok了。

3) 新建項目(pycharm下)

新建工程,解釋器不選擇虛擬環境,選已經存在的解釋器,選項中system interpreterr(中文是系統解釋器)

然后,新建文件config.json,內容如下:

{
  "LOG": {
    "level": "debug",
    "handler": "stream"
  },
  "DINGTALK": "your dingding token",  #釘釘token
  "TUSHARE": "your tushare token",
  "SENDMAIL": {
    "from": "5186817@qq.com",
    "password": "your qq email authorization code",  # 郵箱授權碼
    "to": "5186817@qq.com",
    "server": "smtp.qq.com",
    "port": 587
  }
}

釘釘token設置步驟:

點擊和搜索框同行的加號按鈕--發起群聊--培訓群--輸入群名稱--點擊創建,進入群界面,點擊群設置--智能群助手--添加機器人--選擇自定義--點擊添加--輸入機器人名字--安全設置選擇自定義關鍵詞(比如設置關鍵詞為“交易,”發的消息必須包含“交易”才能推送),點擊完成,下一窗口出現webhook,復制后填入配置文件中,點擊完成。

tushare設置:

登陸tushare網站,移動鼠標到頭像,點擊個人主頁,再點擊接口token標簽,復制填入即可。

新建python文件,比如main.py

from stockquant.quant import *

config.loads('config.json')

2、獲取股票數據

stockquant說明文檔參加 https://gitee.com/usomien/StockQuant

1)獲取實時行情信息

tick = Market.tick(symbol='sh600519')  # "sh601003",或者"sz002307",前者是滬市,后者是深市。返回的tick對象有很多屬性,開盤價,收盤價,買一到買五等等。
print(tick.symbol, tick.last, tick.timestamp) # 股票代碼,當前價格,時間戳

2)獲取k線數據

kline = Market.kline(symbol, timeframe, adj=None, start_date=None, end_date=None)
# timeframe:k線周期,區分大小寫,支持:"5m", "15m", "30m", "1h", "1d", "1w", "1M,分別為5、15、30分鍾、1小時、1天、1周、1月
#adj:復權類型,默認是"3"不復權;前復權:"2";后復權:"1"。已支持分鍾線、日線、周線、月線前后復權。 BaoStock提供的是漲跌幅復權算法復權因子
# start_date:開始日期(包含),格式“YYYY-MM-DD”,為空時取2015-01-01;
# end_date:結束日期(包含),格式“YYYY-MM-DD”,為空時取最近一個交易日;
# return:返回一個列表,其中每一條k線數據包含在一個列表中,不同周期返回列表不同。比如日線返回 :日期,開,高,低,收,昨收,成交量,成交額,“3”,換手,“1”,漲跌幅,“0”。
kline = Market.kline('sh600519', '5m')  # 例子

3)上證綜指與深圳成指

Market.shanghai_component_index()  # 返回字典,包括當前點數,漲跌點數,漲跌率,成交數量,成交金額
Market.shenzhen_component_index()

4) 其它

Market.stocks_list()  # 股票列表,獲取基礎信息數據,包括股票代碼、名稱、區域、行業、上市日期等
Market.today_is_open()  # 今日是否開盤,返回bool值
Market.new_stock()  # 獲取新股上市列表

3、交易實時提醒

content = "### 交易:孫文平\n\n"\
    "> **股票名稱** {symbol}\n\n"\
    "> **當前價格** {last}\n\n".format(
    symbol=tick.symbol,
    last=tick.last
)
DingTalk.markdown(content)  # markdown格式
DingTalk.text(“交易:。。。。”)
sendmail("交易提醒:sh600519的價格已達到2000美元!")  # 微信郵件提醒:我-設置-通用-輔助功能-qq郵箱提醒-啟用

提醒可以加入到循環當中,延時是必須的,防止提醒太頻繁,循環體中可以加入判斷,比如今日是否開盤,股價大於多少提醒等。

程序運行可以用命令行,python 程序名.py。

4、計算指標並批量篩選股票

from stockquant.quant import *

config.loads('config.json')
stocks_pool = []                    # 篩選到的股票加入此列表,股票池
stocks_list = Market.stocks_list()  # 獲取所有股票信息,返回DataFrame格式,ts_code列格式為,000001.SZ,600001.SZ
for item in stocks_list['ts_code']:
    if str(item).endswith('.SH'):
        name = 'sh' + str(item).replace('.SH', '')  # 000001.SH, 轉換為sh000001格式
    else:
        name = 'sz' + str(item).replace('.SZ', '')
    kline = Market.kline(name, '1d')
    if len(kline) < 30:
        continue
    ma20 = MA(20, kline)
    ma30 = MA(30, kline)
    if ma20[-1] > ma30[-1] and ma20[-2] <= ma30[-2]:  # 收盤后下載的k線包括當天的,[-1]是當天的ma值。
        stocks_pool.append(name)
print(stocks_pool)

5、編寫一個策略自動推送信號

from stockquant.quant import *

class Strategy:

    def __init__(self):
        config.loads('config.json')
        self.amount, self.price = 0, 0  # 持股數量,持股價格 都設置為o

    def begin(self):
        if get_localtime()[11: 16] == '16:00' and Market.today_is_open():  # 如果今天開盤,並且是下午4點
            kline = Market.kline('sh600519', '1d')
            ma20, ma30 = MA(20, kline), MA(30, kline)
            cross_over = ma20[-1] > ma30[-1] and ma20[-2] <= ma30[-2]  # 金叉信號
            cross_down = ma20[-1] < ma30[-1] and ma20[-2] >= ma30[-2]  # 死叉信號
            if cross_over:
                DingTalk.text("交易提醒: sh600519 金叉信號出現,請買入!")
                self.amount, self.price = 1, float(kline[-1][4])  # 第五個是收盤價
            elif cross_down and self.amount != 0:
                DingTalk.text("交易提醒: sh600519 死叉信號出現,請賣出!")
                self.amount, self.price = 0, 0
        if not not_open_time() and self.amount > 0:
            tick = Market.tick('sh600519')
            last = tick.last  # 在交易時間段內獲取一下當前的最新價格
            if last <= self.price * 0.9:  # 止損價10%
                DingTalk.text("交易提醒: sh600519 當前價格已經達到止損位,請立即止損!")
                self.amount, self.price = 0, 0

if __name__ == '__main__':
    strategy = Strategy()
    while True:
        try:                    # 為防止出錯后停止運行
            strategy.begin()
            sleep(3)            # 延時3秒
        except Exception as e:
            logger.error(e)

6、雙均線策略回測

from stockquant.quant import *

class Strategy:

    def __init__(self):
        config.loads('config.json')
        self.asset = 10000                      # 總資金
        self.backtest = BackTest()               # 初始化回測模塊
        
        # data = Market.kline(symbol='sh600519', timeframe='1d')     # 從網站獲得k線數據
        res = pd.read_csv('600519.csv')  # 從文件讀取數據,文件每行格式的前幾項,日期,開,高,低,收,昨收,成交量,成交額
        data = res.values.tolist()
        
        self.kline = []                         # 存放遞增k線數據的列表
        for k in data:
            self.kline.append(k)
            self.backtest.initialize(self.kline, data)  # k線入口函數,傳入遞增k線和原始k線
            self.begin()
        plot_asset()    # 回測完成繪制資金曲線圖(出錯的話,到pycharm設置--工具--python scientific--在工具窗口中顯示繪圖對勾去掉)

    def begin(self):                            # 策略主體函數
        if CurrentBar(self.kline) < 30:
            return
        # if self.backtest.timestamp < '2015-01-01' or self.backtest.timestamp > '2018-01-01':
        #     return		# 回測自定義時間段
        ma20, ma30 = MA(20, self.kline), MA(30, self.kline)
        cross_over = ma20[-1] > ma30[-1] and ma20[-2] <= ma30[-2]  # 金叉信號
        cross_down = ma20[-1] < ma30[-1] and ma20[-2] >= ma30[-2]  # 死叉信號

        if cross_over and self.backtest.long_quantity == 0:  # 金叉且未持股
            self.backtest.buy(                              # 模擬買入
                price=self.backtest.close,                  # 買入價格為此根k線收盤價
                amount=self.asset/self.backtest.close,      # 買入數量
                long_quantity=self.asset/self.backtest.close,   # 當前持多數量
                long_avg_price=self.backtest.close,             # 當前持多價格
                profit=0,                                       # 利潤
                asset=self.asset                                # 總資金
            )
        elif cross_down and self.backtest.long_quantity != 0:    # 如果死叉且當前持股
            profit = (self.backtest.close-self.backtest.long_avg_price) * self.backtest.long_quantity
            self.asset += profit                                # 計算變化后的總資金,
            self.backtest.sell(                                 # 模擬賣出
                price=self.backtest.close,
                amount=self.backtest.long_quantity,
                long_quantity=0,
                long_avg_price=0,
                profit=profit,
                asset=self.asset
            )
        if self.backtest.long_quantity > 0 and self.backtest.low <= self.backtest.long_avg_price * 0.9: # 止損
            profit = (self.backtest.low-self.backtest.long_avg_price) * self.backtest.long_quantity
            self.asset += profit
            self.backtest.sell(
                price=self.backtest.long_avg_price * 0.9,
                amount=self.backtest.long_quantity,
                long_quantity=0,
                long_avg_price=0,
                profit=profit,
                asset=self.asset
            )

if __name__ == '__main__':
    strategy = Strategy()

問題:回測結果是否出錯,最大回撤0%?(源碼中回撤是與初始資金進行比較,而不是與收入的高點比較)可以看程序運行時生成的回撤.csv文件分析。

7、歷史k線及策略信號可視化

plot_signal(data, self.buy_signal, self.sell_signal, self.ma20, self.ma30)
# 模塊中沒有此方法,可能是收費模塊里面才有

8、ETF網格策略設計

確定一個基准線,價格會圍繞它上下波動。在基准線的價格上買入50%倉位,在價格高於基准線價格時賣出,低於它的時候買入。基准線上下可以設置幾個格子,比如資金少的,可以設置兩格,即高於基准線+每格表示的數量,賣出使倉位25%,高於基准線+2倍格子,賣出使倉位0%。這個策略非常適合震盪或者通道形態。

from stockquant.quant import *

class EtfGridStrategy:

    def __init__(self):
        config.loads('config.json')
        self._symbol = 'sh512980'
        self._asset = 10000             # 總資金
        self._amount = 0                # 初始倉位
        self.first_buy_day = None       #買入的日期,買入的日期是不能賣出的
        logger.info("EtfGridStrategy start to run ... Current price is {}",format(Market.tick(self._symbol).last))

        while True:
            try:
                self.do_action()
                sleep(5)
            except:
                logger.error()

    def do_action(self):

        if not not_open_time() and Market.today_is_open():

            price = Market.tick(self._symbol).last
            logger.info("Current price is {}".format(price))

            if self._amount == 0 and 3450 <= price <= 3550:     # 基准線是3500
                first_buy_amount = round(self._asset / 2 / price / 100)
                DingTalk.text("交易提醒:請買入底倉!買入價格:{} 買入數量:{}".format(price, first_buy_amount))
                self._amount = first_buy_amount
                self._asset = first_buy_amount * price
                self.first_buy_day = get_date()

            if self._amount > 0:

                if price <= 3250:
                    buy_amount = round(self._asset / 2 / price / 100)
                    DingTalk.text("交易提醒:請增持倉位!買入價格:{} 買入數量:{}".format(price, buy_amount))
                    self._amount += buy_amount
                    self._asset -= buy_amount * price

                elif price <= 3000:
                    buy_amount = round(self._asset / price / 100)
                    DingTalk.text("交易提醒:請增持倉位!買入價格:{} 買入數量:{}".format(price, buy_amount))
                    self._amount += buy_amount
                    self._asset -= buy_amount * price

                if get_date() != self.first_buy_day:

                    if price >= 3750:
                        sell_amount = round(self._amount / 2)
                        DingTalk.text("交易提醒:請減持倉位!賣出價格:{} 賣出數量:{}".format(price, sell_amount))
                        self._amount -= sell_amount
                        self._asset += sell_amount * price

                    elif price >= 4000:
                        sell_amount = round(self._amount)
                        DingTalk.text("交易提醒:請減持倉位!賣出價格:{} 賣出數量:{}".format(price, sell_amount))
                        self._amount -= sell_amount
                        self._asset += sell_amount * price

if __name__ == '__main__':
    EtfGridStrategy()

9、自動化交易

模塊easytrader 參見 https://gitee.com/alexi126/easytrader/blob/master/README.md

https://easytrader.readthedocs.io/zh/master/

下載華泰證券網上交易系統專業版2(官網只有繁體版,到網上搜索)。默認安裝到C:\htzqzyb2,文件夾中有xiadan.exe。

交易界面設置:進入交易界面,點擊 系統-系統設置-交易設置-默認買入賣出價格都設置為空-賣出后查詢股票設置為是-界面設置-下單窗口總在最前面設置為是-界面不操作超時時間設為 0, 客戶端不能最小化也不能處於精簡模式。

import easytrader     

user = easytrader.use('ht_client')   # 設置交易客戶端類型,華泰證券網上交易系統專業版2,不用手動提前登錄交易系統
user.prepare(user='0106********', password='166***', comm_password='12****')  # 啟動並連接客戶端
print(user.balance)  # 獲取資金情況
print(user.position)  # 獲取持倉
user.buy('162411', price=0.55, amount=100)  # 價格0.55,數量100股
user.sell('162411', price=0.55, amount=100)
print(user.today_trades)  # 查詢當日成交
print(user.today_entrusts)  # 查詢當日委托

用stockquant 模塊實現交易(內部封裝了easytrader模塊)(需要先登錄運行交易系統)

from stockquant.quant import *

class Strategy:

    def __init__(self):
        self.trade = Trade(config_file='config.json', symbol='sh512980', path=r'C:\htzqzyb2\xiadan.exe')
        self.do_action()  

    def do_action(self):
        price = Market.tick('sh512980').ask1_price  # 可以把買入賣出語句加入到“5、編寫一個策略”中,實盤交易
        success, error = self.trade.buy(price, 100)
        if error:
            DingTalk.text("交易提醒:失敗{}".format(error))
            return
        logger.info("success:{}".format(success))

if __name__ == '__main__':
    Strategy()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM