The Area MACD Strategy evaluates the balance between bullish and bearish momentum using the MACD main line. For every candle the strategy accumulates the sum of all positive MACD values and the absolute sum of all negative MACD values over a configurable history window. The dominant side defines the trading direction: a stronger positive area favours long positions, while a stronger negative area favours short exposure. A reverse switch allows trading against the detected trend when required.
The implementation uses the high-level StockSharp API with candle subscriptions and indicator bindings. Only completed candles are processed, and all trading logic is encapsulated inside the ProcessCandle handler.
Indicators and Data
MACD (Moving Average Convergence Divergence) with configurable fast, slow and signal periods.
Candles of a user-defined timeframe (30 minutes by default).
Trading Rules
Long Entry – When the cumulative positive MACD area is greater than the cumulative absolute negative area. If reverse mode is enabled the condition is inverted.
Short Entry – When the cumulative absolute negative MACD area dominates. Reverse mode swaps the behaviour.
Position Management – When a new entry signal appears the strategy closes any opposite position before opening the new one so that only a single directional position is held.
Risk Management
Stop Loss – Fixed distance in pips measured from the entry price. Converted automatically to price units using the security price step.
Take Profit – Fixed profit target in pips using the same conversion rules.
Trailing Stop – Optional trailing stop that activates once the position moves in profit by TrailingStopPips + TrailingStepPips. The stop then follows price with a gap defined by TrailingStopPips and only moves forward when the price advances by at least TrailingStepPips more. Both values must be greater than zero to enable the trailing logic.
Parameters
Name
Description
Default
OrderVolume
Order volume used for market entries.
1
HistoryLength
Number of candles stored for the MACD area comparison.
60
MacdFastLength
Fast EMA period for the MACD.
12
MacdSlowLength
Slow EMA period for the MACD.
26
MacdSignalLength
Signal EMA period for the MACD.
9
ReverseSignals
If enabled, swaps the long and short entry conditions.
false
StopLossPips
Stop loss distance expressed in pips.
100
TakeProfitPips
Take profit distance in pips.
150
TrailingStopPips
Trailing stop distance in pips. Set to zero to disable trailing.
5
TrailingStepPips
Additional progress required before the trailing stop is updated. Set to zero to disable trailing.
5
CandleType
Candle timeframe used by the subscription.
30-minute time frame
Usage Notes
Attach the strategy to a portfolio and a security, then adjust the parameters for the target market.
Ensure that both TrailingStopPips and TrailingStepPips are greater than zero to enable trailing protection. Otherwise trailing is ignored and only stop loss / take profit levels are active.
Monitor the log messages for information about stop-loss, take-profit and trailing events. All logs are produced in English as required.
Original Idea
The conversion is based on the MetaTrader 5 "Area MACD" expert advisor. The StockSharp version keeps the core concept of comparing MACD areas while integrating risk management and indicator handling through the framework's high-level API.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Area MACD strategy (simplified). Tracks cumulative positive/negative
/// areas of fast-slow EMA difference to determine trend direction.
/// </summary>
public class AreaMacdStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _historyLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public int HistoryLength
{
get => _historyLength.Value;
set => _historyLength.Value = value;
}
public AreaMacdStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast EMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow EMA period", "Indicators");
_historyLength = Param(nameof(HistoryLength), 20)
.SetGreaterThanZero()
.SetDisplay("History Length", "Area accumulation window", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastLength };
var slowEma = new ExponentialMovingAverage { Length = SlowLength };
var diffHistory = new Queue<decimal>();
decimal posArea = 0, negArea = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, (ICandleMessage candle, decimal fastValue, decimal slowValue) =>
{
if (candle.State != CandleStates.Finished)
return;
var diff = fastValue - slowValue;
diffHistory.Enqueue(diff);
if (diff > 0) posArea += diff;
else negArea += Math.Abs(diff);
if (diffHistory.Count > HistoryLength)
{
var old = diffHistory.Dequeue();
if (old > 0) posArea -= old;
else negArea -= Math.Abs(old);
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (diffHistory.Count < HistoryLength)
return;
// Bullish area dominates
if (posArea > negArea * 1.25m && Position <= 0)
BuyMarket();
// Bearish area dominates
else if (negArea > posArea * 1.25m && Position >= 0)
SellMarket();
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowEma);
DrawOwnTrades(area);
}
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from collections import deque
class area_macd_strategy(Strategy):
def __init__(self):
super(area_macd_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles", "General")
self._fast_length = self.Param("FastLength", 12) \
.SetDisplay("Fast Length", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 26) \
.SetDisplay("Slow Length", "Slow EMA period", "Indicators")
self._history_length = self.Param("HistoryLength", 20) \
.SetDisplay("History Length", "Area accumulation window", "Indicators")
self._diff_history = deque()
self._pos_area = 0.0
self._neg_area = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
@property
def HistoryLength(self):
return self._history_length.Value
def OnReseted(self):
super(area_macd_strategy, self).OnReseted()
self._diff_history = deque()
self._pos_area = 0.0
self._neg_area = 0.0
def OnStarted2(self, time):
super(area_macd_strategy, self).OnStarted2(time)
self._diff_history = deque()
self._pos_area = 0.0
self._neg_area = 0.0
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastLength
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
diff = fv - sv
self._diff_history.append(diff)
if diff > 0:
self._pos_area += diff
else:
self._neg_area += abs(diff)
if len(self._diff_history) > self.HistoryLength:
old = self._diff_history.popleft()
if old > 0:
self._pos_area -= old
else:
self._neg_area -= abs(old)
if len(self._diff_history) < self.HistoryLength:
return
if self._pos_area > self._neg_area * 1.25 and self.Position <= 0:
self.BuyMarket()
elif self._neg_area > self._pos_area * 1.25 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return area_macd_strategy()