Church
|
Date: 8/26/2011
Михаил, спасибо! TraderHelper оказался очень полезным классом. :)
Я с радостью как-нибудь поучаствую в проекте, только одна проблема - у меня всего три недели опыта серьезного программирования.
Если позволите, пара вопросов.
1. Как переделать Process под событийную модель, вроде и так уже событийно до тиков. :) Сделать делать одноразовые правила типа LastTradePriceLess/More и обновлять на свечках? А в чем будет преимущество перед Process - высвобождение ресурсов?
2. Раз вы сделали специальную механику правил-действий, значит стандартная событийная модель чем-то не устраивала. Можете вкратце рассказать философию правил-действий и как их лучше всего использовать?
|
|
Thanks:
|
|
|
|
|
Church
|
Date: 8/26/2011
Кстати, this.When(this.Stopping()).ClosePosition() у меня не работает, а ClosePositionHandler я не могу найти даже через хэлп.
|
|
Thanks:
|
|
|
|
|
Mikhail Sukhov
|
Date: 8/28/2011
Church Кстати, this.When(this.Stopping()).ClosePosition() у меня не работает Чуть подробнее. Church а ClosePositionHandler я не могу найти даже через хэлп. Конечно нет. Это должен быть обработчик в вашем коде.
|
|
Thanks:
|
|
|
|
|
Church
|
Date: 8/28/2011
Сделал эту запись в конструкторе, оверрайд OnStopping удалил. При остановке стратгии при отрытой позиции она не закрывается. PositionManager.Position показывает отличное от нуля число, как и таблица в квике. Кроме удаления OnStopping все аналогично коду выше.
|
|
Thanks:
|
|
|
|
|
OvcharenkoVI
|
Date: 1/12/2012
|
|
|
|
Church Вот мой пример. Комментарии может быть даже слишком подробные - это те моменты, в которые я утыкался сам. 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] –––––––––––––– ");
}
}
Очень полезный пример, наглядная реализация стопов без использования StopLossStrategy помогла
|
|
|
|
|
risty
|
Date: 1/24/2012
Есть вопрос про свечки, надеюсь в тему. Допустим у нас есть какое-то NewMyTrade.Time Я хочу получить свечку с: Code
candle.Time = NewMyTrade.Time - new TimeSpan (0,5,11);
и таймфреймом Code
candle.Timeframe = new TimeSpan (0,5,11);
т.е. получить свечку от произвольного момента в прошлом(для примера взято 5:11) до NewMyTrade.Time, причем при каждом новом NewMyTrade.Time произвольный момент в прошлом меняется. Как это элегантнее всего сделать ?
|
|
Thanks:
|
|
|
|
|
risty
|
Date: 1/25/2012
|
|
|
|
И ещё вопрос про IsCandleBullishOrBearish() В документации написано "True, если бычья, false, если медвежья, null - ни то, ни другое."Code
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:
|
|
|
|