Стратегия Candle Trader
Обзор
Candle Trader анализирует направление (бычье или медвежье) последних четырех завершенных свечей, ища краткосрочные сигналы на разворот. Стратегия работает с одним инструментом, использует рыночные заявки и настраиваемые уровни тейк-профита и стоп-лосса.
Логика торговли
- Покупка (прямой сигнал) – последняя свеча бычья, две предыдущие – медвежьие.
- Покупка (подтверждение) – последняя свеча бычья, предыдущая – медвежья, ещё две ранее – бычьие. Срабатывает только при Continuation =
true. - Продажа (прямой сигнал) – последняя свеча медвежья, две предыдущие бычьие.
- Продажа (подтверждение) – последняя свеча медвежья, предыдущая – бычья, ещё две ранее – медвежьие. Срабатывает только при Continuation =
true. - Если включен Reverse Close, новый сигнал противоположен текущей позиции, стратегия сначала закрывает старую позицию, затем открывает новую.
- Каждая сделка защищается фиксированными уровнями тейк-профита и стоп-лосса ( в шагах цены ).
Параметры
| Название | Описание |
|---|---|
Volume |
объём заявки на каждую сделку. |
TakeProfitTicks |
дистанция до тейк-профита (в шагах цены). |
StopLossTicks |
дистанция до стоп-лосса (в шагах цены). |
Continuation |
включение схем продолжения. |
ReverseClose |
закрыть ли противоположную позицию перед открытием новой. |
CandleType |
тип свечей для анализа. |
Замечания
- учитываются только закрытые свечи;
- перед отправкой новой заявки стратегия удаляет все активные заявки;
- ограничения на убыток и прибыль задаются через
StartProtection; - объём позиций допускает оптимизацию.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on candle direction patterns.
/// Opens long or short positions depending on the directions of the last four candles.
/// </summary>
public class CandleTraderStrategy : Strategy
{
private readonly StrategyParam<decimal> _volume;
private readonly StrategyParam<decimal> _takeProfitTicks;
private readonly StrategyParam<decimal> _stopLossTicks;
private readonly StrategyParam<bool> _continuation;
private readonly StrategyParam<bool> _reverseClose;
private readonly StrategyParam<DataType> _candleType;
private int _bar1Dir;
private int _bar2Dir;
private int _bar3Dir;
private int _bar4Dir;
/// <summary>
/// Order volume.
/// </summary>
public decimal TradeVolume
{
get => _volume.Value;
set => _volume.Value = value;
}
/// <summary>
/// Take profit in price steps.
/// </summary>
public decimal TakeProfitTicks
{
get => _takeProfitTicks.Value;
set => _takeProfitTicks.Value = value;
}
/// <summary>
/// Stop loss in price steps.
/// </summary>
public decimal StopLossTicks
{
get => _stopLossTicks.Value;
set => _stopLossTicks.Value = value;
}
/// <summary>
/// Enable continuation pattern.
/// </summary>
public bool Continuation
{
get => _continuation.Value;
set => _continuation.Value = value;
}
/// <summary>
/// Close opposite position before opening a new one.
/// </summary>
public bool ReverseClose
{
get => _reverseClose.Value;
set => _reverseClose.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CandleTraderStrategy()
{
_volume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume", "General");
_takeProfitTicks = Param(nameof(TakeProfitTicks), 500m)
.SetGreaterThanZero()
.SetDisplay("Take Profit Ticks", "Take profit in price steps", "Risk Management");
_stopLossTicks = Param(nameof(StopLossTicks), 50m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss Ticks", "Stop loss in price steps", "Risk Management");
_continuation = Param(nameof(Continuation), true)
.SetDisplay("Use Continuation", "Allow continuation pattern", "Trading Logic");
_reverseClose = Param(nameof(ReverseClose), true)
.SetDisplay("Reverse Close", "Close opposite position on signal", "Trading Logic");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bar1Dir = _bar2Dir = _bar3Dir = _bar4Dir = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
// Process only finished candles
if (candle.State != CandleStates.Finished)
return;
// Shift stored directions
_bar4Dir = _bar3Dir;
_bar3Dir = _bar2Dir;
_bar2Dir = _bar1Dir;
// Determine direction of current candle
_bar1Dir = candle.ClosePrice > candle.OpenPrice ? 1 : candle.ClosePrice < candle.OpenPrice ? -1 : 0;
// Ensure sufficient history
if (_bar4Dir == 0)
return;
var buyDirect = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == -1;
var buyCont = _bar1Dir == 1 && _bar2Dir == -1 && _bar3Dir == 1 && _bar4Dir == 1 && Continuation;
var sellDirect = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == 1;
var sellCont = _bar1Dir == -1 && _bar2Dir == 1 && _bar3Dir == -1 && _bar4Dir == -1 && Continuation;
if ((buyDirect || buyCont) && Position <= 0)
{
if (ReverseClose && Position < 0) BuyMarket();
BuyMarket();
}
else if ((sellDirect || sellCont) && Position >= 0)
{
if (ReverseClose && Position > 0) SellMarket();
SellMarket();
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class candle_trader_strategy(Strategy):
"""
Strategy based on candle direction patterns.
Opens long or short positions depending on the directions of the last four candles.
"""
def __init__(self):
super(candle_trader_strategy, self).__init__()
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetDisplay("Volume", "Order volume", "General")
self._take_profit_ticks = self.Param("TakeProfitTicks", 500.0) \
.SetDisplay("Take Profit Ticks", "Take profit in price steps", "Risk Management")
self._stop_loss_ticks = self.Param("StopLossTicks", 50.0) \
.SetDisplay("Stop Loss Ticks", "Stop loss in price steps", "Risk Management")
self._continuation = self.Param("Continuation", True) \
.SetDisplay("Use Continuation", "Allow continuation pattern", "Trading Logic")
self._reverse_close = self.Param("ReverseClose", True) \
.SetDisplay("Reverse Close", "Close opposite position on signal", "Trading Logic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._bar1_dir = 0
self._bar2_dir = 0
self._bar3_dir = 0
self._bar4_dir = 0
@property
def trade_volume(self):
return self._trade_volume.Value
@trade_volume.setter
def trade_volume(self, value):
self._trade_volume.Value = value
@property
def take_profit_ticks(self):
return self._take_profit_ticks.Value
@take_profit_ticks.setter
def take_profit_ticks(self, value):
self._take_profit_ticks.Value = value
@property
def stop_loss_ticks(self):
return self._stop_loss_ticks.Value
@stop_loss_ticks.setter
def stop_loss_ticks(self, value):
self._stop_loss_ticks.Value = value
@property
def continuation(self):
return self._continuation.Value
@continuation.setter
def continuation(self, value):
self._continuation.Value = value
@property
def reverse_close(self):
return self._reverse_close.Value
@reverse_close.setter
def reverse_close(self, value):
self._reverse_close.Value = value
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(candle_trader_strategy, self).OnReseted()
self._bar1_dir = 0
self._bar2_dir = 0
self._bar3_dir = 0
self._bar4_dir = 0
def OnStarted2(self, time):
super(candle_trader_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
# Shift stored directions
self._bar4_dir = self._bar3_dir
self._bar3_dir = self._bar2_dir
self._bar2_dir = self._bar1_dir
# Determine direction of current candle
if candle.ClosePrice > candle.OpenPrice:
self._bar1_dir = 1
elif candle.ClosePrice < candle.OpenPrice:
self._bar1_dir = -1
else:
self._bar1_dir = 0
# Ensure sufficient history
if self._bar4_dir == 0:
return
buy_direct = self._bar1_dir == 1 and self._bar2_dir == -1 and self._bar3_dir == -1
buy_cont = (self._bar1_dir == 1 and self._bar2_dir == -1 and self._bar3_dir == 1
and self._bar4_dir == 1 and self.continuation)
sell_direct = self._bar1_dir == -1 and self._bar2_dir == 1 and self._bar3_dir == 1
sell_cont = (self._bar1_dir == -1 and self._bar2_dir == 1 and self._bar3_dir == -1
and self._bar4_dir == -1 and self.continuation)
if (buy_direct or buy_cont) and self.Position <= 0:
if self.reverse_close and self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif (sell_direct or sell_cont) and self.Position >= 0:
if self.reverse_close and self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return candle_trader_strategy()