量化交易——雙均線策略(金叉死叉)


一、雙均線概念

  均線:對於每一個交易日,都可以計算出前N天的移動平均值,然后把這些平均值連起來,成為一條線,就叫做N日移動平均線。移動平均線常用線有5日、10日、30日、60日、120日的指標。

    5日和10日的是短線操作參照指標,稱作均線指標;

    30日和60日的是中期均線指標,稱作均線指標;

    120日和240日的是長期均線指標,稱作均線指標。

  金叉:短期均線上穿長期均線,買入信號。

  死叉:短期均線下穿長期均線,賣出信號。

  交易策略:金叉買入,死叉賣出

二、金叉死叉交易練習

1、使用tushare包獲取某股票的歷史行情

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt    # Python繪圖和數據可視化的工具包
import tushare as ts


ts.get_k_data("601318", start="2004-01-30").to_csv('601318.csv')
df = pd.read_csv('601318.csv', index_col="date", parse_dates=['date'])[['open', 'close', 'high', 'low']]
print(df)
"""
              open   close    high     low
date                                      
2007-03-01  21.254  19.890  21.666  19.469
2007-03-02  19.979  19.728  20.166  19.503
...            ...     ...     ...     ...
2020-01-22  85.000  85.220  85.480  83.830
2020-01-23  84.010  83.490  84.560  82.480

[3076 rows x 4 columns]
"""

2、使用pandas包計算該股票歷史數據的5日和30均線

  5日均線則前4天沒有均線,30日均線則前29天沒有均線。

(1)普通方法

df['ma5'] = np.nan     # 創建5日均值列
df['ma30'] = np.nan    # 創建30日均值列
print(df)
"""
              open   close    high     low  ma5  ma30
date                                                 
2007-03-01  21.254  19.890  21.666  19.469  NaN   NaN
2007-03-02  19.979  19.728  20.166  19.503  NaN   NaN
...            ...     ...     ...     ...  ...   ...
2020-01-22  85.000  85.220  85.480  83.830  NaN   NaN
2020-01-23  84.010  83.490  84.560  82.480  NaN   NaN
"""
for i in range(4, len(df)):    # 從第五個到最后一個
    # 當天和前四天的收盤價的平均值為5日均線值
    df.loc[df.index[i], 'ma5'] = df["close"][i-4:i+1].mean()   # i=4是第五天,[0,5]
print(df)
"""
              open   close    high     low      ma5  ma30
date                                                     
2007-03-01  21.254  19.890  21.666  19.469      NaN   NaN
2007-03-02  19.979  19.728  20.166  19.503      NaN   NaN
2007-03-05  19.545  18.865  19.626  18.504      NaN   NaN
2007-03-06  18.704  19.235  19.554  18.597      NaN   NaN
2007-03-07  19.252  19.758  19.936  19.090  19.4952   NaN
...            ...     ...     ...     ...      ...   ...
2020-01-23  84.010  83.490  84.560  82.480  85.6320   NaN
"""

for i in range(29, len(df)):    # 從第30個到最后一個
    # 當天和前29天的收盤價的平均值為30日均線值
    df.loc[df.index[i], 'ma30'] = df["close"][i-29:i+1].mean()
print(df)
"""
              open   close    high     low      ma5       ma30
date                                                          
2007-03-01  21.254  19.890  21.666  19.469      NaN        NaN
2007-03-02  19.979  19.728  20.166  19.503      NaN        NaN
2007-03-05  19.545  18.865  19.626  18.504      NaN        NaN
2007-03-06  18.704  19.235  19.554  18.597      NaN        NaN
2007-03-07  19.252  19.758  19.936  19.090  19.4952        NaN
...            ...     ...     ...     ...      ...        ...
2020-01-17  86.150  86.250  86.900  85.850  86.1780  85.292333
2020-01-20  88.300  87.600  88.700  87.350  86.4080  85.387667
2020-01-21  87.000  85.600  87.290  85.600  86.1620  85.452667
2020-01-22  85.000  85.220  85.480  83.830  86.0440  85.507000
2020-01-23  84.010  83.490  84.560  82.480  85.6320  85.488667
"""

(2)簡單方法

df['ma5'] = df['close'].rolling(5).mean()
df['ma30'] = df['close'].rolling(30).mean()
print(df)
"""
              open   close    high     low      ma5       ma30
date                                                          
2007-03-01  21.254  19.890  21.666  19.469      NaN        NaN
2007-03-02  19.979  19.728  20.166  19.503      NaN        NaN
2007-03-05  19.545  18.865  19.626  18.504      NaN        NaN
2007-03-06  18.704  19.235  19.554  18.597      NaN        NaN
2007-03-07  19.252  19.758  19.936  19.090  19.4952        NaN
...            ...     ...     ...     ...      ...        ...
2020-01-17  86.150  86.250  86.900  85.850  86.1780  85.292333
2020-01-20  88.300  87.600  88.700  87.350  86.4080  85.387667
2020-01-21  87.000  85.600  87.290  85.600  86.1620  85.452667
2020-01-22  85.000  85.220  85.480  83.830  86.0440  85.507000
2020-01-23  84.010  83.490  84.560  82.480  85.6320  85.488667
"""

3、使用matplotlib包可視化歷史數據的收盤價和兩條均線

df[['close', 'ma5', 'ma30']].plot()
plt.show()

  運行后顯示效果:

  

4、分析輸出所有金叉日期和死叉日期

  如果前一交易日五日均線小於30日均線,后一交易日五日均線大於30日均線,則說明是金叉;

  如果前一交易日五日均線大於30日均線,后一交易日五日均線小於30日均線,則說明是死叉。

(1)循環的解法

# dropna()刪除含有空數據的全部行,axis參數可刪除含義空數據的全部列
df = df.dropna()
gloden_cross = []    # 金叉
death_cross = []      # 死叉

for i in range(0, len(df)-1):
    if df['ma5'][i] >= df['ma30'][i] and df['ma5'][i-1] <= df['ma30'][i-1]:
        gloden_cross.append(df.index[i])
    if df['ma5'][i] <= df['ma30'][i] and df['ma5'][i-1] >= df['ma30'][i-1]:
        death_cross.append(df.index[i])

print('金叉日期', gloden_cross)
"""
金叉日期 [Timestamp('2007-06-14 00:00:00'), Timestamp('2007-12-10 00:00:00'),..., Timestamp('2020-01-02 00:00:00')]
"""
print('死叉日期', death_cross)
"""
死叉日期 [Timestamp('2007-06-04 00:00:00'), Timestamp('2007-11-06 00:00:00'),..., Timestamp('2019-12-23 00:00:00')]
"""

(2)簡便算法

# dropna()刪除含有空數據的全部行,axis參數可刪除含義空數據的全部列
df = df.dropna()

sr1 = df['ma5'] < df['ma30']
sr2 = df['ma5'] >= df['ma30']

death_cross = df[sr1 & sr2.shift(1)].index
golden_cross = df[-(sr1 | sr2.shift(1))].index

print('金叉日期', golden_cross)
"""
金叉日期 DatetimeIndex(['2007-04-12', '2007-06-14', '2007-12-10', '2008-04-23',..., '2020-01-02']
"""
print('死叉日期', death_cross)
"""
死叉日期 DatetimeIndex(['2007-06-04', '2007-11-06', '2007-12-13', '2008-05-20',..., '2019-11-12', '2019-12-23']
"""

5、使用該策略的炒股收益率

  如果我從2010年1月1日起,初始資金為100000元,金叉盡量買入,死叉全部賣出,則到今天為止,我的炒股收益率?

# 炒股收益率
first_money = 100000
money = first_money    # 持有的資金
hold = 0               # 持有的股票
sr1 = pd.Series(1, index=golden_cross)
sr2 = pd.Series(0, index=death_cross)

sr = sr1.append(sr2).sort_index()    # 將兩個表合並,並按時間排序
sr = sr['2010-01-01':]           # 從2010年1月1日開始

for i in range(0, len(sr)):
    p = df['open'][sr.index[i]]      # 當天的開盤價
    if sr.iloc[i] == 1:
        # 金叉
        buy = money // (100 * p)   # 買多少手
        hold += buy * 100
        money -= buy * 100 * p
    else:
        # 死叉
        money += hold * p
        hold = 0    # 持有股票重置為0

# 計算最后一天股票市值加上持有的資金
p = df['open'][-1]
now_money = hold * p + money
print('當前持有資產總額:', now_money)
print('盈虧情況:', now_money - first_money)
"""
當前持有資產總額: 551977.7999999997
盈虧情況: 451977.7999999997
"""

  需要注意的是:這里金叉死叉都是按照當天的收盤價計算的。但是如果得到當天的收盤價就已經無法進行交易了。因此要讓策略可行,需要按照當天的開盤價計算。

三、在JoinQuant(聚寬)平台實現雙均線策略

  金叉:若5日均線大於10日均線且不持倉
  死叉:若5日均線小於10日均線且持倉

1、策略實現

# 導入函數庫
from jqdata import *

# 初始化函數,設定基准等等
def initialize(context):
    # 設定滬深300作為基准
    set_benchmark('000300.XSHG')
    # 開啟動態復權模式(真實價格)
    set_option('use_real_price', True)
    # 股票類每筆交易時的手續費是:買入時佣金萬分之三,賣出時佣金萬分之三加千分之一印花稅, 每筆交易佣金最低扣5塊錢
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')

    g.security = ["601318.XSHG"]
    g.p1 = 5      # 五日均線
    g.p2 = 10     # 十日均線


def handle_data(context, data):
    # 遍歷股票
    for stock in g.security:
        df = attribute_history(stock, g.p2)
        # 十日均線的值
        ma10 = df['close'].mean()
        # 五日均線的值
        ma5 = df['close'][-5:].mean()
        
        if ma10 > ma5 and stock in context.portfolio.positions:   # 具有持倉
            # 死叉賣出
            order_target(stock, 0)
        if ma10 < ma5 and stock not in context.portfolio.positions:    # 不具有持倉
            # 金叉買入
            # order(stock, context.portfolio.available_cash)       # 能買多少買多少
            order(stock, context.portfolio.available_cash * 0.8)         # 可用資金的80%買入

 

  執行效果:

  

 

2、record函數——畫圖函數

  調用record函數可用來描畫額外的曲線。

  參數:一個或多個key=value形式的參數,key為曲線名稱,value為值。

  在雙均線策略中加入畫圖函數:

# 導入函數庫
from jqdata import *

# 初始化函數,設定基准等等
def initialize(context):
    # 設定滬深300作為基准
    set_benchmark('000300.XSHG')
    # 開啟動態復權模式(真實價格)
    set_option('use_real_price', True)
    # 股票類每筆交易時的手續費是:買入時佣金萬分之三,賣出時佣金萬分之三加千分之一印花稅, 每筆交易佣金最低扣5塊錢
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')

    g.security = ["601318.XSHG"]
    g.p1 = 5      # 五日均線
    g.p2 = 10     # 十日均線


def handle_data(context, data):
    # 遍歷股票
    for stock in g.security:
        df = attribute_history(stock, g.p2)
        # 十日均線的值
        ma10 = df['close'].mean()
        # 五日均線的值
        ma5 = df['close'][-5:].mean()
        
        if ma10 > ma5 and stock in context.portfolio.positions:   # 具有持倉
            # 死叉賣出
            order_target(stock, 0)
        if ma10 < ma5 and stock not in context.portfolio.positions:    # 不具有持倉
            # 金叉買入
            # order(stock, context.portfolio.available_cash)       # 能買多少買多少
            order(stock, context.portfolio.available_cash * 0.8)         # 可用資金的80%買入
    record(ma5=ma5, ma10=ma10)

  執行顯示如下:

  


免責聲明!

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



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