Send Close Order is a port of the 2009 MetaTrader 4 expert advisor "SendCloseOrder" by Vladimir Hlystov. The original script draws four manual trendlines based on Bill Williams fractals and opens or closes market orders whenever price touches one of those projected levels. The StockSharp version replicates the decision logic with fully automated line management and works on any candle series provided by the platform.
Trading logic
Fractal detection – every finished candle updates a five-bar sliding window. Once the window is full, the candle in the middle is checked against the Bill Williams fractal conditions. Confirmed highs and lows are stored chronologically.
Trendline reconstruction
Sell line connects the latest two upward fractals that are separated by a downward fractal, forming a resistance slope.
Close #1 is the sell line shifted upward by 15 price steps (15 × Security.PriceStep) and acts as the long exit rail.
Buy line connects the latest two downward fractals that are separated by an upward fractal, forming a support slope.
Close #2 is the buy line shifted downward by 15 price steps and acts as the short exit rail.
Signal evaluation – the four lines are extrapolated to the timestamp of the finished candle. If the projected price lies inside the candle’s high/low range (with a small tolerance of two price steps), the corresponding action is triggered.
Order management
Touching Close #1 or Close #2 immediately closes the entire position via ClosePosition().
Touching the Sell or Buy line opens a market order with volume TradeVolume, provided that the resulting absolute position does not exceed MaxOrders × TradeVolume. When an opposite position exists, the order offsets it first and then stacks a new entry, mirroring the behaviour of hedging accounts.
Parameters
Name
Default
Description
EnableSellLine
true
Allow trades when the projected resistance line is hit.
EnableBuyLine
true
Allow trades when the projected support line is hit.
EnableCloseLongLine
true
Allow closing long positions on the shifted resistance line (Close #1).
EnableCloseShortLine
true
Allow closing short positions on the shifted support line (Close #2).
MaxOrders
1
Maximum number of stacked entries in the current direction.
TradeVolume
0.1
Volume of each individual market order.
CandleType
1h time frame
Candle series used for fractal calculations.
Differences versus the MetaTrader version
The StockSharp port recalculates the four lines every time a new fractal appears. In MetaTrader the user had to delete and redraw trendlines manually.
Execution is based on aggregated net positions; simultaneous long and short baskets are not supported by StockSharp’s default portfolio model.
Touch detection uses the high/low of the finished candle with a price-step tolerance instead of the instantaneous Bid/Ask quotes from ticks.
Chart objects (trendlines and labels) are not created; the focus is on trading signals.
Usage notes
The strategy can run on any instrument that provides candles and a valid PriceStep. When Security.PriceStep is zero the code falls back to 0.0001.
Increase MaxOrders to emulate the stacking behaviour of the original EA. Keep TradeVolume aligned with the instrument’s lot size to avoid rounding.
The line offset is fixed to the historical value of 15 points. Adjust the source code if the MetaTrader input is modified.
Only the C# implementation is provided. A Python translation will be added separately if required.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _fractalHigh;
private decimal _fractalLow;
private decimal _prev2High;
private decimal _prev1High;
private decimal _prev2Low;
private decimal _prev1Low;
private int _barCount;
public SendCloseOrderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
// Detect fractal high: prev1High > prev2High and prev1High > current high
if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
_fractalHigh = _prev1High;
// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
_fractalLow = _prev1Low;
_prev2High = _prev1High;
_prev1High = high;
_prev2Low = _prev1Low;
_prev1Low = low;
if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
return;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _fractalHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _fractalLow && close < emaVal)
{
_entryPrice = close;
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class send_close_order_strategy(Strategy):
def __init__(self):
super(send_close_order_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(send_close_order_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
self._bar_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# Detect fractal high: prev1High > prev2High and prev1High > current high
if self._bar_count > 3 and self._prev1_high > self._prev2_high and self._prev1_high > high:
self._fractal_high = self._prev1_high
# Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if self._bar_count > 3 and self._prev1_low < self._prev2_low and self._prev1_low < low:
self._fractal_low = self._prev1_low
self._prev2_high = self._prev1_high
self._prev1_high = high
self._prev2_low = self._prev1_low
self._prev1_low = low
if self._fractal_high == 0 or self._fractal_low == 0 or av <= 0:
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._fractal_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._fractal_low and close < ev:
self._entry_price = close
self.SellMarket()
def OnReseted(self):
super(send_close_order_strategy, self).OnReseted()
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
def CreateClone(self):
return send_close_order_strategy()