| 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 помогла
			
			
			
			
		
 | 
			
				|  |  | 
			
				|  | 
		
			| 
 | 
		
			
				| 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));
                }
                
            } Не возвращает ни одной медвежьей свечки при прогоне на пяти днях истории(дальше остановил тест). Бычьи возвращаются. Это баг или я что-то не так понял в описании метода ?
			
			
			
			
		
 | 
			
				|  | 
	
		| Спасибо: |   |  | 
			
				|  |