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


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


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

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

Теги:


Спасибо:


< 1 2 
Church

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


Михаил, спасибо! TraderHelper оказался очень полезным классом. :)

Я с радостью как-нибудь поучаствую в проекте, только одна проблема - у меня всего три недели опыта серьезного программирования.

Если позволите, пара вопросов.

1. Как переделать Process под событийную модель, вроде и так уже событийно до тиков. :)
Сделать делать одноразовые правила типа LastTradePriceLess/More и обновлять на свечках? А в чем будет преимущество перед Process - высвобождение ресурсов?

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

Church

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


Кстати, this.When(this.Stopping()).ClosePosition() у меня не работает, а ClosePositionHandler я не могу найти даже через хэлп.
Спасибо:

Mikhail Sukhov

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


Church Перейти
Кстати, this.When(this.Stopping()).ClosePosition() у меня не работает


Чуть подробнее.

Church Перейти
а ClosePositionHandler я не могу найти даже через хэлп.


Конечно нет. Это должен быть обработчик в вашем коде.
Спасибо:

Church

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


Сделал эту запись в конструкторе, оверрайд OnStopping удалил. При остановке стратгии при отрытой позиции она не закрывается. PositionManager.Position показывает отличное от нуля число, как и таблица в квике. Кроме удаления OnStopping все аналогично коду выше.
Спасибо:

OvcharenkoVI

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


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

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


Очень полезный пример, наглядная реализация стопов без использования StopLossStrategy помогла
Спасибо: JakeGreen

risty

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


Есть вопрос про свечки, надеюсь в тему.
Допустим у нас есть какое-то NewMyTrade.Time
Я хочу получить свечку с:
Код
candle.Time = NewMyTrade.Time - new TimeSpan (0,5,11);

и таймфреймом
Код
candle.Timeframe = new TimeSpan (0,5,11);


т.е. получить свечку от произвольного момента в прошлом(для примера взято 5:11) до NewMyTrade.Time, причем при каждом новом NewMyTrade.Time произвольный момент в прошлом меняется.

Как это элегантнее всего сделать ?
Спасибо:

risty

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


И ещё вопрос про IsCandleBullishOrBearish()
В документации написано "True, если бычья, false, если медвежья, null - ни то, ни другое."

Код
var SmallCandels = _candleManager.GetTimeFrameCandles(Security, _SmallTF, SmallCandelsNumber);

foreach (var SmallCandel in SmallCandels)
            {
                
                if (SmallCandel.IsCandleBullishOrBearish() == false) 
                { 
                    this.AddLog(new LogMessage(this, Trader.MarketTime, ErrorTypes.None,
                                   "МЕДВЕЖЬЯ _SmallTF = {0}, SmallCandel.Time = {1}, OpenPrice = {2}, ClosePrice = {3}",
                                   _SmallTF, SmallCandel.Time, SmallCandel.OpenPrice, SmallCandel.ClosePrice));
                }

                if (SmallCandel.IsCandleBullishOrBearish() == true)
                {
                    this.AddLog(new LogMessage(this, Trader.MarketTime, ErrorTypes.None,
                                   "БЫЧЬЯ _SmallTF = {0}, SmallCandel.Time = {1}, OpenPrice = {2}, ClosePrice = {3}",
                                   _SmallTF, SmallCandel.Time, SmallCandel.OpenPrice, SmallCandel.ClosePrice));
                }
                

            }



Не возвращает ни одной медвежьей свечки при прогоне на пяти днях истории(дальше остановил тест).
Бычьи возвращаются.
Это баг или я что-то не так понял в описании метода ?
Спасибо:
< 1 2 

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

loading
clippy