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


Пример событийной стратегии на свечках
Atom
8/14/2011


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

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

Tags:


Thanks:


1 2  >
Mikhail Sukhov

Avatar
Date: 8/14/2011
Reply


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


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

Teddy

Avatar
Date: 8/14/2011
Reply


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

Mikhail Sukhov

Avatar
Date: 8/14/2011
Reply


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


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

Church

Avatar
Date: 8/15/2011
Reply


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

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

wakwak

Avatar
Date: 8/23/2011
Reply


Church Go to

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


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

freelancer

Avatar
Date: 8/24/2011
Reply


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

Code
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();
        }
    }
}
Thanks: wakwak JakeGreen Lisana

Church

Avatar
Date: 8/25/2011
Reply


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

Code
/*
 * Каркас событийной стратегии на индикаторах
 * 
 * Сверху в стратегию передаются сами индикаторы. Обязанность по прогону индикаторов по истории и по их обновлению ложится на тот код, в котором стратегия создается.
 * Для свечных индюков это событие 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] –––––––––––––– ");
    }
}
Thanks: wakwak KAX JakeGreen Pavel-NS Lisana Jeta Кот Матроскин Роман

Mikhail Sukhov

Avatar
Date: 8/25/2011
Reply


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


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

    или
    Code
    
    this.When(this.Stopping())
    .Do(ClosePositionHandler);
    

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


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

Mikhail Sukhov

Avatar
Date: 8/25/2011
Reply


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


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

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

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

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

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

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


Thanks:

Mikhail Sukhov

Avatar
Date: 8/25/2011
Reply


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

Attach files by dragging & dropping, , or pasting from the clipboard.

loading
clippy