Python 動(dòng)量交易策略分析阿里巴巴股票

你有沒(méi)有注意到已經(jīng)上漲的股票未來(lái)有繼續(xù)上漲的趨勢(shì)?同樣,股票持續(xù)下跌似乎會(huì)導(dǎo)致更多的損失。這被稱為動(dòng)量,依賴于這些模式的策略是基于動(dòng)量的策略。我們開(kāi)發(fā)了一個(gè)基本的動(dòng)量策略并在阿里巴巴(NYSE: BABA)這只股票上對(duì)其進(jìn)行測(cè)試以查看回報(bào)。
動(dòng)量交易策略,即Momentum Trading Strategy。在經(jīng)典力學(xué)里,動(dòng)量即物體質(zhì)量和速度的乘積,動(dòng)量一方面描述了物體的運(yùn)動(dòng)狀態(tài),另一方面也描述了慣性的大小。在證券市場(chǎng)上,我們也可以把“證券的價(jià)格”類比成運(yùn)動(dòng)的物體,價(jià)格上漲時(shí),可以說(shuō)價(jià)格有著上漲的動(dòng)量,價(jià)格下跌時(shí)其具有下跌的動(dòng)量。這種動(dòng)量可能會(huì)使上漲或下跌繼續(xù)維持下去,也可能該動(dòng)量會(huì)越來(lái)越小,直到使之運(yùn)動(dòng)狀態(tài)發(fā)生改變。股票資產(chǎn)組合的中期收益存在延續(xù)性,即中期價(jià)格具有向某一方向連續(xù)變動(dòng)的動(dòng)量效應(yīng)。
投資者早就知道動(dòng)量的影響,并發(fā)現(xiàn)這些影響出現(xiàn)在各種市場(chǎng)和時(shí)間范圍內(nèi)。在單一工具上運(yùn)行這些策略也稱為趨勢(shì)跟蹤或時(shí)間序列動(dòng)量。我們將堅(jiān)持使用后一個(gè)名稱,并從現(xiàn)在開(kāi)始將其縮寫為 TSM。
雖然通過(guò)將其與一系列指標(biāo)、風(fēng)險(xiǎn)管理因素和多樣化相結(jié)合,有很多方法可以運(yùn)行此策略,但我們將從簡(jiǎn)單開(kāi)始展示。
首先,我們可以轉(zhuǎn)向 Python 和一些標(biāo)準(zhǔn)庫(kù)。
import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport yfinance as yf
從這里,我們可以構(gòu)建稱為 TSMStrategy 的基本策略函數(shù)。需要引入時(shí)間序列的收益對(duì)數(shù)、我們感興趣的時(shí)期以及一個(gè)確定我們是否允許空頭頭寸的布爾變量。然后該函數(shù)將為我們模擬策略并返回性能的累積總和以供進(jìn)一步分析。
def TSMStrategy(returns, period=1, shorts=False):if shorts:position = returns.rolling(period).mean().map(lambda x: -1 if x <= 0 else 1)else:position = returns.rolling(period).mean().map(lambda x: 0 if x <= 0 else 1)performance = position.shift(1) * returnsreturn performance
這是一個(gè)簡(jiǎn)單(但強(qiáng)大)策略的簡(jiǎn)單函數(shù)。下一步需要我們?yōu)樗峁┮恍?shù)據(jù),看看它的表現(xiàn)如何。我將選擇阿里巴巴的股票(NYSE: BABA)來(lái)看看我們的模型表現(xiàn)如何。
ticker = 'BABA'yfObj = yf.Ticker(ticker)data = yfObj.history(start='2014-01-01', end='2021-09-01')
使用 yfinance 包,我們可以獲得 BABA 的全部公開(kāi)歷史。它于 2002 年開(kāi)始交易,但將開(kāi)始日期設(shè)置為 2000 年將使我們能夠從頭開(kāi)始挑選股票而不會(huì)出現(xiàn)任何錯(cuò)誤。
要將其傳遞給我們的策略,我們需要計(jì)算收益對(duì)數(shù)并將其提供給我們的函數(shù)。
returns = np.log(data['Close'] / data['Close'].shift(1)).dropna()我們可以實(shí)施的最簡(jiǎn)單的 TSM 策略,購(gòu)買昨天上漲的股票,如果下跌則賣出(如果我們持有它,否則我們就等待)。讓我們?cè)囈辉嚒?/p>performance = TSMStrategy(returns, period=1, shorts=False).dropna()years = (performance.max() - performance.min()).days / 365perf_cum = np.exp(performance.cumsum())tot = perf_cum[1] - 1ann = perf_cum[1] ** (1 / years) - 1vol = performance.std() * np.sqrt(252)rfr = 0.02sharpe = (ann - rfr) / volprint(f"1-day TSM Strategy yields:" + f"\n\t{tot*100:.2f}% total returns" + f"\n\t{ann*100:.2f}% annual returns" + f"\n\t{sharpe:.2f} Sharpe Ratio")baba_ret = np.exp(returns.cumsum())b_tot = baba_ret[-1] - 1b_ann = baba_ret[-1] ** (1 / years) - 1b_vol = returns.std() * np.sqrt(252)b_sharpe = (b_ann - rfr) / b_volprint(f"Baseline Buy-and-Hold Strategy yields:" + f"\n\t{b_tot*100:.2f}% total returns" + f"\n\t{b_ann*100:.2f}% annual returns" + f"\n\t{b_sharpe:.2f} Sharpe Ratio")1-day TSM Strategy yields: 225.03% total returns 6.44% annual returns 0.12 Sharpe RatioBaseline Buy-and-Hold Strategy yields: 184.63% total returns 5.70% annual returns 0.07 Sharpe Ratio
1 日 TSM 策略以合理的年度收益擊敗買入并持有策略(忽略交易成本,鑒于這種短期策略,交易成本可能很高)。1 天收益可能充滿了許多錯(cuò)誤趨勢(shì),因此我們可以運(yùn)行各種不同的時(shí)間段來(lái)查看它們?nèi)绾委B加。我們將在 3、5、15、30 和 90 天的時(shí)間段內(nèi)循環(huán)運(yùn)行我們的模型。
import matplotlib.gridspec as gridspecperiods = [3, 5, 15, 30, 90]fig = plt.figure(figsize=(12, 10))gs = fig.add_gridspec(4, 4)ax0 = fig.add_subplot(gs[:2, :4])ax1 = fig.add_subplot(gs[2:, :2])ax2 = fig.add_subplot(gs[2:, 2:])ax0.plot((np.exp(returns.cumsum()) - 1) * 100, label=ticker, linestyle='-')perf_dict = {'tot_ret': {'buy_and_hold': (np.exp(returns.sum()) - 1)}}perf_dict['ann_ret'] = {'buy_and_hold': b_ann}perf_dict['sharpe'] = {'buy_and_hold': b_sharpe}for p in periods:log_perf = TSMStrategy(returns, period=p, shorts=False)perf = np.exp(log_perf.cumsum())perf_dict['tot_ret'][p] = (perf[-1] - 1)ann = (perf[1] ** (1/years) - 1)perf_dict['ann_ret'][p] = annvol = log_perf.std() * np.sqrt(252)perf_dict['sharpe'] = (ann - rfr) / volax0.plot((perf - 1) * 100, label=f'{p}-Day Mean')ax0.set_ylabel('Returns (%)')ax0.set_xlabel('Date')ax0.set_title('Cumulative Returns')ax0.grid()ax0.legend()_ = [ax1.bar(i, v * 100) for i, v in enumerate(perf_dict['ann_ret'].values())]ax1.set_xticks([i for i, k in enumerate(perf_dict['ann_ret'])])ax1.set_xticklabels([f'{k}-Day Mean'if type(k) is int else ticker fork in perf_dict['ann_ret'].keys()],rotation=45)ax1.grid()ax1.set_ylabel('Returns (%)')ax1.set_xlabel('Strategy')ax1.set_title('Annual Returns')_ = [ax2.bar(i, v) for i, v in enumerate(perf_dict['sharpe'].values())]ax2.set_xticks([i for i, k in enumerate(perf_dict['sharpe'])])ax2.set_xticklabels([f'{k}-Day Mean'if type(k) is int else ticker fork in perf_dict['sharpe'].keys()],rotation=45)ax2.grid()ax2.set_ylabel('Sharpe Ratio')ax2.set_xlabel('Strategy')ax2.set_title('Sharpe Ratio')plt.tight_layout()plt.show()

從上圖可知,15 天動(dòng)量指標(biāo)為我們提供了最佳的風(fēng)險(xiǎn)收益。但是,這些結(jié)果存在很大差異。這表明我們沒(méi)有非常穩(wěn)健的策略。我們可以在這個(gè)基本策略的基礎(chǔ)上結(jié)合其他指標(biāo),例如移動(dòng)平均線或指數(shù)加權(quán)移動(dòng)平均線來(lái)表示動(dòng)量。我們可以通過(guò)結(jié)合止損或追蹤止損來(lái)更好地管理我們的風(fēng)險(xiǎn),而不是在我們有 15 天下跌或持平時(shí)退出更接近頂部的交易。我們還可以在多種證券之間分配資本,從而從多樣化中受益,并在趨勢(shì)出現(xiàn)時(shí)跟隨。其中一些改進(jìn)需要更復(fù)雜的、事件驅(qū)動(dòng)的回測(cè)系統(tǒng)來(lái)進(jìn)行模擬。
