Финансовая лаборатория

Биржевая торговля и торговые роботы


Просматривая старые посты блога "Финансовая лаборатория" я заметил, что за мной имеется должок. Примерно в апреле 2011 года я начал рассказывать о торговой системе HighLowLong в качестве примера того, как нужно создавать торговую систему с помощью WealthScript и языка C#. Схему показал, основные блоки кода тоже показал и даже пообещал в дальнейшем выложить весь код этой системы. Ну как говорится, лучше поздно, чем никогда. Поэтому сегодня выполню обещание.

Итак, система очень проста по своей сути и как я писал, вряд ли можно торговать эту систему без дополнительных доработок. Однако для того, чтобы понять - как системы строятся - это буквально идеальный вариант. Тем более я специально (для простоты) оставил только правила входа в длинную позицию и выхода из этой позиции. Если Вы захотите, можете дописать код, чтобы получилась система, которая могла бы торговать и в шорт.

Данная система является однопозиционной.  Здесь же еще раз упомяну, что однопозиционная, или SP (Single-Position Strategy) стратегия это такая стратегия, которая в единицу времени для одного финансового инструмента может иметь не более одной позиции. О том, как выглядит шаблон однопозиционной торговой стратегия я уже рассказывал.

Однако, здесь я использовал не встроенный по умолчанию в программе Wealth-Lab шаблон кода торговой системы, а шаблон, который использует для своих систем Игорь Чечет. Этот шаблон мне показался очень удобным. Плюс к тому, его можно использовать при построении генератора торговых стратегий, но об этом я скажу чуть позднее.

Вход в лонг осуществляется по стоп ордеру при пробитии сверху вниз уровня, являющимся максимумом цен финансового инструмента за оптимизируемый период. Выход из длинной позиции осуществляется также при пробитии уровня. Однако уровень этот определяется как минимумы цен за определенный период (этот период тоже оптимизируется). И как Вы уже поняли, этот уровень должен пробиваться сверху вниз, чтобы используя стоп-приказ можно было выйти из позиции.

В общем, система получилась простая, элегантная, и как это не странно, показывает приемлемые результаты даже без предварительной доработки.

Пример кода торговой системы:

Пример (как выполнить пример приведенного кода)...


// Автор: Власов Дмитрий Викторович (http://finlabportal.ru)
// Генератор сигналов на вход: BreakOut
// Направление следования сигналам: Forward
// Фильтр тренда: None
// Фильтр флета: None
// Временной фильтр: None
// Технология выхода: BreakOut

using System;
using System.Drawing;
using WealthLab;
using WealthLab.Indicators;

namespace VDV.Strategies
{
    class HL_Long : WealthScript
    {

        #region Объявление параметров торговой системы

        StrategyParameter _buyPeriod; // Период для рассчета HighestHigh для входа в  позицию(+):
        StrategyParameter _sellPeriod; // Период для рассчета LowestLow для выхода из позиции(-):

        #endregion

        #region Инициализация параметров торговой системы

        public HL_Long()
        {
            _buyPeriod = CreateParameter("HighPeriod", 9, 1, 300, 1);
            _sellPeriod = CreateParameter("LowPeriod", 5, 1, 300, 1);
        }

        #endregion

        protected override void Execute()
        {
            int firstValidValue = 1; // Первое значение свечки, при котором существуют все индикаторы

            #region Индикаторы (DataSeries)

            //		double stopPercent = _stopPercent.Value;

            // Линия для входа:
            int buyPeriod = _buyPeriod.ValueInt;

            DataSeries LineToBuy = Highest.Series(High, buyPeriod) >> 1;

            firstValidValue = Math.Max(firstValidValue, LineToBuy.FirstValidValue + 1);
          //  PlotSeries(PricePane, LineToBuy, Color.Green, LineStyle.Solid, 2);

            //Линия для выхода:
            int sellPeriod = _sellPeriod.ValueInt;

            DataSeries LineToSell = Lowest.Series(Low, sellPeriod) >> 1;

            firstValidValue = Math.Max(firstValidValue, LineToSell.FirstValidValue + 1);
            //		PlotSeries(PricePane, LineToSell, Color.Red, LineStyle.Solid, 2);

            #endregion

            #region Переменные для обслуживания позиции

            bool signalBuy = false, signalShort = false; // Сигналы на вход в длинную и короткую позиции

            #endregion

            #region Представление цен с необходимой разрядностью для отображения ордеров

            string PricePattern = "0.";
            for (int i = 0; i < this.Bars.SymbolInfo.Decimals; i++)
                PricePattern += "0";

            #endregion

            PlotStops(); // Отображать уровни, на которых были попытки выхода по S/L

            for (int bar = firstValidValue; bar < Bars.Count; bar++) // Пробегаемся по всем свечкам
            {
                #region Сигналы на вход в позицию и выход из нее

                signalBuy = CrossOver(bar, High, LineToBuy);
                signalBuy = signalBuy && (!IsLastPositionActive || LastActivePosition.PositionType == PositionType.Short); // генерируется когда не в длинной позиции

                signalShort = CrossUnder(bar, Low, LineToSell);
                signalShort = signalShort && (!IsLastPositionActive || LastActivePosition.PositionType == PositionType.Long); // генерируется когда не в короткой позиции

                #endregion

                #region Сопровождение и выход из позиции по Take Profit

                if (!IsLastPositionActive) // Если позиции нет
                {
                    if (signalBuy) // При получении сигнала на вход в длинную позицию
                    {
                        RiskStopLevel = LineToSell[bar]; //рассчитываем величину начального стопа
                        BuyAtStop(bar, LineToBuy[bar]); //покупаем при пересечении верхнего уровня
                    }

                }
                else // Если позиция есть
                {
                    if (LastActivePosition.PositionType == PositionType.Long) // Для длинной позиции
                    {
                        SellAtStop(bar, LastActivePosition, LineToSell[bar]); // Попробовать выйти по S/L
                    }
                }

                #endregion

            }
        }
    }
}

В этот раз для тестирования будем брать акции с таймфреймом в один час. Настроим величину комиссии в тестах равной 0,1% от оборота. Такая большая величина комиссии была взята специально, чтобы подобрать параметры таким образом, чтобы они были не чувствительны к проскальзыванию и комиссиям.

В качестве первой бумаги выберем Северсталь - как и в прошлый раз. Тестировать будем за последние 5 лет, чтобы было видно, как система вела себя во время кризиса 2008 года. Попробуем подобрать оптимальные параметры. Оптимизируемым параметром здесь является длина периода, причем это разные периода для входа в позицию и для выхода из нее.

Перебираем значения от 1 до 300, значит общее количество вариантов будет 90000 штук. Если использовать в качестве метода оптимизации полный перебор (Exhaustive), то программе понадобится на это 9 часов. О предполагаемом времени мы можем узнать, если нажмем на кнопку "Оценить ("Estimate"). Конечно же для нас такое время для оптимизации неприемлемо.

Оптимизация

Оптимизация методом полного перебора

К счастью, у программы Wealth-Lab есть метод оптимизации, который позволит найти оптимальные параметры (или очень близкие к ним) не за 9 часов, а буквально за несколько минут. Это метод генетической оптимизации. Им и воспользуемся. Еще одним плюсом метода генетической оптимизации является то, что можно выбрать параметр, по которому можно оптимизировать торговую стратегию. Мне больше всего нравится проводить оптимизацию по показателю "Recovery Factor". Этот показатель отражает отношение валового дохода к максимальной просадке. В результате максимизации этого показателя эквити у нас получается без резких провалов, чего мы и добиваемся.

Генетическая оптимизация

Настройки генетической оптимизации в Wealth-Lab

Нажав на кнопку "Begin Optimization" мы видим, что программе для подбора оптимальных параметров понадобится всего лишь 5 минут. Ждем эти 5 минут и узнаем, что лучшими параметрами для инструмента CHMF (1 час таймфрейм) за период 4 года будет:

для верхней линии: 28
для нижней линии: 25

Теперь посмотрим, как выглядит эквити при условии, если мы входим в позицию на 100% от эквити:

Эквити стратегии

Эквити стратегии при входе на 100% от наличных средств

Еще более важный график - это график просадок.

График просадок

Исследование просадок

Анализируя этот график можно сделать вывод, что для достижения нового максимума никогда не тратилось более 1400 часов. Это составляет около 140 торговых дней. Кроме того, можно увидеть, что максимальная просадка равна 20 %.

Теперь посмотрим, какой средний доход эта стратегия приносила бы нам ежемесячно.

Ежемесячная отдача стратегии

Отдача стратегии в месяц

Как видим, средний доход в месяц за исследуемые 4 года равняется 4,89% - что очень даже неплохо для торговли без плечей. При этом мы должны быть готовы к тому, что 6 месяцев подряд система может не давать дохода. При этом 60% месяцев будем заканчивать в плюсе, причем наибольший доход за один месяц составил 44,5%. В общем, можете сами внимательно рассмотреть все показатели.

Теперь посмотрим как выглядит сам график стратегии:

График

График торговой стратегии

Я не зря публикую этот график последним, т.к. он несет меньше всего информации.

В общем и целом можно сказать, что это вполне нормальная трендследящая система, которая сливает или не зарабатывает на боковике, но затем при наступлении движухи начинает отбивать потерянное и зарабатывать.

Еще раз подчеркиваю, что эту систему просто привел как удобный и наглядный пример, позволяющий новичкам в программировании на C# разобраться с удобным шаблоном, который поможет самостоятельно делать код торговых систем. В следующих постах также буду публиковать программные коды новых торговых систем (поэтому не забудьте подписаться на RSS обновления блога). Однако хочу сразу сказать, что для того, чтобы иметь возможность строить торговые системы недостаточно будет иметь коллекцию готовых систем.

Для этой цели нужно иметь в своем распоряжении заготовки, т.е. существенные части разнообразных торговых систем, которые уже были проанализированы и код которых уже выверен. Такими частями на первом этапе у нас будут правила сопровождения позиции (в данном случае - это выход при прорыве уровня) и правила входа в позицию. В дальнейшем сюда добавятся фильтры, выходы по времени и некоторые другие.

В результате работа с торговыми системами правратится в классический метод анализа и синтеза. С помощью анализа мы разбиваем любую встречающуюся нам торговую систему на составные части (выделяем правила сопровождения, правила входа, фильтры, и т.п.). Затем, с помощью синтеза мы по существующим шаблонам торгового кода синтезируем новые торговые системы. Причем таких систем у нас может быть огромное множество. Например правила сопровождения №1, и вместе с этими правилами пробуем правила входа №1, №2, №3. В этоге у нас будет 3 совершенно разные торговые системы.

На сегодня все. Кто хочет быстрее разобраться с программированием на C# - рекомендую ознакомится с инструкцией по программированию торговых стратегий на С# на русском языке.

1 комментарий

  1. Antrax пишет:

    Добрый день.
    Анализируя пример стратегии, столкнулся с такой проблемой:если убрать сдвиг в обоих DataSeries “>> 1″, тогда пример на любых инструментах перестает генерировать сделки вообще, а почему это происходит непонятно…

Оставить комментарий