Пример событийной стратегии на свечках


Пример событийной стратегии на свечках
Atom
14.08.2011


Пытаюсь разобраться в том, как работает событийная стратегия на свечках. Насколько я могу понять, со времени выпуска документации механизмы изменились и описанные на форуме и в API .when.do конструкции на CandleToken'ах не работают, а без исходников понять почему - не получается.

Просьба к участникам форума - выложите, пожалуйста, каркас такой стратегии - без логики, можно с принтами в ключевых местах. Буду очень признателен, да и наверное не только я.

Теги:


Спасибо:


1 2  >
Mikhail Sukhov

Фотография
Дата: 14.08.2011
Ответить


Church Перейти
Пытаюсь разобраться в том, как работает событийная стратегия на свечках. Насколько я могу понять, со времени выпуска документации механизмы изменились и описанные на форуме и в API .when.do конструкции на CandleToken'ах не работают, а без исходников понять почему - не получается.


Документация на сайте устарела, да. Но всегда существует оффлайн документация. Читайте ее. Она соответствует последней версии.
Спасибо: Church

Teddy

Фотография
Дата: 14.08.2011
Ответить


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

Mikhail Sukhov

Фотография
Дата: 14.08.2011
Ответить


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


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

Church

Фотография
Дата: 15.08.2011
Ответить


Михаил, спасибо - я как-то забыл про оффлайновый хэлп.

Просим здешних товарищей - выложите, пожалуйста, каркас (без логики) событийного алгоритма, основанного на свечках!
Спасибо:

wakwak

Фотография
Дата: 23.08.2011
Ответить


Church Перейти

Просим здешних товарищей - выложите, пожалуйста, каркас (без логики) событийного алгоритма, основанного на свечках!


+1, было бы архиполезно!
Спасибо:

freelancer

Фотография
Дата: 24.08.2011
Ответить


Что-то вроде этого:

Код
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
using Ecng.Common;
using Ecng.ComponentModel;
using MoreLinq;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Quik;

namespace MyRobot
{
    class MyStrategyAction : Strategy
    {
        private readonly CandleManager _candleManager;
        private readonly TimeSpan _timeFrame;

        private struct CandlesCurrentAndPrev
        {
            public TimeFrameCandle CandlesCurrent;
            public TimeFrameCandle CandlesPrev;
        }

        private CandlesCurrentAndPrev GetCandlesCurrentAndPrev(CandleManager candleManager, TimeSpan timeFrame)
        {
            CandlesCurrentAndPrev c = new CandlesCurrentAndPrev();

            c.CandlesCurrent = candleManager.GetLastTimeFrameCandle(Security, timeFrame);
            c.CandlesPrev = candleManager.GetTimeFrameCandle(Security, timeFrame, c.CandlesCurrent.Time - timeFrame);

            return c;
        }
        
        public MyStrategyAction(CandleManager candleManager, TimeSpan timeFrame)
        {
            _candleManager = candleManager;

            _timeFrame = timeFrame;

            this.OrderFailed += orderFail => AddErrorLog(orderFail.Error.Message, new object[] { });
            this.StopOrderFailed += stopOrderFail => AddErrorLog(stopOrderFail.Error.Message, new object[] { });
        }

        private void action()
        {
            CandlesCurrentAndPrev candlesCurrentAndPrev = GetCandlesCurrentAndPrev(_candleManager, _timeFrame);

            var candleCurrent = candlesCurrentAndPrev.CandlesCurrent;

            if ((candleCurrent.Time - Trader.MarketTime).Duration() > new TimeSpan(0, 0, 20))
                return;

            if (candleCurrent == null)
            {
                AddWarningLog("Текущая свеча = null", new object[] { });
                return;
            }

            var candle = candlesCurrentAndPrev.CandlesPrev;
            
            var candlesFromFile = File.ReadAllLines("RTS_15MIN.txt").Select(line =>
            {
                var parts = line.Split(',');
                var time = DateTime.ParseExact(parts[0] + parts[1], "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
                return new TimeFrameCandle
                {
                    OpenPrice = parts[2].Replace(".00000", "").To<decimal>(),
                    HighPrice = parts[3].Replace(".00000", "").To<decimal>(),
                    LowPrice = parts[4].Replace(".00000", "").To<decimal>(),
                    ClosePrice = parts[5].Replace(".00000", "").To<decimal>(),
                    TimeFrame = _timeFrame,
                    Time = time,
                    TotalVolume = parts[6].To<int>(),
                    Security = this.Security,
                };
            });

            var candles = _candleManager.GetTimeFrameCandles(Security, _timeFrame, 1000).Take(_candleManager.GetTimeFrameCandles(Security, _timeFrame, 1000).Count() - 1);

            candles = candlesFromFile.Concat(candles).DistinctBy(t => t.Time);            

            #region Генерация сигналов

            
            #endregion

            #region Выполнение сигналов

            
            #endregion
        }

        protected override void OnStarting()
        {
            if (_candleManager.Tokens.Count() == 0)
            {
                AddErrorLog("_candleManager.Tokens.Count() = 0", new object[] { });
                return;
            }

            this.
            When(StrategyRuleConditionHelper.NewCandles(_candleManager.Tokens.ElementAt(0))).
            Do(action);

            base.OnStarting();
        }

        protected override void DisposeManaged()
        {
            base.DisposeManaged();
        }
    }
}
Спасибо: wakwak JakeGreen Lisana

Church

Фотография
Дата: 25.08.2011
Ответить


Вот мой пример. Комментарии может быть даже слишком подробные - это те моменты, в которые я утыкался сам.

Код
/*
 * Каркас событийной стратегии на индикаторах
 * 
 * Сверху в стратегию передаются сами индикаторы. Обязанность по прогону индикаторов по истории и по их обновлению ложится на тот код, в котором стратегия создается.
 * Для свечных индюков это событие CandleManager.CandlesFinished.
 * Такой подход позволяет использовать легко использовать разные стратегии, временно останавливать стратегию через .Stop() (например, перед клирингом или новостями) и снова запускать через .Start()
 * 
 */ 

using System;
using System.Collections.Generic;

using StockSharp.Algo;
using StockSharp.Algo.Candles;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;

public class MyStrategy : Strategy
{
    #region Объявление переменных

    //private readonly <Набор индикаторов из StockSharp.Algo.Indicators>

    private int Position = 0;
    private decimal EntrySlip = 0.0m;
    private decimal ExitSlip = 1000.0m;

    /// <summary>
    /// Проскальзывание при входе
    /// </summary>
    /// <remarks>Необходимо задавать при инициализации стратегии, иначе будет равно 0.</remarks>
    public decimal EntrySlippage { set { EntrySlip = value; } get { return EntrySlip; } }
    /// <summary>
    /// Проскальзывание при выходе
    /// </summary>
    /// <remarks>Необходимо задавать при инициализации стратегии, иначе будет равно 1000.</remarks>
    public decimal ExitSlippage { set { ExitSlip = value; } get { return ExitSlip; } }

    // Стопы хранятся в виде переменных, а не стоп-ордеров - для совместимости с тестером
    private decimal LongStopLoss;
    private decimal ShortStopLoss;

    private bool InTrade = false;
    private bool IsExited = false;
    #endregion

    // Конструктор
    public MyStrategy(/*<индикаторы>*/)
    {
        this.Name = "...";
        //<Передача индикаторов в тело стратегии>
    }

    protected override void OnStarting()
    {
        // Подписка на свои трейды - для расчета и обновления позиции
        this.NewMyTrades += RecalculatePosition;

        // Если до запуска стратегии не был переопределен объем, объем будет 1.
        if (this.Volume == 0) this.Volume = 1;

        // Подписка на новые тики - для осуществления основных действий
        this
            .When(Security.SecurityNewTrades())
            .Do(Process);
                    
        // Если основные действия производятся по окончанию свечек, то придется передавать в стратегию CandleToken и CandleManager
        // и подписываться на события CandleManager.CandlesFinished или создавать правила на CandleToken.CandlesFinished

        base.OnStarting();
    }
    protected override void OnStopping()
    {
        // Отписываемся от событий
        this.Rules.Remove(Security.SecurityNewTrades());
        this.NewMyTrades -= RecalculatePosition;

        // Ликвидируем позицию
        if (Position > 0)
        {
            var order = CreateOrder(OrderDirections.Sell, this.Security.BestAsk.Price - ExitSlip, Position);
            RegisterOrder(order);
        }
        else if (Position < 0)
        {
            var order = CreateOrder(OrderDirections.Buy, this.Security.BestAsk.Price + ExitSlip, -Position);
            RegisterOrder(order);
        }

        base.OnStopping();
    }


    // Расчет позиции
    /// <summary>
    /// Пересчет позиции при новых собственных сделках
    /// </summary>
    private void RecalculatePosition(IEnumerable<MyTrade> trades)
    {
        foreach (var trade in trades)
        {
            if (trade.Order.Direction == OrderDirections.Buy)
                Position += trade.Trade.Volume;
            if (trade.Order.Direction == OrderDirections.Sell)
                Position -= trade.Trade.Volume;
        }
    }


    // Главный обработчик (изменение котировок). Если сигналы должны генерироваться только по окончанию свечки, то этот код надо переместить в метод, 
    // который вызывается в ответ на событие CandleManager.CandleFinished.
    /// <summary>
    /// Центральный метод - обработка новых тиков
    /// </summary>
    /// <remarks>Проверка условий входа/выхода с учетом текущей позиции; Генерация сигналов.</remarks>
    private void Process()
    {
        if (!IsExited) // а вдруг уже выходим, но позиция еще не успела пересчитаться?
        {
            if (ExitLongSignal() && (Position > 0))
                ExitLong(this.Security.BestBid.Price);

            if (ExitShortSignal() && (Position < 0))
                ExitShort(this.Security.BestAsk.Price);
        }

        if (!InTrade) // не более одного входа на свечке
        {
            if (GoLongSignal() && (Position <= 0))
                GoLong(this.Security.BestAsk.Price);

            if (GoShortSignal() && (Position >= 0))
                GoShort(this.Security.BestBid.Price);
        }
    }

    // Логика - проверка условий входа/выхода

    // Идти ли в лонг на текущем тике? 
    private bool GoLongSignal()
    {
        if (this.Security.LastTrade != null)
            return (/*логическое условие входа в лонг, например ema1.Value>ema2.Value*/);
        else
            return false;
    }
    private bool GoShortSignal()
    {
        if (this.Security.LastTrade != null)
            return (/*логическое условие входа в шорт, например ema1.Value<ema2.Value*/);
        else
            return false;
    }
    private bool ExitLongSignal()
    {
        if (this.Security.LastTrade != null)
            return ((this.Security.LastTrade.Price < LongStopLoss) || (/*логическое условие для выхода из лонга*/));
        else
            return false;
    }
    private bool ExitShortSignal()
    {
        if (this.Security.LastTrade != null)
            return ((this.Security.LastTrade.Price > ShortStopLoss) || (/*логическое условие для выхода из шорта*/));
        else
            return false;
    }

    // Исполнение
    private void GoLong(decimal Price)
    {
        var order = CreateOrder(OrderDirections.Buy, this.Security.BestAsk.Price + EntrySlip, this.Volume); // настроить по вкусу :)
        order.ExecutionCondition = OrderExecutionConditions.FillOrCancel; 
        RegisterOrder(order);
        LongStopLoss = CalcStopLoss(OrderDirections.Buy, Price);
        this.AddInfoLog("=> ENTER LONG.");
        InTrade = true;
        IsExited = false;
    }
    private void GoShort(decimal Price)
    {
        var order = CreateOrder(OrderDirections.Sell, this.Security.BestBid.Price - EntrySlip, this.Volume);
        order.ExecutionCondition = OrderExecutionConditions.FillOrCancel;
        RegisterOrder(order);
        ShortStopLoss = CalcStopLoss(OrderDirections.Sell, Price);
        this.AddInfoLog("=> ENTER SHORT.");
        InTrade = true;
        IsExited = false;
    }
    private void ExitLong(decimal Price)
    {
        var order = CreateOrder(OrderDirections.Sell, this.Security.BestBid.Price - ExitSlip, this.Volume);
        order.ExecutionCondition = OrderExecutionConditions.FillOrCancel;
        RegisterOrder(order);
        this.AddInfoLog("<= EXIT LONG.");
        InTrade = false;
        IsExited = true;
    }
    private void ExitShort(decimal Price)
    {
        var order = CreateOrder(OrderDirections.Buy, this.Security.BestAsk.Price + ExitSlip, this.Volume);
        RegisterOrder(order);
        this.AddInfoLog("<= EXIT SHORT.");
        InTrade = false;
        IsExited = true;
    }

    // Расчет изначального стопа
    /// <summary>
    /// Расчет изначального стопа на основе ATR
    /// </summary>
    private decimal CalcStopLoss(OrderDirections direct, decimal tradePrice)
    {
        return /*расчитанный тем или иным образом стоплосс*/
    }

    /// <summary>
    /// Сохраняет в лог отчет о текущем состоянии важнейших параметров стратегии.
    /// </summary>
    public void PrintStrategyState()
    {
        this.AddInfoLog(" –––––––––––– [Status report] –––––––––––– ");
        this.AddInfoLog("|  Current P/L = " + this.PnLManager.PnL.ToString());
        this.AddInfoLog("|  Position = {0}", Position);
        // Добавить инфы по вкусу.
        this.AddInfoLog(" –––––––––––––– [End report] –––––––––––––– ");
    }
}
Спасибо: wakwak KAX JakeGreen Pavel-NS Lisana Jeta Кот Матроскин Роман

Mikhail Sukhov

Фотография
Дата: 25.08.2011
Ответить


Полезно своими наработками делиться с товарищем по цеху. Вы привели свой код, а я увидел в нем неточности. Ловите бесплатные напутствия [laugh]:


  1. Security.SecurityNewTrades() каждый раз создает новое правило. Первый раз вы делаете все правильно, добавляя его в свою коллекцию. Второй раз в OnStopping вы не удаляете старой правило, а пытаетесь удалить свеже созданное. Ошибки в этом случае не возникает. Поэтому вы и не находите эту ошибку.
  2. Правила, если им не делать критерий остановки, по умолчанию останавливаются сами и удаляются так же из коллекции правил. Нет необходимости это делать принудительно.
  3. Закрытие позиции лучше так же делать через правила:
    Код
    
    this.When(this.Stopping())
    .ClosePosition();
    

    или
    Код
    
    this.When(this.Stopping())
    .Do(ClosePositionHandler);
    

  4. RecalculatePosition == TraderHelper.GetPosition
  5. Метод Process напрашивается сам на событийных подход


Как совет, посмотрите, какие методы присутствуют в TraderHelper + StrategyRuleConditionHelper.
Спасибо: Church

Mikhail Sukhov

Фотография
Дата: 25.08.2011
Ответить


Аналогичные советы, что я дал Church


  1.  
    Код
    this.
                When(StrategyRuleConditionHelper.NewCandles(_candleManager.Tokens.ElementAt(0))).
                Do(action);

    Заменяется на более элегантное:
    Код
    this.
                When(_candleManager.Tokens.ElementAt(0).NewCandles()). // лучше токен вообще хранить как свойство стратегии
                Do(action);

    А еще лучше передавать новые свечки в обработчик:
    Код
    this.
                When(_candleManager.Tokens.ElementAt(0).NewCandles()). // лучше токен вообще хранить как свойство стратегии
                Do<IEnumerable<Candle>>(action);

  2. GetCandlesCurrentAndPrev - Посмотрите на метод ICandleContainer.GetCandle(index)
  3. action. Писать в стратегии парсинг файла плохой стиль. Считайте файл где-то еще, и передавайте в стратегию уже полученные данные.

  4. Код
    AddErrorLog(orderFail.Error.Message, new object[] { });

    вот так тоже будет работать:
    Код
    AddErrorLog(orderFail.Error.Message);


Спасибо:

Mikhail Sukhov

Фотография
Дата: 25.08.2011
Ответить


Хотите получать такие советы регулярно? Прочитайте надпись и спросите меня, чем вы можете помочь проекту. Помогая проекту, проект в лице людей с голубыми никами будет помогать и вам.[cool]
Спасибо:
1 2  >

Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy