Javo v1 Strategy
Javo v1 combines Heikin Ashi candles with a pair of exponential moving averages. A position is opened when the HA direction and the fast/slow EMA crossover align. The approach attempts to catch emerging trends while smoothing noise.
Details
- Entry Criteria:
- Long: HA bullish and
EMA_fast > EMA_slow - Short: HA bearish and
EMA_fast < EMA_slow
- Long: HA bullish and
- Long/Short: Both sides
- Exit Criteria: Opposite signal
- Stops: None
- Default Values:
FastEmaPeriod= 1SlowEmaPeriod= 30
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: Heikin Ashi, EMA
- Stops: No
- Complexity: Low
- Timeframe: Hourly
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Javo v1 strategy.
/// Builds Heikin Ashi candles and runs a fast and a slow EMA on the Heikin Ashi close. Goes long when the Heikin Ashi
/// candle is bullish and the fast EMA is above the slow one, short when the candle is bearish and the fast EMA is below
/// the slow one. The opposite signal reverses the position.
/// </summary>
public class JavoV1Strategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private decimal? _prevHaOpen;
private decimal? _prevHaClose;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastEmaPeriod
{
get => _fastEmaPeriod.Value;
set => _fastEmaPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int SlowEmaPeriod
{
get => _slowEmaPeriod.Value;
set => _slowEmaPeriod.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public JavoV1Strategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 1)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period on the Heikin Ashi close", "Moving Averages");
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period on the Heikin Ashi close", "Moving Averages");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastEma = null;
_slowEma = null;
_prevHaOpen = null;
_prevHaClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHaOpen = null;
_prevHaClose = null;
_fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
_slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastEma);
DrawIndicator(area, _slowEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
var haOpen = _prevHaOpen is decimal prevOpen && _prevHaClose is decimal prevClose
? (prevOpen + prevClose) / 2m
: (candle.OpenPrice + candle.ClosePrice) / 2m;
_prevHaOpen = haOpen;
_prevHaClose = haClose;
var fast = _fastEma.Process(haClose, candle.ServerTime, true).ToDecimal();
var slow = _slowEma.Process(haClose, candle.ServerTime, true).ToDecimal();
if (!_fastEma.IsFormed || !_slowEma.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (haClose > haOpen && fast > slow && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (haClose < haOpen && fast < slow && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
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 indicator_extensions import *
class javo_v1_strategy(Strategy):
"""
Javo v1 strategy.
Builds Heikin Ashi candles and runs a fast and a slow EMA on the Heikin Ashi close. Goes long when the Heikin Ashi
candle is bullish and the fast EMA is above the slow one, short when the candle is bearish and the fast EMA is below
the slow one. The opposite signal reverses the position.
"""
def __init__(self):
super(javo_v1_strategy, self).__init__()
self._fast_ema_period = self.Param("FastEmaPeriod", 1) \
.SetGreaterThanZero() \
.SetDisplay("Fast EMA", "Fast EMA period on the Heikin Ashi close", "Moving Averages")
self._slow_ema_period = self.Param("SlowEmaPeriod", 30) \
.SetGreaterThanZero() \
.SetDisplay("Slow EMA", "Slow EMA period on the Heikin Ashi close", "Moving Averages")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._fast_ema = None
self._slow_ema = None
self._prev_ha_open = None
self._prev_ha_close = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(javo_v1_strategy, self).OnReseted()
self._fast_ema = None
self._slow_ema = None
self._prev_ha_open = None
self._prev_ha_close = None
def OnStarted2(self, time):
super(javo_v1_strategy, self).OnStarted2(time)
self._prev_ha_open = None
self._prev_ha_close = None
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self._fast_ema_period.Value
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self._slow_ema_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._fast_ema)
self.DrawIndicator(area, self._slow_ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
open_price = float(candle.OpenPrice)
close = float(candle.ClosePrice)
ha_close = (open_price + float(candle.HighPrice) + float(candle.LowPrice) + close) / 4.0
if self._prev_ha_open is not None and self._prev_ha_close is not None:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
else:
ha_open = (open_price + close) / 2.0
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
fast = float(to_decimal(process_float(self._fast_ema, ha_close, candle.ServerTime, True)))
slow = float(to_decimal(process_float(self._slow_ema, ha_close, candle.ServerTime, True)))
if not self._fast_ema.IsFormed or not self._slow_ema.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if ha_close > ha_open and fast > slow and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif ha_close < ha_open and fast < slow and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return javo_v1_strategy()