初學者的Python金融分析日記- QuantopianEP1 – Pipeline

內容:
1.Quantopian平台介紹
2.Quantopian演算法設計模式
3.Pipeline工具介紹

Quantopian平台介紹

Quantopian是一個美國著名雲端回測平台,它提供了一個好用的架構讓我們再上面回測交易策略,儘管現在Quantopian已經不允許我們使用它的平台直接進行線上交易,但我們仍可使用免費回測的功能理解很多金融概念。

要使用Quantopian的功能,我們必須先註冊一個免費帳號,可以選擇用google帳號註冊!

而Quantopian的功能主要可以分為Notebooks以及Algorithm兩大部分,Notebooks允許我們建立Jupyter notebook來研究適當的交易信號,而Algorithm則是允許我們寫下交易策略演算法來進行回測。

Algorithm環境
Notebook環境

一般來說,利用Notebook的高互動性,我們應該會在Notebook環境測試一些有用的交易信號,然後在Algorithm寫交易程式!

Quantopian演算法設計模式

Quantopian有提供一個樣板的程式碼(Getting Started),告訴我們Quantopian希望的設計模式大概是怎模樣,一個典型的Quantopian的交易程式會長得有點像這樣
1.決定交易的標的(Universe),這邊通常是美股
2.利用Pipeline計算因子與篩選標的
3.利用因子值來幫標的打分數,在一些限制( 槓桿 等等)與風險模型一起算出最佳資產配置

而實際上,Quantopian已經幫我們寫好1,3的部分了,所以如果使用Getting Started程式碼的話,我們只需要仔細思考如何做出有用的因子(第二步),所以學習Quantopian最佳方式,可能事先學習Quantopian提供的資料處理工具-Pipeline,本文也會從Pipeline開始講起,然後在切回Getting Started的程式碼!

Pipeline工具介紹

我們會先在Research環境介紹如何使用Pipeline,然後在介紹Alogorithm時介紹如何移植到Algorithm環境中!

首先,先點進Notebooks,然後按旁邊的+符號(如下圖),即可創建一個Jupyter notebook!

Pipeline是甚麼?

  • Pipeline物件為Quantopian 提供處理資料的工具
  • 在處理資料時,有一些常見的使用場景:​
  • 1. 在一組資產集合(大集合)中裡用移動窗口計算N個常數值(計算因子,像是ROE)​
  • 2. 根據計算出來的值選取一個交易宇宙(小的子集合)(用因子值來篩選,像是選ROE高過多少的資產)​
  • 對應Pipeline的術語,第一類計算為Factor,第二類計算為Filter​

如何在筆記本環境創建以及運行Pipeline

我們可以使用內建的Pipeline建構子(Pipeline)來製造,然後使用run_pipeline這個函數在筆記本環境下跑Pipeline,下面的程式碼可以建立一個空的Pipeline並回傳,我們會使用這個程式碼為基底來詳細解釋如何使用Pipeline!

from quantopian.pipeline import Pipeline

def make_pipeline():
    return Pipeline()

#創建pipeline實例
my_pipe = make_pipeline()
from quantopian.research import run_pipeline
result = run_pipeline(my_pipe, '2015-05-05', '2015-05-05')
result.head()

則會跑出

可以看到輸出為一個DataFrame,且具有兩層index的結構,第一層為時間,第二層為股票名!

Pipeline功能一:創建因子

因子是將在某一時間的某一股票映射到一個數值的函數,舉例來說,ROE因子便可以回傳任一時間任一股票的ROE的值!

在Quantopian裡,因子被包裝成一個物件,方便Pipeline在處理資料中調用它並且計算,一些常用的因子Quantopian已經幫我們內建好了,我們以下先介紹如何使用內建因子!

內建因子

我們先引入兩個Quantopian寫好的類別,一個是USEquityPricing,它將會是我們的資料集,裡面包含了美股的資料,另一個為SimpleMovingAverage,為Quantopian的內建因子,可用來算移動平均的值!

from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage

再引入這兩個套件後,我們將剛剛程式碼複製貼來這邊,並且在make_pipeline函數裡面添加

def make_pipeline():

    mean_close_10 = SimpleMovingAverage(
        inputs=[USEquityPricing.close],
        window_length=10
    )

    return Pipeline(
        columns={
            '10_day_mean_close': mean_close_10
        }
    )

這邊可以看到我們新增了一個變數叫做mean_close_10,這是利用SimpleMovingAverage建立的因子,裡面需要填入使用的資料與移動期數的選取,最後在回傳時,我們讓columns這個變數等於一個字典,其中key為行名,value為因子值!完整的程式碼如下

from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage

# 計算10天的收盤價的移動平均
def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    
    
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10
        }     
                   )

my_pipe = make_pipeline()
result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')

最後回傳的結果如下

結合因子

我們也可以用加減乘除把因子給結合出來,譬如說我們覺得10天線與30天線之間的比率差是一個有意義的因子,則我們可以直接把因子相減再除,程式碼如下

#結合因子
def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    mean_close_30 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=30
    )
    percent_difference = (mean_close_10 - mean_close_30)/ mean_close_30
    
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10,
            '30_day_mean_close':mean_close_30,
            'percent_difference':percent_difference
        }     
                   )

my_pipe = make_pipeline()

from quantopian.research import run_pipeline

result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')

自訂因子​

更多的時候,我們是用我們的智慧想到一些前人沒想到的因子,很有可能這些因子是沒有辦法使用加減乘除與內建因子做出來的,怎麼辦?我們可以自訂因子,自訂因子的辦法基本上是繼承Quantopian寫好的一個叫CustomFactor的物件,並改寫裡面的compute方法!


基本上可以把inputs想像成一個M乘N的矩陣,其中M為選取的期間,N為資產數量,我們會根據資料的值每個時間點計算出因子值,以下是一個具體的例子!

#自訂因子

from quantopian.pipeline import CustomFactor
import numpy as np

class StdDev(CustomFactor):
    def compute(self,today,asset_ids,out,values):
        out[:] = np.nanstd(values,axis=0)

    
def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    mean_close_30 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=30
    )
    percent_difference = (mean_close_10 - mean_close_30)/ mean_close_30
    
    std_dev = StdDev(
    inputs=[USEquityPricing.close],
    window_length=5
    )
    
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10,
            '30_day_mean_close':mean_close_30,
            'percent_difference':percent_difference,
            'std_dev':std_dev
        }     
                   )

my_pipe = make_pipeline()

result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')

可以看到我們自訂一個因子,來計算每一個股票的標準差!

Pipeline功能二:創建Filter

我們有時候想要用因子值來篩選股票,譬如說ROE要大於某個門檻值我們再考慮購買,在這邊,Quantopian叫這種操作為Filter,其形式的定義為

意思是把一個某一時間的股票映射為True/False,以下為一個創建Filter的例子,並將其值寫在一行上面

from quantopian.pipeline.factors import AverageDollarVolume
def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    mean_close_30 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=30
    )
    percent_difference = (mean_close_10 - mean_close_30)/ mean_close_30
    dollar_volume = AverageDollarVolume(window_length=30)
    high_dollar_volume = (dollar_volume > 10000000)
    std_dev = StdDev(
    inputs=[USEquityPricing.close],
    window_length=5
    )
    
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10,
            '30_day_mean_close':mean_close_30,
            'percent_difference':percent_difference,
            'std_dev':std_dev,
            'high_dollar_volume':high_dollar_volume
        }    
        
                   )

my_pipe = make_pipeline()

result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')
result.head()

但一般來說,我們並不會這樣使用Filter,我們會希望它直接幫我們篩選股票,也就是所有False的股票就不要回傳了,我們可以利用Pipeline裡面的變數screen來達到這個效果,下面為這個例子。

def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    mean_close_30 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=30
    )
    percent_difference = (mean_close_10 - mean_close_30)/ mean_close_30
    dollar_volume = AverageDollarVolume(window_length=30)
    high_dollar_volume = (dollar_volume > 10000000)
    std_dev = StdDev(
    inputs=[USEquityPricing.close],
    window_length=5
    )
    
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10,
            '30_day_mean_close':mean_close_30,
            'percent_difference':percent_difference,
            'std_dev':std_dev,
        } ,
        screen=high_dollar_volume
        
                   )

my_pipe = make_pipeline()

result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')
result.head()

可以看到,之前False的股票都沒有回傳進來了!最後我們也可以組合Filter,跟因子類似,我們可以使用邏輯運算,像是and或是or來組合Filter!,下面是一個例子!

#結合filters
def make_pipeline():
    mean_close_10 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=10
    
    )
    mean_close_30 = SimpleMovingAverage(
    inputs=[USEquityPricing.close],
    window_length=30
    )
    percent_difference = (mean_close_10 - mean_close_30)/ mean_close_30
    dollar_volume = AverageDollarVolume(window_length=30)
    high_dollar_volume = (dollar_volume > 10000000)
    latest_close = USEquityPricing.close.latest
    above_20 = latest_close > 20
    std_dev = StdDev(
    inputs=[USEquityPricing.close],
    window_length=5
    )
    is_tradeable = high_dollar_volume & above_20 #結合filters
    return Pipeline(
        columns=
        {
            
            '10_day_mean_close':mean_close_10,
            '30_day_mean_close':mean_close_30,
            'percent_difference':percent_difference,
            'std_dev':std_dev,
        } ,
        screen=is_tradeable
        
                   )

my_pipe = make_pipeline()

result = run_pipeline(my_pipe,'2015-05-05','2015-05-06')
Share

2 thoughts on “初學者的Python金融分析日記- QuantopianEP1 – Pipeline

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *