小小資料科學家
小小資料科學家

機器學習/深度學習/投資理財/社畜日記 有拍必回~~~歡迎拍手!!!! 我的個人網站(施工中) : https://rgib37190.github.io/ 我的網站程式碼顯示得比較清楚ㄛ~~~如果馬特看不清楚可以前往我的個人網站觀看~~~

如何使用Python計算還原股價

最近在開發量化投資系統,要做量化投資最重要的就是對股票進行回測,而回測計算報酬率的時候,必須計算還原股價 ,估計公司沒有除權息調整時的股價,所以這篇文章就來教大家如何用Python計算還原股價。

資料的方面我們使用FindMind所提供的開源數據集,來做計算~

一. 取得股票各年股利分配情況

首先先取得股票的各年股利分配情況的資料,那我們這邊使用台積電(2330),作為範例,參數中的token需要到FindMind的官網去 申請帳號取得。

import requests
import pandas as pd
def get_dividend_data():
    url = "https://api.finmindtrade.com/api/v4/data"
    parameter = {
        "dataset": "TaiwanStockDividend",
        "data_id": "2330",
        "start_date": "2000-01-01",
        "token":'your token'
    }
    dividend_data = requests.get(url, params=parameter)
    dividend_data = dividend_data.json()
    dividend_data = pd.DataFrame(dividend_data['data'])
    return dividend_data

二. 取得股票的股價資料

一樣也是從FindMind官網去取得資料,和取得股利資料一樣。

def get_stock_data():
    url = "https://api.finmindtrade.com/api/v4/data"
    parameter = {
        "dataset": "TaiwanStockPrice",
        "data_id": "2330",
        "start_date": "2006-01-01",
        "token":'your_token'
    }
    resp = requests.get(url, params=parameter)
    stock_data = resp.json()
    stock_data = pd.DataFrame(stock_data["data"])
    
    return stock_data

三. 股利資料欄位介紹以及計算調整股價

首先我們計算還原股價會用到的是以下四個欄位:

  1. CashExDividendTradingDate : 除息交易日
  2. StockExDividendTradingDate : 除權交易日
  3. CashEarningsDistribution : 現金股利
  4. StockEarningsDistribution + StockStatutorySurplus : 股票股利

計算調整股價公式:

主要分成兩部分:

a. 計算除權因子: 在發放股票股利那天以前(包含當天)的股價都要乘上除權因子去調整股價。

除權因子=1/(1+股票股利/10)

除權因子=1/(1+股票股利/10)

b. 計算除息因子:

這裡有人可能會直接用收盤價扣現金股利,但這樣會導致股價調整前和調整後日收益率改變,但用下面的方法就可以讓日收益率不變, 因為我們是去計算發放現金股利後與原本股價的改變比例,而這裡t-1代表的是昨日的價格,在發放現金股利那天以前的股價 都要乘上除息因子去調整股價。

程式計算說明

首先將股利資料分別分成現金股利以及股票股利,並且將這兩個資料合併到股票價格資料上,因為股利資料會有先公布的資料, 也就是未來的資料,所以合併的同時也可以使未來的資料刪掉。

dividend_data = get_dividend_data()
stock_data = get_stock_data()  

# 將資料分成現金股利和股票股利
stock_dividend_data = dividend_data[['StockExDividendTradingDate', 'StockEarningsDistribution', 'StockStatutorySurplus']]
stock_dividend_data = stock_dividend_data.rename(columns={"StockExDividendTradingDate":'date'})
cash_dividend_data = dividend_data[['CashExDividendTradingDate', 'CashEarningsDistribution']]
cash_dividend_data = cash_dividend_data.rename(columns={"CashExDividendTradingDate":'date'})

# 順便也將未來要發股利的去掉了
stock_data = stock_data.merge(stock_dividend_data, on='date', how='left')
stock_data = stock_data.merge(cash_dividend_data, on='date', how='left')

然後在按照公式分別計算除權因子和除息因子

# 計算除權因子
stock_dividend_factor = 1 / (1 + (stock_data.loc[stock_data['StockEarningsDistribution'].notnull(), 'StockEarningsDistribution'] + stock_data.loc[stock_data['StockEarningsDistribution'].notnull(), 'StockStatutorySurplus']) / 10)
stock_data.loc[stock_dividend_factor.index-1, 'stock_dividend_factor'] = stock_dividend_factor.values

# 計算除息因子
stock_data['previous_close_price'] = stock_data['close'].shift(1)
cash_dividend_factor = (stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'previous_close_price'] - stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'CashEarningsDistribution']) / stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'previous_close_price'] 
stock_data.loc[cash_dividend_factor.index-1, 'cash_dividend_factor'] = cash_dividend_factor.values
cash_dividend_index = stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'cash_dividend_factor'].index

然後將每一期的除息因子都做累積乘積,這樣就可以將每一期調整的程度反映到過去的股價上,算完除息因子後乘上除權因子 就可以得到總因子,再乘上原始股價就可以得到調整股價,因為有些是在除權後的股價,因此就是填充為原始股價。

# 累積總因子
cum_prod_cash_dividend = stock_data.loc[stock_data['cash_dividend_factor'].notnull(), 'cash_dividend_factor'][::-1].cumprod()[::-1]
stock_data.loc[cash_dividend_factor.index-1, 'cash_dividend_factor'] = cum_prod_cash_dividend.values

stock_data['cash_dividend_factor'] = stock_data['cash_dividend_factor'].fillna(method='backfill')
stock_data['stock_dividend_factor'] = stock_data['stock_dividend_factor'].fillna(method='backfill')

stock_data['cash_dividend_factor'] = stock_data['cash_dividend_factor'].fillna(method='backfill')
stock_data['stock_dividend_factor'] = stock_data['stock_dividend_factor'].fillna(method='backfill').fillna(1)
stock_data['total_factor'] = stock_data['cash_dividend_factor'] * stock_data['stock_dividend_factor']

stock_data['adjust_price'] = stock_data['close'] * stock_data['total_factor']

# 還沒到除息日的股價用現在的股價填充
stock_data.loc[stock_data['adjust_price'].isnull(), 'adjust_price'] = stock_data.loc[stock_data['adjust_price'].isnull(), 'close']

那以下就是完整的程式碼,如果有什麼問題歡迎在下面留言,喜歡我的作品可以幫我拍拍手喔!!!

import requests
import pandas as pd

def get_dividend_data():
    url = "https://api.finmindtrade.com/api/v4/data"
    parameter = {
        "dataset": "TaiwanStockDividend",
        "data_id": "2330",
        "start_date": "2000-01-01", 
        "token":'your token'
    }
    dividend_data = requests.get(url, params=parameter)
    dividend_data = dividend_data.json()
    dividend_data = pd.DataFrame(dividend_data['data'])
    return dividend_data

def get_stock_data():
    url = "https://api.finmindtrade.com/api/v4/data"
    parameter = {
        "dataset": "TaiwanStockPrice",
        "data_id": "2330",
        "start_date": "2006-01-01",
        "token":'your token'
    }
    resp = requests.get(url, params=parameter)
    stock_data = resp.json()
    stock_data = pd.DataFrame(stock_data["data"])
    
    return stock_data

def cal_adjusted_price(stock_data, dividend_data):
    stock_dividend_data = dividend_data[['StockExDividendTradingDate', 'StockEarningsDistribution', 'StockStatutorySurplus']]
    stock_dividend_data = stock_dividend_data.rename(columns={"StockExDividendTradingDate":'date'})
    cash_dividend_data = dividend_data[['CashExDividendTradingDate', 'CashEarningsDistribution']]
    cash_dividend_data = cash_dividend_data.rename(columns={"CashExDividendTradingDate":'date'})

    # 順便也將未來要發股利的去掉了
    stock_data = stock_data.merge(stock_dividend_data, on='date', how='left')
    stock_data = stock_data.merge(cash_dividend_data, on='date', how='left')

    # 計算除權因子
    stock_dividend_factor = 1 / (1 + (stock_data.loc[stock_data['StockEarningsDistribution'].notnull(), 'StockEarningsDistribution'] + stock_data.loc[stock_data['StockEarningsDistribution'].notnull(), 'StockStatutorySurplus']) / 10)
    stock_data.loc[stock_data['StockEarningsDistribution'].notnull(), 'stock_dividend_factor'] = stock_dividend_factor

    # 計算除息因子
    stock_data['previous_close_price'] = stock_data['close'].shift(1)
    cash_dividend_factor = (stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'previous_close_price'] - stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'CashEarningsDistribution']) / stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'previous_close_price'] 
    stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'cash_dividend_factor'] = cash_dividend_factor
    cash_dividend_index = stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'cash_dividend_factor'].index
    # 累積總因子
    cum_prod_cash_dividend = stock_data.loc[stock_data['cash_dividend_factor'].notnull(), 'cash_dividend_factor'][::-1].cumprod()[::-1]
    cum_prod_cash_dividend.index = cash_dividend_index
    stock_data.loc[stock_data['CashEarningsDistribution'].notnull(), 'cash_dividend_factor'] = cum_prod_cash_dividend

    stock_data['cash_dividend_factor'] = stock_data['cash_dividend_factor'].fillna(method='backfill')
    stock_data['stock_dividend_factor'] = stock_data['stock_dividend_factor'].fillna(method='backfill')

    stock_data['cash_dividend_factor'] = stock_data['cash_dividend_factor'].fillna(method='backfill')
    stock_data['stock_dividend_factor'] = stock_data['stock_dividend_factor'].fillna(method='backfill').fillna(1)
    stock_data['total_factor'] = stock_data['cash_dividend_factor'] * stock_data['stock_dividend_factor']

    stock_data['adjust_price'] = stock_data['close'] * stock_data['total_factor']

    # 還沒到除息日的股價用現在的股價填充
    stock_data.loc[stock_data['adjust_price'].isnull(), 'adjust_price'] = stock_data.loc[stock_data['adjust_price'].isnull(), 'close']
    
    return stock_data
  
if __name__ == "__main__":
  dividend_data = get_dividend_data()
  stock_data = get_stock_data()
  stock_data = cal_adjusted_price(stock_data, dividend_data)


CC BY-NC-ND 2.0 版权声明

喜欢我的文章吗?
别忘了给点支持与赞赏,让我知道创作的路上有你陪伴。

加载中…

发布评论