一、雙均線概念
均線:對於每一個交易日,都可以計算出前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)
執行顯示如下: