Mikhail Sukhov
|
Date: 8/14/2011
Church Пытаюсь разобраться в том, как работает событийная стратегия на свечках. Насколько я могу понять, со времени выпуска документации механизмы изменились и описанные на форуме и в API .when.do конструкции на CandleToken'ах не работают, а без исходников понять почему - не получается.
Документация на сайте устарела, да. Но всегда существует оффлайн документация. Читайте ее. Она соответствует последней версии.
|
|
|
|
|
Teddy
|
Date: 8/14/2011
Да было бы не плохо выложить каркас робота по событийной модели.А то вроде бы утверждается что за основу теперь берётся событийная модель,а самого полноценного примера нет. Думаю очень многие были бы благодарны ,особенно начинающие. Спасибо.
|
|
Thanks:
|
|
|
|
|
Mikhail Sukhov
|
Date: 8/14/2011
Teddy Да было бы не плохо выложить каркас робота по событийной модели.А то вроде бы утверждается что за основу теперь берётся событийная модель,а самого полноценного примера нет. Думаю очень многие были бы благодарны ,особенно начинающие. Спасибо. В разделе Событийная модель показан пример хеджирования опционов. Если он не нравится и нужен другой пример, то предлагаю попросить здешних товарищей о том, чтобы привели кусочек кода из своего робота. Я пример подал.
|
|
Thanks:
|
|
|
|
|
Church
|
Date: 8/15/2011
Михаил, спасибо - я как-то забыл про оффлайновый хэлп.
Просим здешних товарищей - выложите, пожалуйста, каркас (без логики) событийного алгоритма, основанного на свечках!
|
|
Thanks:
|
|
|
|
|
wakwak
|
Date: 8/23/2011
Church Просим здешних товарищей - выложите, пожалуйста, каркас (без логики) событийного алгоритма, основанного на свечках!
+1, было бы архиполезно!
|
|
Thanks:
|
|
|
|
|
freelancer
|
Date: 8/24/2011
|
|
|
|
Что-то вроде этого: 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();
}
}
}
|
|
|
|
|
Church
|
Date: 8/25/2011
|
|
|
|
Вот мой пример. Комментарии может быть даже слишком подробные - это те моменты, в которые я утыкался сам. 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] –––––––––––––– ");
}
}
|
|
|
|
|
Mikhail Sukhov
|
Date: 8/25/2011
Полезно своими наработками делиться с товарищем по цеху. Вы привели свой код, а я увидел в нем неточности. Ловите бесплатные напутствия [laugh]:
- Security.SecurityNewTrades() каждый раз создает новое правило. Первый раз вы делаете все правильно, добавляя его в свою коллекцию. Второй раз в OnStopping вы не удаляете старой правило, а пытаетесь удалить свеже созданное. Ошибки в этом случае не возникает. Поэтому вы и не находите эту ошибку.
- Правила, если им не делать критерий остановки, по умолчанию останавливаются сами и удаляются так же из коллекции правил. Нет необходимости это делать принудительно.
- Закрытие позиции лучше так же делать через правила:
Code
this.When(this.Stopping())
.ClosePosition();
или
Code
this.When(this.Stopping())
.Do(ClosePositionHandler);
- RecalculatePosition == TraderHelper.GetPosition
- Метод Process напрашивается сам на событийных подход
Как совет, посмотрите, какие методы присутствуют в TraderHelper + StrategyRuleConditionHelper.
|
|
|
|
|
Mikhail Sukhov
|
Date: 8/25/2011
Аналогичные советы, что я дал Church
-
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);
- GetCandlesCurrentAndPrev - Посмотрите на метод ICandleContainer.GetCandle(index)
- action. Писать в стратегии парсинг файла плохой стиль. Считайте файл где-то еще, и передавайте в стратегию уже полученные данные.
Code
AddErrorLog(orderFail.Error.Message, new object[] { });
вот так тоже будет работать:
Code
AddErrorLog(orderFail.Error.Message);
|
|
Thanks:
|
|
|
|
|
Mikhail Sukhov
|
Date: 8/25/2011
Хотите получать такие советы регулярно? Прочитайте надпись и спросите меня, чем вы можете помочь проекту. Помогая проекту, проект в лице людей с голубыми никами будет помогать и вам.[cool]
|
|
Thanks:
|
|
|
|