Улучшение алгоритмов¶
Maximum stock weight¶
Неразумно полагаться на одну-две компании при написании алгоритма. В таком случае, даже если стратегия верна, непредсказуемые мировые события/новости могут нанести непоправимый ущерб инвестиционному портфелю (Например, 1 и 2).
Каждый год таких новостей много. Они сливаются с информационным шумом, и мы их забываем. Однако они отражаются в исторических данных, а так же сильно повлияют на прибыль в будущем.
Хороший способ диверсифицировать риски - увеличить количество инструментов в инвестиционном портфеле. После этого мы можем maximum stock weight сделать равным 0.05. Это означает, что мы будем выделять не более 5% нашего капитала на каждую инструмент в портфеле.
def get_weights_bordering_max_capital(weights, border=0.1):
weights_return = weights.fillna(0)
under_border = weights_return <= border
weights_return = weights_return.where(under_border)
weights_return = weights_return.fillna(border)
up_border = weights_return >= -1 * border
weights_return = weights_return.where(up_border)
weights_return = weights_return.fillna(-1 * border)
return weights_return
Пример использования
import qnt.data as qndata
import qnt.stats as qnstats
import qnt.stepper as qnstepper
import datetime as dt
data = qndata.load_data(
tail = dt.timedelta(days=4*365),
forward_order=True)
def get_weights_strategy(data):
strategy = data.sel(field="open") * data.sel(field="is_liquid")
weights = strategy / abs(strategy).sum('asset')
return weights
def get_weights_bordering_max_capital(weights, border=0.1):
weights_return = weights.fillna(0)
under_border = weights_return <= border
weights_return = weights_return.where(under_border)
weights_return = weights_return.fillna(border)
up_border = weights_return >= -1 * border
weights_return = weights_return.where(up_border)
weights_return = weights_return.fillna(-1 * border)
return weights_return
weights_clean = get_weights_strategy(data)
weights = get_weights_bordering_max_capital(weights_clean,
0.05)
stat = qnstats.calc_stat(data, weights)
display(stat.to_pandas().tail())
qnstats.print_correlation(weights, data)
qnstepper.write_output(weights)
field | equity | relative_return | volatility | underwater | max_drawdown | sharpe_ratio | mean_return | bias | instruments | avg_turnover | avg_holding_time |
---|---|---|---|---|---|---|---|---|---|---|---|
time | |||||||||||
2020-08-17 | 1.775526 | 0.007091 | 0.209698 | 0.000000 | -0.309622 | 0.709352 | 0.148750 | 1.0 | 951.0 | 0.023546 | 109.270150 |
2020-08-18 | 1.782287 | 0.003808 | 0.209700 | 0.000000 | -0.309622 | 0.710370 | 0.148965 | 1.0 | 951.0 | 0.023550 | 109.262512 |
2020-08-19 | 1.775737 | -0.003675 | 0.209498 | -0.003675 | -0.309622 | 0.733613 | 0.153691 | 1.0 | 951.0 | 0.023553 | 109.253208 |
2020-08-20 | 1.780279 | 0.002558 | 0.209498 | -0.001127 | -0.309622 | 0.740572 | 0.155149 | 1.0 | 951.0 | 0.023550 | 109.264783 |
2020-08-21 | 1.785862 | 0.003136 | 0.209503 | 0.000000 | -0.309622 | 0.746723 | 0.156440 | 1.0 | 951.0 | 0.023554 | 117.762112 |
WARNING! This strategy correlates with other strategies.
The number of systems with a larger Sharpe ratio and correlation larger than 0.8: 2
The max correlation value (with systems with a larger Sharpe ratio): 0.9009298804903136
Current sharpe ratio(3y): 0.7467229068047863
write output: /root/fractions.nc.gz
Neutralization¶
Давайте проанализируем показатели акций 500 самых крупных компаний, котирующихся на фондовых биржах США. Так называемый индекс S&P500 (рис. 1). Видно, что в среднем рынок растёт. Доход S&P500 сильно варьируется от нескольких процентов до более чем 20% для некоторых годов. Значит ли это, что просто держать длинные позиции хорошая идея?
Рис.1
Несмотря на рост S&P500, его Sharpe ratio менее 1. Одна из главных причин - периодические финансовые кризисы. Вот некоторые из них:
1987 год. «Черный понедельник». Индекс Dow Jones упал на 22,6%. Причина - массовый отток инвесторов с региональных рынков.
2000-2003. Крах доткомов. Кризис вызван массовым вложением денег в интернет-проекты.
2007–2008. Мировой экономический кризис. Начался с ипотечного кризиса в США, банкротства банков и падения цен на акции, проложив путь мировому экономическому кризису (иногда называемому «великой рецессией»)
Последствия кризисов видны на графике S&P 500 и проявляются в падении рынка до 30%. Наивно думать, что кризис это страшные истории из прошлого. Начало 2020 года ознаменовалось падением экономики, вызванным коронавирусом.
Мы можем исключить влияние рынка, если уравновесим длинные и короткие позиции для нашего алгоритма. Таким образом, суммарные инвестиции в рынок составят $0. Нейтрализация может быть сделана для всего рынка или для каждого сектора экономики отдельно (в целом для любой группы). Математическая формулировка нейтрализации крайне проста.
Допустим алгоритм определил вектор весов weightsi для i-ого дня. Для того что бы сделать алгоритм нейтральным относительно всего рынка (market-neutral), достаточно изменить веса алгоритма следующим образом:
neutralized_weightsi = weightsi - mean(weightsi).
Теперь среднее вектора весов для каждого дня равно нулю. Это означает, что мы не вкладываем деньги и не выводим их с рынка.
from qnt.neutralization import neutralize
weights = neutralize(weights, weights.asset ,group = 'market')
Пример использования
import qnt.data as qndata
import qnt.stats as qnstats
import qnt.stepper as qnstepper
import datetime as dt
from qnt.neutralization import neutralize
data = qndata.load_data(
tail = dt.timedelta(days=4*365),
forward_order=True)
def get_weights_strategy(data):
strategy = data.sel(field="open") * data.sel(field="is_liquid")
weights = strategy / abs(strategy).sum('asset')
return weights
weights_clean = get_weights_strategy(data)
weights = neutralize(weights_clean, weights_clean.asset ,group = 'market')
stat = qnstats.calc_stat(data, weights)
display(stat.to_pandas().tail())
qnstats.print_correlation(weights, data)
qnstepper.write_output(weights)
field | equity | relative_return | volatility | underwater | max_drawdown | sharpe_ratio | mean_return | bias | instruments | avg_turnover | avg_holding_time |
---|---|---|---|---|---|---|---|---|---|---|---|
time | |||||||||||
2020-08-17 | 1.371047 | 0.003825 | 0.085536 | 0.000000 | -0.103292 | 0.967307 | 0.082739 | 0.525606 | 951.0 | 0.018116 | 92.184119 |
2020-08-18 | 1.380413 | 0.006832 | 0.085615 | 0.000000 | -0.103292 | 0.989071 | 0.084680 | 0.525149 | 951.0 | 0.018120 | 92.183188 |
2020-08-19 | 1.377511 | -0.002102 | 0.085543 | -0.002102 | -0.103292 | 1.007448 | 0.086180 | 0.523804 | 951.0 | 0.018130 | 92.173027 |
2020-08-20 | 1.383244 | 0.004161 | 0.085570 | 0.000000 | -0.103292 | 1.027185 | 0.087896 | 0.523686 | 951.0 | 0.018136 | 92.178857 |
2020-08-21 | 1.387808 | 0.003299 | 0.085586 | 0.000000 | -0.103292 | 1.040333 | 0.089038 | 0.522531 | 951.0 | 0.018140 | 96.456512 |
WARNING! This strategy correlates with other strategies.
The number of systems with a larger Sharpe ratio and correlation larger than 0.8: 2
The max correlation value (with systems with a larger Sharpe ratio): 0.9132571807203661
Current sharpe ratio(3y): 1.0403329174432017
write output: /root/fractions.nc.gz