現在全球股市大熱,很多人都忍不住誘惑,跳進去買賣,如何『選股』、『擇時』,賺到錢就是一門重要而困難的課題,本文以最簡單的『移動平均法』(Moving Average)為例,說明如何以此操作策略,『擇時』買賣,並以回測(Back Testing)計算損益,驗證策略是否可行。這種以策略進行交易的模式,稱為『演算法交易』(Algorithmic Trading),當然,也意謂我們可以導入機器學習、深度學習或強化學習等人工智慧的各式演算法,進行更智慧型的投資,例如多家金控推出的機器人理財。
太多的文章介紹如何使用爬蟲,抓取股票歷史資料,筆者就不贅述了。直接抓取5年資料,進行回測(Back Testing)。
首先讀進最夯的台積電(2330)股票5年日股價資料,如下:
# 載入套件
import pandas as pd
# 讀日股價資料
df = pd.read_csv('2330.csv')
df['date'] = pd.to_datetime(df['date'])
df.head(10)
執行結果如下:
注意,我們會使用調整後股價(adjusted close),欄位名稱為adjclose,而不是使用收盤價,因為,股票會經過除權、除息或分割,調整後股價會反映真實的淨股價漲跌。
計算移動平均線,非常簡單,Pandas 直接提供 Rolling 函數實踐『移動視窗』,另 mean 函數計算平均數,組合起來就是移動平均,月線(約22個營業日) 程式如下:
# 載入套件
sma_short = pd.DataFrame()
sma_short['date'] = df['date']
sma_short['adjclose'] = df['adjclose'].rolling(window=22).mean()
sma_short.loc[15:30]
執行結果如下,前21筆為Null:
計算季線(約22個營業日)程式如下:
sma_long = pd.DataFrame()
sma_long['date'] = df['date']
sma_long['adjclose'] = df['adjclose'].rolling(window=66).mean()
sma_long.loc[90:110]
我們以短期移動平均線與長期移動平均線交叉點作為買賣點,以月線與季線為例:
先畫出兩條線
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10,6))
sns.lineplot(x='date', y='adjclose', data=sma_short)
sns.lineplot(x='date', y='adjclose', data=sma_long)
執行結果如下:
寫一個函數如下,找出買賣點,請仔細看程式註解:
import numpy as np
def buy_sell(df):
signal_buy = [] # 買點價格
signal_sell = [] # 賣點價格
flag=-1 # 買賣點旗標,短期超過長期為1,反之為0
# 掃描每一筆資料
for index, row in df.iterrows():
# 短期超過長期
if row[df.columns[1]] > row[df.columns[2]]:
if flag!=1: # 之前的短期未超過長期,即黃金交叉
signal_buy.append(row[df.columns[3]])
signal_sell.append(np.nan)
flag=1
else:
signal_buy.append(np.nan)
signal_sell.append(np.nan)
elif row[df.columns[1]] < row[df.columns[2]]:
if flag!=0: # 之前的長期未超過短期,即死亡交叉
signal_buy.append(np.nan)
signal_sell.append(row[df.columns[3]])
flag=0
else:
signal_buy.append(np.nan)
signal_sell.append(np.nan)
else:
signal_buy.append(np.nan)
signal_sell.append(np.nan)
return (signal_buy, signal_sell)
呼叫上述函數,得到買賣點:
signal_buy, signal_sell = buy_sell(df_new)
# 買點
df_buy = pd.DataFrame({'date': df['date'], 'signal_buy':signal_buy})
df_buy = df_buy[~np.isnan(signal_buy)]
df_buy
買點:
賣點:
# 賣點
df_sell = pd.DataFrame({'date': df['date'], 'signal_sell':signal_sell})
df_sell = df_sell[~np.isnan(signal_sell)]
df_sell
繪製所有資訊:
# 合併短期與長期移動平均線
df_new = sma_short.copy()
df_new = df_new.rename({'adjclose':'sma_short'}, axis=1)
df_new.insert(2, 'sma_long', sma_long['adjclose'])
df_new.insert(3, 'adjclose', df['adjclose'])
signal_buy, signal_sell = buy_sell(df_new)
# 繪圖
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize=(10,6))
sns.lineplot(x='date', y='adjclose', data=sma_short, color='g', label='短期趨勢')
sns.lineplot(x='date', y='adjclose', data=sma_long, color='b', label='長期趨勢')
plt.plot(df['date'], df['adjclose'], color='r', alpha=0.5, label='日線')
plt.scatter(df['date'], signal_buy, c='r', marker='^', s=150)
plt.scatter(df['date'], signal_sell, c='g', marker='^', s=150)
plt.legend()
執行結果如下:
這部份比較複雜,基本上在賣出時計算損益,但是,額外有兩點要考慮:
寫個函數計算損益:
# 計算損益(profit/loss)
def calc_profit(df_buy, df_sell, df):
df_profit = df_buy.merge(df_sell, on='date', how='outer')
df_profit.sort_values(by='date', inplace=True)
df_date = df.set_index('date')
balance=0
profit=0
cost=0
for index, row in df_profit.iterrows():
if not row['signal_buy'] is None:
balance+=1
cost+=df_date.loc[row['date'], 'adjclose']
elif not row['signal_sell'] is None:
if balance>0:
avg_cost = cost / balance
profit += df_date.loc[row['date'], 'adjclose'] - avg_cost
cost -= avg_cost
else:
profit += df_date.loc[row['date'], 'adjclose']
balance-=1
if balance>0:
profit += df_date.loc[row['date'], 'adjclose'] * balance - cost
elif balance<0:
profit += df_date.loc[row['date'], 'adjclose'] * balance
return profit
calc_profit(df_buy, df_sell, df)
** 以上假設一次只買賣一張,大賺 97 萬(972 x 1000)。 **
移動平均法其實有一個缺點,因為採用平均數,買點會比起漲點晚入場,賣點會比起跌點晚出場,如遇大跌,恐怕來不及出清,因此,加上停損點/停利點會更好,譬如,賺20%就賣,不等死亡交叉點才賣,避免大賠,停利點操作也是類似作法。
除了上述實驗外,還可以作其他測試:
10年前筆者任職財經資料庫公司時,曾設計一個『策略交易系統』,可提供使用者撰寫簡單程式碼,自訂策略,進行回測,並列印投資報告,只可惜當時未獲公司高層抬愛,無法順利上市銷售,10年後,該公司才推出相關的API,功能不及當初系統的一半,銷售狀況想當然耳,令人不勝噓唏。
除了移動平均法,也可使用其他技術指標,進行回測,甚至基本面也可以,包括月營收、盈餘、財務比率均可,或者搭配量能(Volume),都是常用的策略,每一個策略並不會一體適用於所有公司,因此,如何找到適合的策略套用在特定的公司,就夠大家研究一陣子了。如果再將AI導入,那就夠精彩了。
本篇程式及測試資料收錄在『這裡』。
請問調整後股價(adjusted close)是要自行計算嗎?還是可以用爬蟲抓到呢?
因為在證券交易網站找不到。謝謝!
Yafoo finance API 有提供。可使用yahoo_fin套件
http://theautomatic.net/2018/01/25/coding-yahoo_fin-package/
from yahoo_fin.stock_info import get_data, tickers_sp500, tickers_nasdaq, tickers_other, get_quote_table
aapl = get_data("aapl")
aapl
版主你好,請教你幾個問題
我遇到第一張圖片的問題,我有看到其他人也問相同問題,但我還是沒有解決,我是套用我的DATA,我的程式是放在code資料夾,DATA資料夾也是放在code資料夾裡,不知道這樣放有沒有錯,上面是我修改的路徑還有我資料夾的截圖。