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

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


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

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


Tags:


Thanks:


< 1 2 
Church

Avatar
Date: 8/26/2011
Reply


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

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

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

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

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

Thanks:

Church

Avatar
Date: 8/26/2011
Reply


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

Thanks:

Mikhail Sukhov

Avatar
Date: 8/28/2011
Reply


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

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

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

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

Thanks:

Church

Avatar
Date: 8/28/2011
Reply


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

Thanks:

OvcharenkoVI

Avatar
Date: 1/12/2012
Reply


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 помогла
Thanks: JakeGreen

risty

Avatar
Date: 1/24/2012
Reply


Есть вопрос про свечки, надеюсь в тему. Допустим у нас есть какое-то NewMyTrade.Time Я хочу получить свечку с:

candle.Time = NewMyTrade.Time - new TimeSpan (0,5,11);

и таймфреймом

candle.Timeframe = new TimeSpan (0,5,11);

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

Как это элегантнее всего сделать ?

Thanks:

risty

Avatar
Date: 1/25/2012
Reply


И ещё вопрос про 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));
                }
                

            }

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

Thanks:
< 1 2 

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

loading
clippy