<jupyter_start><jupyter_text>Формируем бенчмарк<jupyter_code>universe = pqr.Universe(prices)
universe.filter(volume >= 1_000_000)

benchmark = pqr.Benchmark.from_universe(universe)<jupyter_output><empty_output><jupyter_text>Перебор параметров<jupyter_code>def dynamic_factor_preprocessor_factory(looking, lag, holding):
    return [
        pqr.Filter(universe.mask),
        pqr.LookBackPctChange(looking),
        pqr.Lag(lag),
        pqr.Hold(holding)
    ]


def mean_factor_preprocessor_factory(looking, lag, holding):
    return [
        pqr.Filter(universe.mask),
        pqr.LookBackMean(looking),
        pqr.Lag(lag),
        pqr.Hold(holding)
    ]

fm = pqr.fm.FactorModel(
    picking_strategies=pqr.fm.split_quantiles(3),
    allocation_strategy=pqr.EqualWeights(),
    add_wml=True
)
param_grid_mom = {
    "(1, 0, 1)": dynamic_factor_preprocessor_factory(1, 0, 1),
    "(1, 1, 1)": dynamic_factor_preprocessor_factory(1, 1, 1),
    "(3, 0, 3)": dynamic_factor_preprocessor_factory(3, 0, 3),
    "(3, 1, 3)": dynamic_factor_preprocessor_factory(3, 1, 3),
    "(6, 0, 6)": dynamic_factor_preprocessor_factory(6, 0, 6),
    "(6, 1, 6)": dynamic_factor_preprocessor_factory(6, 1, 6),
    "(9, 0, 9)": dynamic_factor_preprocessor_factory(9, 0, 9),
    "(9, 1, 9)": dynamic_factor_preprocessor_factory(9, 1, 9),
    "(12, 0, 12)": dynamic_factor_preprocessor_factory(12, 0, 12),
    "(12, 1, 12)": dynamic_factor_preprocessor_factory(12, 1, 12),
}

mom_raw = pqr.Factor(prices, better="more")

gs_mom = pqr.fm.GridSearch(
    factor_preprocessors=param_grid_mom,
    factor_model=fm
)

gs_mom(
    factor=mom_raw,
    universe=universe,
    target=pqr.metrics.Alpha(benchmark)
).plot.bar(figsize=(10, 10))

plt.title("Alpha for different parameters")
plt.xlabel("Parameters")
plt.ylabel("Alpha")
plt.grid();
param_grid_value = {
    "(1, 0, 1)": mean_factor_preprocessor_factory(1, 0, 1),
    "(1, 1, 1)": mean_factor_preprocessor_factory(1, 1, 1),
    "(3, 0, 3)": mean_factor_preprocessor_factory(3, 0, 3),
    "(3, 1, 3)": mean_factor_preprocessor_factory(3, 1, 3),
    "(6, 0, 6)": mean_factor_preprocessor_factory(6, 0, 6),
    "(6, 1, 6)": mean_factor_preprocessor_factory(6, 1, 6),
    "(9, 0, 9)": mean_factor_preprocessor_factory(9, 0, 9),
    "(9, 1, 9)": mean_factor_preprocessor_factory(9, 1, 9),
    "(12, 0, 12)": mean_factor_preprocessor_factory(12, 0, 12),
    "(12, 1, 12)": mean_factor_preprocessor_factory(12, 1, 12),
}

value_raw = pqr.Factor(pe, better="more")

gs_value = pqr.fm.GridSearch(
    factor_preprocessors=param_grid_value,
    factor_model=fm
)

gs_value(
    factor=value_raw,
    universe=universe,
    target=pqr.metrics.Alpha(benchmark)
).plot.bar(figsize=(10, 10))

plt.title("Alpha for different parameters")
plt.xlabel("Parameters")
plt.ylabel("Alpha")
plt.grid();<jupyter_output>/home/akbabkin/programming/notebooks/venv/lib/python3.8/site-packages/numpy/lib/nanfunctions.py:1395: RuntimeWarning: All-NaN slice encountered
  result = np.apply_along_axis(_nanquantile_1d, axis, a, q,<jupyter_text>По альфе лучшей моментум стратегией с точки зрения WML-портфеля оказалась стратегия (1, 0, 1), т.е. краткосрочный моментум. А среди value-стратегий лучшими параметрами стали (3, 1, 3), т.е. среднесрочный фактор стоимости. Рассмотрим их поподробнее.<jupyter_code>best_mom = pqr.Factor(
    prices,
    better="more",
    preprocessor=dynamic_factor_preprocessor_factory(1, 0, 1)
)
best_value = pqr.Factor(
    pe,
    better="less",
    preprocessor=mean_factor_preprocessor_factory(3, 0, 3)
)

portfolios_mom = fm(best_mom, universe)
portfolios_value = fm(best_value, universe)<jupyter_output><empty_output><jupyter_text>Аналитика  Ликбез по дашбордам Дашборд состоит из 2 видов объектов: графики и таблицы. Что график, что таблица имеют единый интерфейс: принимают на вход последовательность (список) портфелей (pqr.Portfolio), ничего не возвращают, но рисуют график (через matplotlib и plt.show) или таблицу (через Ipython.display). Дашборд может содержать любое количество графиков и таблиц в любом порядке.Непосредственно сами графики и таблицы строятся над конкретными метриками. Различие состоит в том, что таблица строится на основе нескольких числовых метрик, а график - на основе одной timeseries-метрики. С точки зрения интерфейсов числовые (numeric) и timeseries-метрики очень похожи: при вызове считается сырая метрика, не переведенная ни в  какие единицы, но также имеется метод `fancy`, который возвращает "красивый" результат метрики (например, переводит в проценты), также каждая метрика имеет `fancy_name` - красивое имя для таблицы/графика. НО: для числовой метрики результат вызова - число (обычно float) или namedtuple (для некоторых метрик, которые имеют флаг `statistics`), результат `fancy` - строка с округлением до 2 знаков после запятой; а для timeseries-метрики - результат вызова - pd.Series типа float, результат `fancy` - переведенный в проценты, если требуется, pd.Series типа float.Попробуем теорию на практике.<jupyter_code># нарисуем underwater график, который по сути просто считает просадку портфеля во времени
dd = pqr.metrics.Drawdown()
dd(portfolios_mom[0]).plot();<jupyter_output><empty_output><jupyter_text>Получилось неплохо, если дописать еще 5-10 строчек, можно получить красивый график, но если захотим отобразить сразу несколько портфелей для сравнения, то надо добавить еще цикл, внимательно проследить за тем, чтобы не зажевало исходную точку. Если надо добавить бенчмарк, то надо проследить, чтобы бенчмарк начинался с начала нашего инвестирования и т.д. Чтобы это все не делать руками, воспользуемся графиком из библиотеки.<jupyter_code>underwater = pqr.dash.Graph(metric=dd, figsize=(15, 3))

underwater(portfolios_mom)<jupyter_output><empty_output><jupyter_text>Теперь займемся таблицей.<jupyter_code>mini_table = pqr.dash.Table(
    pqr.metrics.SharpeRatio(rf=0),
    pqr.metrics.MeanReturn(statistics=True, annualizer=1), # средняя месячная дох-ть, т.к. аннуалайзер=1
    pqr.metrics.Volatility(annualizer=1), # средняя месячная вол-ть, т.к. аннуалайзер=1
    
    pqr.metrics.MeanExcessReturn(benchmark, statistics=True, annualizer=12),
    pqr.metrics.Alpha(benchmark, statistics=True, annualizer=12),
    pqr.metrics.Beta(benchmark, statistics=False),
)
mini_table(portfolios_mom)<jupyter_output><empty_output><jupyter_text>После этого объединим все в 1 дашборд, добавив предварительно лог-график для накопленной доходности в сравнении с бенчмарком.<jupyter_code>dash = pqr.dash.Dashboard(
    pqr.dash.Graph(metric=pqr.metrics.CompoundedReturns(), log_scale=True, benchmark=benchmark, figsize=(15, 5)),
    underwater,
    mini_table
)<jupyter_output><empty_output><jupyter_text>Все готово: отобразим результаты по лучшим стратегиям.<jupyter_code>dash(portfolios_mom)
dash(portfolios_value)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11542405155998242, 'fraction_numerical': 0.02827010399882818, 'mean_word_length': 5.648490749756572, 'pattern_counts': {'":': 20, '<': 32, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '518749', 'license': 'MIT', 'n_tokens_mistral': 2864, 'n_tokens_neox': 2923, 'n_words': 558, 'path': 'examples/china_momentum_value.ipynb', 'repo_name': 'atomtosov/pqr', 'size': '6827'}