跳轉到

Pandas 策略開發基礎語法

1. 安裝環境

import pandas as pd
import numpy as np
from finlab import data

2. 創造資料物件

data.get() 回傳的是一個 DataFrame,以日期為索引、股票代號為欄位。取單一欄位則為 Series。

# dataframe:資料格式範例
df = data.get('price:收盤價')
df = df[df.index <= '2020-07-01']

# series:資料格式範例
series = df['0050']

3. 資料檢索

df = data.get('price:收盤價')

# head(n):取前 n 列,預設為 5
demo_head = df.head(3)

# tail(n):取後 n 列,預設為 5
demo_tail = df.tail(3)

# index:取索引
demo_index = df.index

# columns:取欄位
demo_columns = df.columns

# values:取值
demo_values = df.values

# boolean indexing:索引布林邏輯篩選
# ex:取得索引大於 2020 年份的資料
demo_boolean_indexing = df[df.index > '2020']

# loc:使用標籤篩選欄列
# ex:選擇 2020-01-03~2020-01-08 中所有標的的股價
demo_loc1 = df.loc['2020-01-03':'2020-01-08']

# ex:選擇 0050 & 1101 & 2330 的股價,以下兩種方法等價
demo_loc2 = df.loc[:, ['0050', '1101', '2330']]
demo_column_select = df[['0050', '1101', '2330']]

# ex:選擇 2020-01-03~2020-01-08 中 0050 & 1101 & 2330 的股價
demo_loc3 = df.loc['2020-01-03':'2020-01-08', ['0050', '1101', '2330']]

# iloc:使用標籤位置篩選欄列
# ex:選擇第 6 列到第 10 列的資料
demo_iloc1 = df.iloc[5:10]

# ex:選擇 df 表中第 6 檔到第 10 檔標的資料
demo_iloc2 = df.iloc[:, 5:10]

# ex:選擇第 6 列到第 10 列 & 第 6 檔到第 10 檔標的資料
demo_iloc3 = df.iloc[5:10, 5:10]

# nlargest:取前 n 大
# ex:取近一日股價前 n 大的標的
demo_nlargest = df.iloc[-1].nlargest(10)

# nsmallest:取前 n 小
# ex:取近一日股價前 n 小的標的
demo_nsmallest = df.iloc[-1].nsmallest(10)

4. 顯示控制

Pandas 預防資源消耗,預設顯示列數為 10,中間資料以 ... 帶過。若想要全展開或調整顯示數量來檢視資料,可作以下控制。要注意的是此影響為全域,接下來的顯示數都會被影響。

df = data.get('price:收盤價')

# 列數全展開
pd.set_option("display.max_rows", None)

# 欄數全展開
pd.set_option("display.max_columns", None)

# 最多顯示 20 列
pd.set_option("display.max_rows", 20)

# 最多顯示 20 欄
pd.set_option("display.max_columns", 20)

# 還原顯示數預設初始值
pd.reset_option("^display")

5. 資料運算

df = data.get('price:收盤價')

# 產生股價大於 100 元的布林 DataFrame
# 若大於 100 則顯示 True(計算時視為 1),否則顯示 False(計算時視為 0)
price_up_10 = df > 100

# sum:加總,axis=0 為整欄加總,axis=1 為整列加總
# ex:計算每日股價大於 100 的標的數量
demo_sum = price_up_10.sum(axis=1)

# mean:平均數,常用於計算均線
demo_mean = df.mean()

# median:中位數
demo_median = df.median()

# std:標準差,常用於計算乖離率
demo_std = df.std()

# max:最大值
demo_max = df.max()

# min:最小值
demo_min = df.min()

# cumsum:累加
# ex:累積漲多少元
demo_diff = df.diff()
demo_cumsum = demo_diff.cumsum()

# cumprod:累乘
# ex:計算累積報酬率
demo_pct_change = df.pct_change() + 1
demo_cumprod = demo_pct_change.cumprod()

# cummax:累積最大值,常用於計算 drawdown
demo_cummax = df.cummax()

# cummin:累積最小值
demo_cummin = df.cummin()

# quantile:第 c 百分位數數值,常用於取標的前 c% 強標的
demo_quantile = df.iloc[-1].quantile(0.9)

# corr:相關性
demo_corr = df.iloc[:, :5].corr()

# describe:取統計資料
demo_describe = df.describe()

6. 移動窗格作業

df = data.get('price:收盤價')

# shift:資料移動,常用於前後期數值增減比較、年增率計算
# ex:收盤價向下平移一列
demo_shift1 = df.shift()
# ex:收盤價向上平移一列
demo_shift2 = df.shift(-1)

# rolling:移動窗格作業,常結合資料運算公式做滾動式計算
# ex:計算 20 日移動平均線(前 n 筆資料未滿 10 日取 NaN,未滿 20 日以 n 日計算)
demo_rolling = df.rolling(20, min_periods=10).mean()

# diff:列數相減
# ex:取每日漲跌價
demo_diff = df.diff()

# pct_change:列數相除
# ex:取每日漲跌幅
demo_pct_change = df.pct_change()

7. 使用 FinlabDataFrame 的功能

除了使用熟悉的 Pandas 語法,還可以使用 FinlabDataFrame 的 function!

data.get() 的資料繼承 FinlabDataFrame 的功能,可用函式如下:

函式 說明
average(n) 取移動平均,min_periods=int(n/2)
is_largest(n) 取前 n 大
is_smallest(n) 取前 n 小
rise(n=1) 取比前第 n 筆高
fall(n=1) 取比前第 n 筆低
sustain(nwindow, nsatisfy=None) 取滾動 nwindow 筆加總大於 nsatisfy
quantile_row(n) 返回請求軸上給 n 定分位數的值
df[['0050', '1101']].average(10)
df.iloc[-10:].is_largest(5)
df.iloc[-10:].is_smallest(5)
df.iloc[-10:, 10:15].rise()
df.iloc[-10:, 10:15].fall()

# ex:是否連兩日上漲?
df.iloc[-10:, 10:15].rise().sustain(2)

# ex:取近 10 日股價前 90% 分位數
df.iloc[-10:].quantile_row(0.9)

8. 視覺化

8-1. 乖離率線圖

使用布林通道(Bollinger Bands)繪製股價走勢與上下軌道。

df = data.get('price:收盤價').iloc[-400:]['2330']
mean_20 = df.rolling(20).mean()
std_value = df.rolling(20).std()

up = mean_20 + std_value * 2
down = mean_20 - std_value * 2
up.plot(label='up', legend=True)
down.plot(label='down', legend=True)
mean_20.plot(label='ma_20', legend=True)
df.plot(title='bias plot', label='close', legend=True, figsize=(20, 8), grid=True)

8-2. 相關性熱力圖

將多檔股票的相關性以熱力圖呈現,快速觀察標的之間的連動關係。

df = data.get('price:收盤價')
check_list = ['1101', '1102', '2330', '2454', '6263', '9939']
demo_corr = df.iloc[-600:][check_list].corr()

demo_corr.style.background_gradient(cmap='viridis') \
    .set_properties(**{'font-size': '20px'})

9. 策略範例

9-1. 均線多頭

選出股價同時站上 5 日、10 日、20 日、60 日均線的股票,取前 10 大。

df = data.get('price:收盤價')
# 限定範圍
df = df[(df.index > '2015') & (df.index < '2019')]

cond1 = df > df.rolling(5).mean()
cond2 = df > df.rolling(10, min_periods=5).mean()
cond3 = df > df.rolling(20, min_periods=10).mean()
cond4 = df > df.rolling(60, min_periods=40).mean()

position = (df * (cond1 & cond2 & cond3 & cond4))
position = position[position > 0]
position = position.is_largest(10)

9-2. 突破布林通道上緣

選出收盤價剛突破布林通道上緣的股票,並限制股價在 5~200 元之間。

df = data.get('price:收盤價')
mean_20 = df.rolling(20).mean()
std_value = df.rolling(20).std()

up = mean_20 + std_value * 2

# 收盤價剛站上布林通道上緣
cond1 = (df > up) & (df.shift() < up)
cond2 = (df > 5) & (df < 200)
position = (cond1 & cond2)
position = position[position > 0]
position = position.is_smallest(20)