[Python筆記]何時該買賣(1):動態規劃

這幾天想這個題目寫的豆頁很痛= =,誰叫我動態規劃一直都沒有學好ㄞㄞㄞ,趁還沒忘記的時候趕快來做個筆記。

結果我發現其實這似乎不是最佳解啊哈哈哈^^。

條件與目標

目標

在我們的 function 裡,我們要在「知道證券的所有歷史資料」的設定下決定何時該買、何時該賣、或是什麼都不做,才能在「考慮手續費」的情況裡得到最大的獲利。

理論上來說,每一筆資料應該會有一個標準答案,也就是在最佳買點和賣點的考量下產生一個利潤極值。

變數定義

priceVec
  • 是一個矩陣。
  • 裡面會是一筆價格的歷史資料(等一下會用的是 SPY ETF 每日 Adj Close 的價格)。
transFeeRate
  • 是一個數字。
  • 每次交易所需的手續費,測試用的時候常用 0.01 ,代表每次「買、賣」都要多付出 1% 的手續費給第三方(如政府)。

交易條件

  • 每天在看到價格時,只能做一次「買」或「賣」,或是什麼都不做。
  • 每次買賣都是歐硬,不留倉、不建倉、不分散風險,要買就是用手頭現金全買證券,要賣就是全賣掉手上持有的證券換現金。所以每天我們身上只會有「全部都是現金」跟「全部都是證券」。
  • 這並「不是」什麼技術分析之類的去「預估」未來的股價,因為我們在一開始就知道全部的歷史價格(放在priceVec裡面)。這是要用過去的資料去、考慮交易會有手續費的情況下去推得何時該買賣以求得罪大收益。
  • 動態規劃只是其中一種方法而已,或許會有更好的方法。

思緒

因為條件告訴我們:

每天我們身上只會有「全部都是現金」跟「全部都是證券」。

所以在每天都考慮兩種持有狀態(現金或證券),共四種情況:

  1. 我今天「結束時」持有現金:

    • 是因為我昨天結束時也持有現金(意思就是我今天什麼都不做)。
    • 我在今天還沒結束時把昨天結束時持有的證券全部賣了,換成現金。
  2. 我今天「結束時」持有證券:

    • 是因為我昨天結束時也持有證券(意思就是我今天什麼都不做)。
    • 我在今天還沒結束時把昨天結束時持有的現金全部拿去買了,換成證券。

我們每天要取的極值是以下兩種:

  1. 在「今天結束時持有現金」狀況的前提下:

    • 如果我們算出「我今天什麼都不做」比「把昨天的證券賣了」還要好(就是擁有現金比較多),那我們今天就什麼都不做。
    • 反之,如果賣了證券可以讓今天結束時的我擁有更多的現金,那我應該要今天賣掉。
  2. 在「今天結束時持有證券」狀況的前提下:

    • 如果我們算出「我今天什麼都不做」比「把昨天的現金拿來買證券」還要好(就是擁有證券「淨值」比較多),那我們今天就什麼都不做。
    • 反之,如果買了證券可以讓今天結束時的我擁有的證券「淨值」較多,那我應該在今天買入。

概念

動態規劃

度的,有了以上的想法,就把他換成程式碼惹。

我們假設一開始第零天的預設狀況:

  1. 我們有一塊錢(持有現金)。這樣最後跑出的數值可以直接當變成幾倍來用。
  2. 我們沒有證券。

所以初始的狀態會是:

cash = 1 # 持有的現金量(單位是元)
stock = 0 # 持有的證券張數(單位是張)

於是,在第 i 天時:

對上面 1. 的情況而言:

cash = max(cash , stock*(priceVec[i]*(1-transFeeRate)))
  • 等號左邊是「今天結束後」更新的持有現金量
  • 等號右邊取兩者極大值,對應我們今天到底該「什麼都不做(持有跟昨天一樣的現金量)」或「全賣債券」的狀態。在這裡,每次賣掉證券所得到的現金要考慮手續費的扣減,實得:

$$ cash = \frac {stock \cdot priceVec[i]}{1-transFeeRate} $$

對上面的 2. 的情況而言:

stock = max(stock, cash/(priceVec[i]*(1+transFeeRate)))		
  • 等號左邊是「今天結束後」更新的持有的證券張數
  • 等號右邊取兩者極大值,對應我們今天到底該「什麼都不做(持有跟昨天一樣的證券)」或「全買債券」的狀態。在這裡,每次花現金買入證券所得到的張數要考慮手續費的扣減,實得:

$$ stock = \frac {cash}{priceVec[i] \cdot (1+transFeeRate)} $$

最後比較的極值

到了最後一天結束時,我們比較手上持有的「現金」和「證券淨值」何者比較大,比較大的就是我們可以在這次的交易過程獲得的最大利潤。

其中,證券淨值就是無論如何都要在最後一天賣出所有的證券張數所得到的現金,即:

$$ stockvalue = stock \cdot priceVec[-1] \cdot (1-transFeeRate) $$

# 證券淨值
stockvalue = stock*priceVec[-1]*(1-transFeeRate)

# 比較
maxvalue = max(cash, stockvalue)

# 印出
print(maxvalue)

實作

'''
By ChingRu @ 2018-11-18
'''

def myOptimAction(priceVec, transFeeRate):
	import numpy as np

	# 先檢查共有幾筆資料
	dataLen = len(priceVec)
	
	# set as default
	cash = 1
	stock = 0

	for i in range(0, dataLen):	
		# maximized two situation for each day
		cash = max(cash,stock*(priceVec[i]*(1-transFeeRate)))
		stock = max(stock, cash/(priceVec[i]*(1+transFeeRate)))		

	# output maximum value
	stockvalue = stock*priceVec[-1]*(1-transFeeRate)
	maxvalue = max(cash, stockvalue)
	print(maxvalue)

當用SPY ETF這筆 csv 裡面的 Adj Close 價格試跑時,得到 216.64223869369437 ,相當於原本一塊錢增加 216 倍。