交易中的數(shù)學(xué):夏普(Sharpe)和索蒂諾(Sortino)比率
投資回報(bào)率是投資者和萌新交易員用來(lái)分析交易績(jī)效的最明顯指標(biāo)。 專(zhuān)業(yè)交易者會(huì)采用更可靠的工具來(lái)分析策略,比如夏普(Sharpe)比率和索蒂諾(Sortino)比率等。 在這篇文章中,我們研究簡(jiǎn)單的例子來(lái)理解這些比率是如何計(jì)算的。 交易策略的評(píng)估細(xì)節(jié)在前文中曾進(jìn)行了討論“交易中的數(shù)學(xué): 如何評(píng)估交易結(jié)果"。 建議您閱讀這篇文章,從而刷新認(rèn)知、或?qū)W習(xí)新知識(shí)。赫茲股票期量化軟件
編輯搜圖
請(qǐng)點(diǎn)擊輸入圖片描述(最多18字)
夏普比率
經(jīng)驗(yàn)豐富的投資者和交易者經(jīng)常運(yùn)用多種策略進(jìn)行交易,投資不同的資產(chǎn),以此獲得持久的結(jié)果。 這是智能投資的概念之一,意味著創(chuàng)建投資組合。 每個(gè)證券/策略組合都有自己的風(fēng)險(xiǎn)和回報(bào)參數(shù),能以某種方式進(jìn)行比較。赫茲股票期量化軟件
進(jìn)行這種比較的最具參考價(jià)值的工具之一就是夏普比率,它是由諾貝爾獎(jiǎng)獲得者威廉·F·夏普于 1966 年開(kāi)發(fā)的。 該比率的計(jì)算采用基本績(jī)效指標(biāo),包括平均回報(bào)率、回報(bào)標(biāo)準(zhǔn)差和無(wú)風(fēng)險(xiǎn)回報(bào)。赫茲股票期量化軟件
夏普比率的缺點(diǎn)是,用于分析的源數(shù)據(jù)必須呈正態(tài)分布。 換言之,收益分布圖應(yīng)該是對(duì)稱(chēng)的,不應(yīng)該有尖峰或陡坑。
夏普比率使用以下公式計(jì)算:
Sharpe Ratio = (Return - RiskFree)/Std
其中:
Return — 某一時(shí)段的平均回報(bào)率。 例如,月度、季度、年度、等等。
RiskFree — 同期無(wú)風(fēng)險(xiǎn)回報(bào)率。 傳統(tǒng)上,這些資產(chǎn)包括銀行存款、債券和其它 100% 可靠的最低風(fēng)險(xiǎn)資產(chǎn)。
Std — 同期投資組合回報(bào)的標(biāo)準(zhǔn)偏差。 收益偏離預(yù)期值越大,交易員賬戶(hù)或投資組合資產(chǎn)的風(fēng)險(xiǎn)和波動(dòng)性就越高。赫茲股票期量化軟件
回報(bào)
回報(bào)率是依據(jù)一定時(shí)段內(nèi)資產(chǎn)價(jià)值的變化來(lái)計(jì)算的。 返回值用于計(jì)算夏普比率的同一時(shí)間段。 一般來(lái)講,會(huì)考慮年度夏普比率,但計(jì)算也可以依據(jù)季度、月度、甚至每日的數(shù)值。 回報(bào)率由以下公式計(jì)算:
Return[i] = (Close[i]-Close[i-1])/Close[i-1]
其中:
Return[i] — 間隔 i 的回報(bào);
Close[i] — 第 i 個(gè)區(qū)間結(jié)束時(shí)的資產(chǎn)價(jià)值;
Close[i-1] — 上一個(gè)間隔結(jié)束時(shí)的資產(chǎn)價(jià)值。
換言之,回報(bào)可以寫(xiě)為所選期間資產(chǎn)價(jià)值的相對(duì)變化:
Return[i] = Delta[i]/Previous
其中:
Delta[i] = (Close[i]-Close[i-1]) — 所選期間資產(chǎn)價(jià)值的絕對(duì)變化;
Previous = Close[i-1] — 上一個(gè)間隔結(jié)束時(shí)的資產(chǎn)價(jià)值。赫茲股票期量化軟件
若要依據(jù)日線(xiàn)數(shù)值計(jì)算年度的夏普比率,我們應(yīng)該采用年內(nèi)每天的回報(bào)值,并取回報(bào)累積除以天數(shù)計(jì)算平均日回報(bào)。?
Return = Sum(Return[i])/N
其中 N 是天數(shù)。
無(wú)風(fēng)險(xiǎn)回報(bào)
無(wú)風(fēng)險(xiǎn)回報(bào)的概念是有條件的,因?yàn)轱L(fēng)險(xiǎn)總會(huì)存在。 由于夏普比率用于比較相同時(shí)間間隔內(nèi)的不同策略/投資組合,因此可以在公式中選取零無(wú)風(fēng)險(xiǎn)回報(bào)。 就是,
RiskFree = 0
標(biāo)準(zhǔn)偏差或回報(bào)率
標(biāo)準(zhǔn)偏差表示隨機(jī)變量如何偏離平均值。 首先,計(jì)算平均回報(bào)值,然后累計(jì)距均值的回報(bào)偏差。 結(jié)果之和除以回報(bào)數(shù)字以便獲得離散度。 離散度的平方根是標(biāo)準(zhǔn)偏差。
D = Sum((Return - Return[i])^2 )/N
STD = SQRT(D)
前面提及的文章中提供了計(jì)算標(biāo)準(zhǔn)偏差的示例。
計(jì)算任何時(shí)間段的夏普比率,并將其轉(zhuǎn)換為年度值
自 1966 年以來(lái),夏普比率的計(jì)算方法一直不曾改變。 這種計(jì)算方法被廣泛認(rèn)可后,該變量改為更現(xiàn)代的名稱(chēng)。 在那時(shí),資金和投資組合績(jī)效評(píng)估是基于若干年來(lái)賺取的回報(bào)。 此后,依據(jù)月度數(shù)據(jù)進(jìn)行計(jì)算,而由此產(chǎn)赫茲股票期量化軟件生的夏普比率會(huì)被映射到年度值。 這種方法可以比較兩種資金、投資組合或策略。
夏普比率能夠輕易地從不同時(shí)期和時(shí)間幀擴(kuò)展到年度值。 這是依據(jù)將結(jié)果值乘以年度間隔與當(dāng)前間隔之比的平方根來(lái)實(shí)現(xiàn)的。 我們來(lái)研究下面的例子。
假設(shè)我們采用每日回報(bào)值計(jì)算夏普比率 — SharpeDaily。 結(jié)果應(yīng)轉(zhuǎn)換為年度值 SharpeAnnual。 年度比率與周期比率的平方根成正比,即一年當(dāng)中相應(yīng)的每日間隔數(shù)量。 鑒于一年當(dāng)中有 252 個(gè)交易日,基于每日回報(bào)的夏普比率應(yīng)乘以 252 的平方根。 這將得到年度夏普比率:赫茲股票期量化軟件
SharpeAnnual?=?SQRT(252)*SharpeDaily?//?252?working?days?in?a?year
如果該值是基于 H1 時(shí)間幀計(jì)算的,我們要采用相同的原則 — 首先將 SharpeHourly 轉(zhuǎn)換為 SharpeDaily,然后計(jì)算年度 Sharpe 比率。 一根 D1 柱線(xiàn)包括 24 根 H1 柱線(xiàn),因此公式如下:
SharpeDaily?=?SQRT(24)*SharpeHourly???//?24?hours?fit?into?D1
并非所有金融產(chǎn)品都是 24 小時(shí)交易的。 但在測(cè)試人員對(duì)同一金融產(chǎn)品的交易策略進(jìn)行評(píng)估時(shí),這一點(diǎn)并不重要,因?yàn)楸容^是針對(duì)相同的測(cè)試間隔和時(shí)間幀進(jìn)行的。
依據(jù)夏普比率評(píng)估策略
依據(jù)策略/投資組合的績(jī)效,夏普比率可得到不分?jǐn)?shù)值,甚至是負(fù)值。 將夏普比率轉(zhuǎn)換為年度值可由經(jīng)典方式進(jìn)行解釋?zhuān)悍种?br>?含義?說(shuō)明?夏普比率 < 0糟糕這樣的策略無(wú)利可圖?0 < 夏普比率? < 1.0
未定義
風(fēng)險(xiǎn)沒(méi)有得到足夠回報(bào)。 在沒(méi)有其它選擇的情況下,可以考慮這種策略
?夏普比率 ≥ 1.0
良好
如果夏普比率大于 1,可能意味著風(fēng)險(xiǎn)得到了足夠回報(bào),投資組合/策略可以顯示出正面的結(jié)果
?夏普比率 ≥ 3.0優(yōu)秀高分值表示在每筆特定成交中遭遇虧損的概率非常低
不要忘記夏普系數(shù)是一個(gè)常規(guī)的統(tǒng)計(jì)變量。 它反映了回報(bào)和風(fēng)險(xiǎn)之間的比率。 因此,在分析不同的投資組合和策略時(shí),重要的是將夏普比率與建議值相關(guān)聯(lián),或與相關(guān)值進(jìn)行比較。赫茲股票期量化軟件
針對(duì) EURUSD,2020 年度的夏普比率計(jì)算
夏普比率最初用來(lái)評(píng)估通常由許多股票組成的投資組合。 股票的價(jià)值每天都在變化,投資組合的價(jià)值也隨之變化。 價(jià)值和回報(bào)的變化可以在任何時(shí)間幀內(nèi)進(jìn)行衡量。 我們來(lái)觀(guān)察 EURUSD 計(jì)算結(jié)果。
計(jì)算是在兩個(gè)時(shí)間幀 H1 和 D1 上進(jìn)行的。 然后,我們將結(jié)果轉(zhuǎn)換為年度值,并進(jìn)行比較,看看是否存在差異。 我們將選用 2020 年的柱線(xiàn)收盤(pán)價(jià)進(jìn)行計(jì)算。
MQL5 的代碼
//+------------------------------------------------------------------+//| Script program start function????????????????????????????????????|//+------------------------------------------------------------------+void OnStart()
??{//---?? double H1_close[],D1_close[];
?? double h1_returns[],d1_returns[];
?? datetime from = D'01.01.2020';
?? datetime to = D'01.01.2021';
?? int bars = CopyClose("EURUSD",PERIOD_H1,from,to,H1_close);
?? if(bars == -1)
??????Print("CopyClose(\"EURUSD\",PERIOD_H1,01.01.2020,01.01.2021 failed. Error ",GetLastError());
?? else???? {
??????Print("\nCalculate the mean and standard deviation of returns on H1 bars");
??????Print("H1 bars=",ArraySize(H1_close));
??????GetReturns(H1_close,h1_returns);
??????double average = ArrayMean(h1_returns);
??????PrintFormat("H1 average=%G",average);
??????double std = ArrayStd(h1_returns);
??????PrintFormat("H1 std=%G",std);
??????double sharpe_H1 = average / std;
??????PrintFormat("H1 Sharpe=%G",sharpe_H1);
??????double sharpe_annual_H1 = sharpe_H1 * MathSqrt(ArraySize(h1_returns));
??????Print("Sharpe_annual(H1)=", sharpe_annual_H1);
???? }
?? bars = CopyClose("EURUSD",PERIOD_D1,from,to,D1_close);
?? if(bars == -1)
??????Print("CopyClose(\"EURUSD\",PERIOD_D1,01.01.2020,01.01.2021 failed. Error ",GetLastError());
?? else???? {
??????Print("\nCalculate the mean and standard deviation of returns on D1 bars");????
??????Print("D1 bars=",ArraySize(D1_close));
??????GetReturns(D1_close,d1_returns);
??????double average = ArrayMean(d1_returns);
??????PrintFormat("D1 average=%G",average);
??????double std = ArrayStd(d1_returns);
??????PrintFormat("D1 std=%G",std);
??????double sharpe_D1 = average / std;
??????double sharpe_annual_D1 = sharpe_D1 * MathSqrt(ArraySize(d1_returns));
??????Print("Sharpe_annual(H1)=", sharpe_annual_D1);
???? }
??}//+------------------------------------------------------------------+//|??Fills the returns[] array of returns ???????????????????????????|//+------------------------------------------------------------------+void GetReturns(const double & values[], double & returns[])
??{
?? int size = ArraySize(values);//--- if less than 2 values, return an empty array of returns?? if(size < 2)
???? {
??????ArrayResize(returns,0);
??????PrintFormat("%s: Error. ArraySize(values)=%d",size);
??????return;
???? }
?? else???? {
??????//--- fill returns in a loop??????ArrayResize(returns, size - 1);
??????double delta;
??????for(int i = 1; i < size; i++)
????????{
???????? returns[i - 1] = 0;
???????? if(values[i - 1] != 0)
?????????? {
????????????delta = values[i] - values[i - 1];
????????????returns[i - 1] = delta / values[i - 1];
?????????? }
????????}
???? }//---??}//+------------------------------------------------------------------+//|??Calculates the average number of array elements ????????????????|//+------------------------------------------------------------------+double ArrayMean(const double & array[])
??{
?? int size = ArraySize(array);
?? if(size < 1)
???? {
??????PrintFormat("%s: Error, array is empty",__FUNCTION__);
??????return(0);
???? }
?? double mean = 0;
?? for(int i = 0; i < size; i++)
??????mean += array[i];
?? mean /= size;
?? return(mean);
??}//+------------------------------------------------------------------+//|??Calculates the standard deviation of array elements?????????????|//+------------------------------------------------------------------+double ArrayStd(const double & array[])
??{
?? int size = ArraySize(array);
?? if(size < 1)
???? {
??????PrintFormat("%s: Error, array is empty",__FUNCTION__);
??????return(0);
???? }
?? double mean = ArrayMean(array);
?? double std = 0;
?? for(int i = 0; i < size; i++)
??????std += (array[i] - mean) * (array[i] - mean);
?? std /= size;
?? std = MathSqrt(std);
?? return(std);
??}??//+------------------------------------------------------------------+/*
Result
Calculate the mean and standard deviation of returns on H1 bars
H1 bars:6226
H1 average=1.44468E-05
H1 std=0.00101979
H1 Sharpe=0.0141664Sharpe_annual(H1)=1.117708053392263Calculate the mean and standard deviation of returns on D1 bars
D1 bars:260
D1 average=0.000355823
D1 std=0.00470188Sharpe_annual(H1)=1.2179005039019222*/
使用?MetaTrader 5 函數(shù)庫(kù)?計(jì)算的 Python 代碼
import math
from datetime import datetimeimport MetaTrader5 as mt5# display data on the MetaTrader 5 packageprint("MetaTrader5 package author: ", mt5.__author__)
print("MetaTrader5 package version: ", mt5.__version__)# import the 'pandas' module for displaying data obtained in the tabular formimport pandas as pd
pd.set_option('display.max_columns', 50)??# how many columns to show
pd.set_option('display.width', 1500)??# max width of the table to show# import pytz module for working with the time zoneimport pytz# establish connection to the MetaTrader 5 terminalif not mt5.initialize():
????print("initialize() failed")
????mt5.shutdown()# set time zone to UTCtimezone = pytz.timezone("Etc/UTC")# create datetime objects in the UTC timezone to avoid the local time zone offsetutc_from = datetime(2020, 1, 1, tzinfo=timezone)
utc_to = datetime(2020, 12, 31, hour=23, minute=59, second=59, tzinfo=timezone)# get EURUSD H1 bars in the interval 2020.01.01 00:00 - 2020.31.12 13:00 in the UTC timezonerates_H1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_H1, utc_from, utc_to)# also get D1 bars in the interval 2020.01.01 00:00 - 2020.31.12 13:00 in the UTC timezonerates_D1 = mt5.copy_rates_range("EURUSD", mt5.TIMEFRAME_D1, utc_from, utc_to)# shut down connection to the MetaTrader 5 terminal and continue processing obtained barsmt5.shutdown()# create DataFrame out of the obtained datarates_frame = pd.DataFrame(rates_H1)# add the "Return" columnrates_frame['return'] = 0.0# now calculate the returns as return[i] = (close[i] - close[i-1])/close[i-1]prev_close = 0.0for i, row in rates_frame.iterrows():
????close = row['close']
????rates_frame.at[i, 'return'] = close / prev_close - 1 if prev_close != 0.0 else 0.0????prev_close = close
print("\nCalculate the mean and standard deviation of returns on H1 bars")
print('H1 rates:', rates_frame.shape[0])
ret_average = rates_frame[1:]['return'].mean()??# skip the first row with zero returnprint('H1 return average=', ret_average)
ret_std = rates_frame[1:]['return'].std(ddof=0) # skip the first row with zero returnprint('H1 return std =', ret_std)
sharpe_H1 = ret_average / ret_std
print('H1 Sharpe = Average/STD = ', sharpe_H1)
sharpe_annual_H1 = sharpe_H1 * math.sqrt(rates_H1.shape[0]-1)
print('Sharpe_annual(H1) =', sharpe_annual_H1)# now calculate the Sharpe ratio on the D1 timeframerates_daily = pd.DataFrame(rates_D1)# add the "Return" columnrates_daily['return'] = 0.0# calculate returnsprev_return = 0.0for i, row in rates_daily.iterrows():
????close = row['close']
????rates_daily.at[i, 'return'] = close / prev_return - 1 if prev_return != 0.0 else 0.0????prev_return = close
print("\nCalculate the mean and standard deviation of returns on D1 bars")
print('D1 rates:', rates_daily.shape[0])
daily_average = rates_daily[1:]['return'].mean()
print('D1 return average=', daily_average)
daily_std = rates_daily[1:]['return'].std(ddof=0)
print('D1 return std =', daily_std)
sharpe_daily = daily_average / daily_std
print('D1 Sharpe =', sharpe_daily)
sharpe_annual_D1 = sharpe_daily * math.sqrt(rates_daily.shape[0]-1)
print('Sharpe_annual(D1) =', sharpe_annual_D1)
Result
Calculate the mean and standard deviation of returns on H1 bars
H1 rates: 6226
H1 return average= 1.4446773215242986e-05
H1 return std = 0.0010197932969323495
H1 Sharpe = Average/STD = 0.014166373968823358Sharpe_annual(H1) = 1.117708053392236Calculate the mean and standard deviation of returns on D1 bars
D1 rates: 260
D1 return average= 0.0003558228355051694
D1 return std = 0.004701883757646081
D1 Sharpe = 0.07567665511222807Sharpe_annual(D1) = 1.2179005039019217
如您所見(jiàn),MQL5 和 Python 的計(jì)算結(jié)果是相同的。 源代碼附在下面(CalculateSharpe_2TF)。赫茲股票期量化軟件
依據(jù) H1 和 D1 柱線(xiàn)計(jì)算的年度夏普比率的差別:分別對(duì)應(yīng) 1.117708 和 1.217900。 我們來(lái)嘗試找出原因。