Adam and Eve Strategy
Trend-following strategy combining Heiken Ashi candles with a cascade of simple moving averages. A short is opened when a bearish Heiken Ashi candle without an upper wick appears and all monitored moving averages (5, 7, 9, 10, 12, 14, 20) slope downward. A long position is triggered by a bullish candle without a lower wick and all averages sloping upward. Each trade targets a profit at a distance of one ATR(14) from entry with no stop loss.
Details
- Entry Criteria: previous Heiken Ashi candle without upper (short) or lower (long) wick and aligned SMA stack
- Long/Short: Both
- Exit Criteria: profit target at ATR(14) distance
- Stops: None
- Default Values:
AtrPeriod= 14
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: SMA (5,7,9,10,12,14,20), Heiken Ashi, ATR
- Stops: Target only
- Complexity: Intermediate
- Timeframe: Configurable, default 15-minute
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Moderate
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>
/// Adam and Eve strategy.
/// Uses Heiken Ashi candles and a stack of SMAs to detect strong trends.
/// A bearish Heiken Ashi candle without an upper wick and falling averages opens a short.
/// A bullish candle without a lower wick and rising averages opens a long.
/// Each trade targets one ATR from entry without a stop loss.
/// </summary>
public class AdamAndEveStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private decimal? _prevHaOpen;
private decimal? _prevHaClose;
private decimal? _prevHaHigh;
private decimal? _prevHaLow;
private decimal? _sma5Prev1;
private decimal? _sma5Prev2;
private decimal? _sma7Prev1;
private decimal? _sma7Prev2;
private decimal? _sma9Prev1;
private decimal? _sma9Prev2;
private decimal? _sma10Prev1;
private decimal? _sma10Prev2;
private decimal? _sma12Prev1;
private decimal? _sma12Prev2;
private decimal? _sma14Prev1;
private decimal? _sma14Prev2;
private decimal? _sma20Prev1;
private decimal? _sma20Prev2;
private decimal? _targetPrice;
/// <summary>
/// Candle type to process.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// ATR period for profit target.
/// </summary>
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="AdamAndEveStrategy"/>.
/// </summary>
public AdamAndEveStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR based profit target", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHaOpen = null;
_prevHaClose = null;
_prevHaHigh = null;
_prevHaLow = null;
_sma5Prev1 = _sma5Prev2 = null;
_sma7Prev1 = _sma7Prev2 = null;
_sma9Prev1 = _sma9Prev2 = null;
_sma10Prev1 = _sma10Prev2 = null;
_sma12Prev1 = _sma12Prev2 = null;
_sma14Prev1 = _sma14Prev2 = null;
_sma20Prev1 = _sma20Prev2 = null;
_targetPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma5 = new ExponentialMovingAverage { Length = 5 };
var sma7 = new ExponentialMovingAverage { Length = 7 };
var sma9 = new ExponentialMovingAverage { Length = 9 };
var sma10 = new ExponentialMovingAverage { Length = 10 };
var sma12 = new ExponentialMovingAverage { Length = 12 };
var sma14 = new ExponentialMovingAverage { Length = 14 };
var sma20 = new ExponentialMovingAverage { Length = 20 };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma5, sma7, sma9, sma10, sma12, sma14, sma20, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(
ICandleMessage candle,
decimal sma5,
decimal sma7,
decimal sma9,
decimal sma10,
decimal sma12,
decimal sma14,
decimal sma20,
decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!_prevHaOpen.HasValue)
{
ComputeHeikenAshi(candle, out var haOpen, out var haClose, out var haHigh, out var haLow);
_prevHaOpen = haOpen;
_prevHaClose = haClose;
_prevHaHigh = haHigh;
_prevHaLow = haLow;
UpdateSma(sma5, ref _sma5Prev1, ref _sma5Prev2);
UpdateSma(sma7, ref _sma7Prev1, ref _sma7Prev2);
UpdateSma(sma9, ref _sma9Prev1, ref _sma9Prev2);
UpdateSma(sma10, ref _sma10Prev1, ref _sma10Prev2);
UpdateSma(sma12, ref _sma12Prev1, ref _sma12Prev2);
UpdateSma(sma14, ref _sma14Prev1, ref _sma14Prev2);
UpdateSma(sma20, ref _sma20Prev1, ref _sma20Prev2);
return;
}
var bearishPrev = _prevHaClose < _prevHaOpen;
var bullishPrev = _prevHaClose > _prevHaOpen;
var noUpperWickPrev = _prevHaOpen == _prevHaHigh;
var noLowerWickPrev = _prevHaOpen == _prevHaLow;
var smasDown =
IsDecreasing(sma5, _sma5Prev1, _sma5Prev2) &&
IsDecreasing(sma7, _sma7Prev1, _sma7Prev2) &&
IsDecreasing(sma9, _sma9Prev1, _sma9Prev2) &&
IsDecreasing(sma10, _sma10Prev1, _sma10Prev2) &&
IsDecreasing(sma12, _sma12Prev1, _sma12Prev2) &&
IsDecreasing(sma14, _sma14Prev1, _sma14Prev2) &&
IsDecreasing(sma20, _sma20Prev1, _sma20Prev2);
var smasUp =
IsIncreasing(sma5, _sma5Prev1, _sma5Prev2) &&
IsIncreasing(sma7, _sma7Prev1, _sma7Prev2) &&
IsIncreasing(sma9, _sma9Prev1, _sma9Prev2) &&
IsIncreasing(sma10, _sma10Prev1, _sma10Prev2) &&
IsIncreasing(sma12, _sma12Prev1, _sma12Prev2) &&
IsIncreasing(sma14, _sma14Prev1, _sma14Prev2) &&
IsIncreasing(sma20, _sma20Prev1, _sma20Prev2);
if (Position == 0 && IsFormedAndOnlineAndAllowTrading())
{
if (bearishPrev && noUpperWickPrev && smasDown)
{
SellMarket();
_targetPrice = candle.ClosePrice - atr;
}
else if (bullishPrev && noLowerWickPrev && smasUp)
{
BuyMarket();
_targetPrice = candle.ClosePrice + atr;
}
}
else if (Position > 0 && _targetPrice.HasValue && candle.HighPrice >= _targetPrice.Value)
{
SellMarket();
_targetPrice = null;
}
else if (Position < 0 && _targetPrice.HasValue && candle.LowPrice <= _targetPrice.Value)
{
BuyMarket();
_targetPrice = null;
}
ComputeHeikenAshi(candle, out var nextOpen, out var nextClose, out var nextHigh, out var nextLow);
_prevHaOpen = nextOpen;
_prevHaClose = nextClose;
_prevHaHigh = nextHigh;
_prevHaLow = nextLow;
UpdateSma(sma5, ref _sma5Prev1, ref _sma5Prev2);
UpdateSma(sma7, ref _sma7Prev1, ref _sma7Prev2);
UpdateSma(sma9, ref _sma9Prev1, ref _sma9Prev2);
UpdateSma(sma10, ref _sma10Prev1, ref _sma10Prev2);
UpdateSma(sma12, ref _sma12Prev1, ref _sma12Prev2);
UpdateSma(sma14, ref _sma14Prev1, ref _sma14Prev2);
UpdateSma(sma20, ref _sma20Prev1, ref _sma20Prev2);
}
private void ComputeHeikenAshi(ICandleMessage candle, out decimal haOpen, out decimal haClose, out decimal haHigh, out decimal haLow)
{
haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
haOpen = _prevHaOpen.HasValue ? (_prevHaOpen.Value + _prevHaClose.Value) / 2m : (candle.OpenPrice + candle.ClosePrice) / 2m;
haHigh = Math.Max(candle.HighPrice, Math.Max(haOpen, haClose));
haLow = Math.Min(candle.LowPrice, Math.Min(haOpen, haClose));
}
private static void UpdateSma(decimal current, ref decimal? prev1, ref decimal? prev2)
{
prev2 = prev1;
prev1 = current;
}
private static bool IsDecreasing(decimal current, decimal? prev1, decimal? prev2)
=> prev1.HasValue && prev2.HasValue && current < prev1.Value && prev1.Value < prev2.Value;
private static bool IsIncreasing(decimal current, decimal? prev1, decimal? prev2)
=> prev1.HasValue && prev2.HasValue && current > prev1.Value && prev1.Value > prev2.Value;
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class adam_and_eve_strategy(Strategy):
def __init__(self):
super(adam_and_eve_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Period for ATR based profit target", "Indicators")
self._prev_ha_open = None
self._prev_ha_close = None
self._prev_ha_high = None
self._prev_ha_low = None
self._sma5_prev1 = None
self._sma5_prev2 = None
self._sma7_prev1 = None
self._sma7_prev2 = None
self._sma9_prev1 = None
self._sma9_prev2 = None
self._sma10_prev1 = None
self._sma10_prev2 = None
self._sma12_prev1 = None
self._sma12_prev2 = None
self._sma14_prev1 = None
self._sma14_prev2 = None
self._sma20_prev1 = None
self._sma20_prev2 = None
self._target_price = None
@property
def candle_type(self):
return self._candle_type.Value
@property
def atr_period(self):
return self._atr_period.Value
def OnReseted(self):
super(adam_and_eve_strategy, self).OnReseted()
self._prev_ha_open = None
self._prev_ha_close = None
self._prev_ha_high = None
self._prev_ha_low = None
self._sma5_prev1 = None
self._sma5_prev2 = None
self._sma7_prev1 = None
self._sma7_prev2 = None
self._sma9_prev1 = None
self._sma9_prev2 = None
self._sma10_prev1 = None
self._sma10_prev2 = None
self._sma12_prev1 = None
self._sma12_prev2 = None
self._sma14_prev1 = None
self._sma14_prev2 = None
self._sma20_prev1 = None
self._sma20_prev2 = None
self._target_price = None
def _compute_heiken_ashi(self, candle):
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
ha_close = (o + h + l + c) / 4.0
if self._prev_ha_open is not None:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
else:
ha_open = (o + c) / 2.0
ha_high = max(h, max(ha_open, ha_close))
ha_low = min(l, min(ha_open, ha_close))
return ha_open, ha_close, ha_high, ha_low
@staticmethod
def _is_decreasing(current, prev1, prev2):
if prev1 is None or prev2 is None:
return False
return current < prev1 and prev1 < prev2
@staticmethod
def _is_increasing(current, prev1, prev2):
if prev1 is None or prev2 is None:
return False
return current > prev1 and prev1 > prev2
def OnStarted2(self, time):
super(adam_and_eve_strategy, self).OnStarted2(time)
sma5 = ExponentialMovingAverage()
sma5.Length = 5
sma7 = ExponentialMovingAverage()
sma7.Length = 7
sma9 = ExponentialMovingAverage()
sma9.Length = 9
sma10 = ExponentialMovingAverage()
sma10.Length = 10
sma12 = ExponentialMovingAverage()
sma12.Length = 12
sma14 = ExponentialMovingAverage()
sma14.Length = 14
sma20 = ExponentialMovingAverage()
sma20.Length = 20
atr = AverageTrueRange()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma5, sma7, sma9, sma10, sma12, sma14, sma20, atr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle, v5, v7, v9, v10, v12, v14, v20, atr_val):
if candle.State != CandleStates.Finished:
return
v5 = float(v5)
v7 = float(v7)
v9 = float(v9)
v10 = float(v10)
v12 = float(v12)
v14 = float(v14)
v20 = float(v20)
atr_val = float(atr_val)
if self._prev_ha_open is None:
ha_open, ha_close, ha_high, ha_low = self._compute_heiken_ashi(candle)
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
self._prev_ha_high = ha_high
self._prev_ha_low = ha_low
self._sma5_prev2 = self._sma5_prev1
self._sma5_prev1 = v5
self._sma7_prev2 = self._sma7_prev1
self._sma7_prev1 = v7
self._sma9_prev2 = self._sma9_prev1
self._sma9_prev1 = v9
self._sma10_prev2 = self._sma10_prev1
self._sma10_prev1 = v10
self._sma12_prev2 = self._sma12_prev1
self._sma12_prev1 = v12
self._sma14_prev2 = self._sma14_prev1
self._sma14_prev1 = v14
self._sma20_prev2 = self._sma20_prev1
self._sma20_prev1 = v20
return
bearish_prev = self._prev_ha_close < self._prev_ha_open
bullish_prev = self._prev_ha_close > self._prev_ha_open
no_upper_wick_prev = self._prev_ha_open == self._prev_ha_high
no_lower_wick_prev = self._prev_ha_open == self._prev_ha_low
smas_down = (
self._is_decreasing(v5, self._sma5_prev1, self._sma5_prev2) and
self._is_decreasing(v7, self._sma7_prev1, self._sma7_prev2) and
self._is_decreasing(v9, self._sma9_prev1, self._sma9_prev2) and
self._is_decreasing(v10, self._sma10_prev1, self._sma10_prev2) and
self._is_decreasing(v12, self._sma12_prev1, self._sma12_prev2) and
self._is_decreasing(v14, self._sma14_prev1, self._sma14_prev2) and
self._is_decreasing(v20, self._sma20_prev1, self._sma20_prev2)
)
smas_up = (
self._is_increasing(v5, self._sma5_prev1, self._sma5_prev2) and
self._is_increasing(v7, self._sma7_prev1, self._sma7_prev2) and
self._is_increasing(v9, self._sma9_prev1, self._sma9_prev2) and
self._is_increasing(v10, self._sma10_prev1, self._sma10_prev2) and
self._is_increasing(v12, self._sma12_prev1, self._sma12_prev2) and
self._is_increasing(v14, self._sma14_prev1, self._sma14_prev2) and
self._is_increasing(v20, self._sma20_prev1, self._sma20_prev2)
)
close_price = float(candle.ClosePrice)
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
if self.Position == 0:
if bearish_prev and no_upper_wick_prev and smas_down:
self.SellMarket()
self._target_price = close_price - atr_val
elif bullish_prev and no_lower_wick_prev and smas_up:
self.BuyMarket()
self._target_price = close_price + atr_val
elif self.Position > 0 and self._target_price is not None and high_price >= self._target_price:
self.SellMarket()
self._target_price = None
elif self.Position < 0 and self._target_price is not None and low_price <= self._target_price:
self.BuyMarket()
self._target_price = None
ha_open, ha_close, ha_high, ha_low = self._compute_heiken_ashi(candle)
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
self._prev_ha_high = ha_high
self._prev_ha_low = ha_low
self._sma5_prev2 = self._sma5_prev1
self._sma5_prev1 = v5
self._sma7_prev2 = self._sma7_prev1
self._sma7_prev1 = v7
self._sma9_prev2 = self._sma9_prev1
self._sma9_prev1 = v9
self._sma10_prev2 = self._sma10_prev1
self._sma10_prev1 = v10
self._sma12_prev2 = self._sma12_prev1
self._sma12_prev1 = v12
self._sma14_prev2 = self._sma14_prev1
self._sma14_prev1 = v14
self._sma20_prev2 = self._sma20_prev1
self._sma20_prev1 = v20
def CreateClone(self):
return adam_and_eve_strategy()