Секреты TSLab | Торговые роботы | События
1 Май

Вход и выход на одной свече. Как?

Часто пользователи программы TSLab спрашивают: “Как можно войти в позицию и выйти из позиции в течение одной свечи?”. Другой вопрос, который часто задается: “Как выставить стоп на свече входа?”. Данный вопрос повторяется снова и снова и, вероятно, будет повторяться много раз в будущем. Так как в TSLab инструкция по этим вопросам отсутствует, разберем в статье эти моменты на примерах.

Вход и выход на одной свече. Как?

Наша задача состоит в том, чтобы совершить вход и выход на одной свече не дожидаясь начала следующей свечи. Таким образом, мы можем создавать более гибкие скрипты TSLab. Решить задачу можно одним из двух возможных способов о которых все, в общем, знают. Но практических примеров уж слишком мало и все равно остается непонимание механизма.

  1. Использовать сжатие/разжатие в скрипте.
  2. Использовать пересчет пок/прод, пок/прод(без объема), сделка.

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

Второй вариант просто использует один из пересчетов, позволяющих скрипту часто запускаться и как результат, выход из позиции производится без задержек. Тестировать такой вариант затруднительно, так как в режиме тестирования мы всегда работаем с пересчетом “интервал” и не получится имитировать несколько пересчетов на одной свече. Так же, тут имеется две возможные реализации скрипта:

  • Только один вход и выход на 1 свече.
  • Многократные входы и выходы на одной свече.

Работать, как обычно, будем с применением TSLab API, но даже пользователи визуального редактора найдут здесь для себя полезную информацию.

Используем сжатие и разжатие.

Писать скрипт, показывающий что выйти на свече входа без использования одного из перечисленных способов невозможно, не будем. Это итак всем известно. Сразу накидаем небольшой скрипт реализующий следующую логику: если свеча белая, входим в лонг и если за 1 минуту хай белой свечи не будет пробит, выходим по рынку. Если пробит то выходим через 2 минуты. Базовый таймфрейм 1 мин. Рабочий таймфрейм 5 мин. То есть расчеты входа и хаёв ведем по пятиминутным свечкам, а сигналы выхода будут с лагом в 1 минуту. Ведь чтобы мы могли реагировать на события раз в минуту нам необходимо базовый таймфрейм выбрать в 1 минуту либо чаще, чтобы скрипт пересчитывался чаще чем раз в 5 минут.

using System;
using TSLab.DataSource;
using TSLab.Script;
using TSLab.Script.Handlers;

namespace test
{
    public class OneCandleEntExCompress : IExternalScript
    {
        public void Execute(IContext ctx, ISecurity sec)
        {
            if (sec.IntervalInstance != new Interval(1, DataIntervals.MINUTE))
                throw new Exception("Будем работать только на 1 минутках.");

            var compressed = sec.CompressTo(new Interval(5, DataIntervals.MINUTE));
            var cSignal = new double[compressed.Bars.Count];
            var cPrevHigh = new double[compressed.Bars.Count];
            for (int i = 0; i < compressed.Bars.Count; i++)
            {
                cSignal[i] = compressed.Bars[i].Close > compressed.Bars[i].Open ? 1 : 0;
                cPrevHigh[i] = compressed.Bars[i].High;
            }

            var signal = compressed.Decompress(cSignal);
            var prevHigh = compressed.Decompress(cPrevHigh);

            for (int i = 10; i < ctx.BarsCount; i++)
            {
                var le = sec.Positions.GetLastActiveForSignal("LE", i);
                if (le == null)
                {
                    if (signal[i] > 0)
                        sec.Positions.BuyAtMarket(i+1, 1, "LE");
                }
                else
                {
                    if (i == le.EntryBarNum && sec.HighPrices[i] < prevHigh[i])
                        le.CloseAtMarket(i + 1, "LXM");
                    else if (i == le.EntryBarNum+2)
                        le.CloseAtMarket(i + 1, "LXP");
                }
            }

            if (ctx.IsOptimization)
                return;

            var color = new Color(System.Drawing.Color.Black.ToArgb());
            var pane = ctx.CreatePane(sec.Symbol, 100, false);
            pane.AddList(sec.Symbol, sec, CandleStyles.BAR_CANDLE, color, PaneSides.RIGHT);
            var gl = pane.AddList("PrevHigh", prevHigh, ListStyles.LINE, color, LineStyles.DOT, PaneSides.RIGHT);
            gl.Thickness = 3;

            pane = ctx.CreatePane(sec.Symbol, 50, false);
            gl = pane.AddList(compressed.Symbol, compressed, CandleStyles.BAR_CANDLE, color, PaneSides.RIGHT);
            gl.Thickness = 3;
        }
    }
}

В результате работы скрипта картинка получится вот такая:

вход и выход на одной свече. как?

Рисунок 1. Вход и выход в течение одной свечи.

По рисунку видно, как отрабатывают входы и выходы. Так же, можно многократно входить и выходить в течение одной свечи рабочего таймфрейма, что мы и наблюдаем в правой части графика. Так как хай не пробивается, мы выходим и снова входим в позицию в течение одной 5 минутной свечки. Главная особенность в том, что лаг всегда равен базовому таймфрейму. Мы не можем совершать операции чаще чем происходит пересчет скрипта, отсюда реакция скрипта будет тоже раз в минуту.

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

Используем пересчет пок/прод

Как уже было сказано выше, такой способ работы лишает нас возможности протестировать скрипт на исторических данных. Тестироваться то скрипт будет, но вот вход и выход на одной свече мы получить не сможем в тесте без модификации кода. Увидим его только в режиме Агента. Для анализа такого способа мы используем такой же скрипт что и ранее, только оптимизируем под пересчет “пок/прод”.

using System;
using RusAlgo.Helper;
using TSLab.DataSource;
using TSLab.Script;
using TSLab.Script.Handlers;

namespace test
{
    public class OneCandleEntExBuySell : IExternalScript
    {
        public void Execute(IContext ctx, ISecurity sec)
        {
            if (sec.IntervalInstance != new Interval(1, DataIntervals.MINUTE))
                throw new Exception("Будем работать только на 5 минутках.");

            var lastIndex = ctx.BarsCount - 1;
            var prevBar = sec.Bars[ctx.BarsCount - 2];
            var le = sec.Positions.GetLastActiveForSignal("LE", lastIndex);
            if (le == null)
            {
                if (sec.Bars[lastIndex - 1].IsWhite())
                {
                    // эмулируем лимиткой рыночную заявку
                    var price = prevBar.High + (prevBar.High - prevBar.Low);
                    sec.Positions.BuyAtPrice(lastIndex + 1, 1, price, "LE");
                }
            }
            else
            {
                var price = prevBar.Low - (prevBar.High - prevBar.Low);
                var prevHigh = sec.Bars[le.EntryBarNum - 1].High;
                var barsPassed = lastIndex - le.EntryBarNum;
                // если от входа прошли до конца свечки
                if (barsPassed == 1 && le.EntryBar.High < prevHigh)
                    le.CloseAtPrice(lastIndex + 1, price, "LXM");
                else if (barsPassed == 3)
                    le.CloseAtPrice(lastIndex + 1, price, "LXP");
            }

            if (ctx.IsOptimization)
                return;

            var color = new Color(System.Drawing.Color.Black.ToArgb());
            var pane = ctx.CreatePane(sec.Symbol, 100, false);
            pane.AddList(sec.Symbol, sec, CandleStyles.BAR_CANDLE, color, PaneSides.RIGHT);
        }
    }
}

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

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

Главная наша опора, это номер бара на котором вы совершили вход в позицию. От этого номера мы отталкиваемся для анализа пробития хая и отсчета времени с момента входа. Это вполне нормальная и работающая схема, и есть скрипты которые только так и можно сделать.

ВАЖНО: Если вы отказываетесь от торгового цикла, то получаете профиты и лоси. Профит  таких скриптов в значительно большей  скорости выполнения. У нас нет торгового цикла, мы не плодим кучу виртуальных входов и прочего ненужного хлама. У нас нет страшного сжатия свечек. Все максимально компактно. Лось таких скриптов в невозможности тестирования и потере отрисовки линий стопа и тейка на графике агента (ну если они в вашем скрипте конечно  имеются). Так же при создании таких скриптов нужно иметь больше  понимания  принципов работы TSLab.

вход и выход на одной свече. как?

Рисунок 2. Результаты работы скрипта в режиме лаборатории.

Если вы запустите скрипт в режиме Агента, то картинка не будет отличаться от обычной картинки скрипта с пересчетом “интервал”. Отличие будет только в том, что скрипт будет пересчитываться постоянно и выход из позиции будет происходить на той же свече на которой был вход. А вот сигнал выхода будет проставлен на следующей свече, будто мы работаем по интервалу, а не по “пок/прод”. Такова особенность TSLab.

вход и выход на одной свече. Как?

Рисунок 3. Вход и выход в течение одной свечи с пересчетом “пок/прод”.

Внешний вид картинки не отличим от обычного скрипта с пересчетом “интервал”. Все из за того что сигнал выхода ставится только на след свечу, хотя по факту мы уже давно вышли.

Важной отличительной особенностью такой реализации, является невозможность многократно входить и выходить на одной свече. Это связано с тем, что метод

var le = sec.Positions.GetLastActiveForSignal("LE", lastBar);

будет возвращать активную позицию для свечи lastBar даже если позиция уже в реале будет закрыта. То есть, для свечи на которой был совершен вход в позицию, данный метод всегда будет возвращать позицию с флагом Active. Такова особенность данного метода. Но выйти из позиции открытой где-то ранее и потом снова в нее войти на одной свечке вполне можно, об это четко сигнализирует на рисунке 2 вход и выход на одной свече справа. Если есть непреодолимое желание совершать многократные входы и выходы на одной свече, тогда нужно использовать альтернативный метод, о котором ниже.

Еще в процессе работы скрипта, если у вас версия TSLab не старше чем 1.2.13.0, вам придется лицезреть следующую ошибку:

18:53:12.95 133 Агент 'СкриптПокПродСтатья': Условная заявка по сигналу 'LXP' может не сработать, т.к. текущая цена на рынке 71,8 (Цена условия 71,75)!

Это просто небольшой баг. Он был исправлен сразу же после отправки оного в техническую поддержку. Смысл сообщения в том, что цена заявки ниже рынка и нам выводят сообщение как будто у нас условная заявка. А мы ведь работаем лимиткой.

Альтернативный метод для пересчета пок/прод

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

В качестве скрипта, рассмотрим простой скрипт входа и выхода, без каких либо условий. Главная наша задача сформировать многократные входы и выходы на одной свече.

using TSLab.Script;
using TSLab.Script.Handlers;

namespace test
{
    public class OneCandleEnterExit : IExternalScript
    {
        public void Execute(IContext ctx, ISecurity sec)
        {
            IPosition le = sec.Positions.GetLastActiveForSignal("LE");
            var prevBar = sec.Bars[ctx.BarsCount - 2];
            if (le == null)
            {
                // лимиткой эмулируем рыночную заявку
                var price = prevBar.High + (prevBar.High - prevBar.Low);
                sec.Positions.BuyAtPrice(ctx.BarsCount, 1, price, "LE");
            }
            else
            {
                var price = prevBar.Low - (prevBar.High - prevBar.Low);
                le.CloseAtPrice(ctx.BarsCount, price, "LX");
            }
        }
    }
}

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

вход и выход на одной свече. как?

Рисунок 3. Вход и выход в течение одной свечи с пересчетом “пок/прод” альтернативным способом.

Как видно, понять что происходит достаточно сложно. Куча сигналов сбивается в один большой столбец и разобрать эту массу нереально. Так что, понимать придется используя таблицу сделок и шаманский бубен. И самое страшное: все происходит нереально быстро. За одну секунду может быть 3-9 входов и более.

Выводы

На текущий момент совершить вход и выход на одной свече можно двумя способами. Наиболее оптимальный и универсальный способ – использовать сжатие в больший таймфрейм. Данный способ позволяет не только проводить тестирование скрипта на  исторических данных, но и не требует модификации скрипта для запуска в режиме агента. Помимо перечисленных плюсов, такой способ достаточно понятен, реализация алгоритма выходит прозрачная и привычная.

Второй способ через пересчет “пок/прод” может пригодиться вам в определенных случаях. Используя его можно создавать гораздо более скоростные и легкие скрипты с минимальным временем реакции на изменения рыночной информации (то есть HFT).


comments powered by HyperComments

Илья
2015-08-31 07:34:03
Спасибо, интересная статья. Вопрос про суть проблемы: Т.е. Execute(IContext ctx, ISecurity sec) запускается каждый раз при появлении нового бара рабочего ТФ и поэтому поза var le = sec.Positions.GetLastActiveForSignal("LE", i); появится только через бар?
ra81
2015-09-22 10:00:00
если пересчет у вас Интервал, то да, вы узнаете о позиции только через бар. Если позиция в реале сформируется. Иначе вы будете видеть виртуальную позицию, которая должна была бы быть на этом баре.
Диана
2017-01-02 11:08:32
Секс знакомства http://bit.ly/2hkF2s9
14
Июл
2017

Доверительное управление. Результаты в июне 2017 года.

Доверительное управление. Результаты в июне 2017 года. Июнь индекс РТС вновь провел преимущественно в боковых движениях, а… »

11
Июн
2017

Доверительное управление. Результаты в мае 2017 года.

Доверительное управление. Результаты в мае 2017 года. В мае “болтанка” индекса РТС продолжилась, на паре… »

7
Май
2017

Доверительное управление. Результаты в апреле 2017 года.

Доверительное управление. Результаты в апреле 2017 года. В апреле мы наблюдали очередной месяц “боковика” по… »

2
Апр
2017

Доверительное управление. Результаты в марте 2017 года.

Доверительное управление. Результаты в марте 2017 года. В марте волатильность на рынке несущественно выросла. Все… »

7
Мар
2017

Доверительное управление. Результаты в феврале 2017 года.

Доверительное управление. Результаты в феврале 2017 года. Февраль был самым коротким торговым месяцем, к тому же… »