ICAi Strategy
Strategy based on the ICAi adaptive moving average indicator. The indicator smooths price and adapts its slope using standard deviation. Long positions are opened when the indicator turns upward, short positions when it turns downward.
The algorithm works on any market where candle data is available. Default settings use a 4-hour timeframe and a smoothing length of 12.
Details
- Entry Criteria:
- Long:
Prev < PrevPrev && Current >= Prev - Short:
Prev > PrevPrev && Current <= Prev
- Long:
- Long/Short: Both
- Exit Criteria: Opposite signal
- Stops: Optional fixed stop loss and take profit
- Default Values:
Length= 12CandleType= TimeSpan.FromHours(4).TimeFrame()TakeProfit= 2000StopLoss= 1000
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: ICAi
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- 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>
/// Strategy based on the ICAi adaptive moving average.
/// Computes an adaptive MA using SMA and StdDev, trades on slope reversal.
/// </summary>
public class ICaiStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private SimpleMovingAverage _ma;
private StandardDeviation _std;
private decimal? _prevIcai;
private decimal? _prevSlope;
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public ICaiStrategy()
{
_length = Param(nameof(Length), 12)
.SetGreaterThanZero()
.SetDisplay("Length", "Indicator smoothing length", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma = null;
_std = null;
_prevIcai = null;
_prevSlope = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevIcai = null;
_prevSlope = null;
_ma = new SimpleMovingAverage { Length = Length };
_std = new StandardDeviation { Length = Length };
Indicators.Add(_ma);
Indicators.Add(_std);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
var t = candle.OpenTime;
var maResult = _ma.Process(price, t, true);
var stdResult = _std.Process(price, t, true);
if (!_ma.IsFormed || !_std.IsFormed)
return;
var maVal = maResult.GetValue<decimal>();
var stdVal = stdResult.GetValue<decimal>();
var prev = _prevIcai ?? maVal;
var diff = prev - maVal;
var powDxma = diff * diff;
var powStd = stdVal * stdVal;
decimal koeff = 0m;
if (powDxma >= powStd && powDxma != 0m)
koeff = 1m - powStd / powDxma;
var icai = prev + koeff * (maVal - prev);
_prevIcai = icai;
if (_prevSlope is null)
{
_prevSlope = 0m;
return;
}
var slope = icai - prev;
// Slope reversal: was negative, now positive -> buy
if (_prevSlope <= 0 && slope > 0 && Position <= 0)
BuyMarket();
// Slope reversal: was positive, now negative -> sell
else if (_prevSlope >= 0 && slope < 0 && Position >= 0)
SellMarket();
_prevSlope = slope;
}
}
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 SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class icai_strategy(Strategy):
def __init__(self):
super(icai_strategy, self).__init__()
self._length = self.Param("Length", 12) \
.SetDisplay("Length", "Indicator smoothing length", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._ma = None
self._std = None
self._prev_icai = None
self._prev_slope = None
@property
def length(self):
return self._length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(icai_strategy, self).OnReseted()
self._ma = None
self._std = None
self._prev_icai = None
self._prev_slope = None
def OnStarted2(self, time):
super(icai_strategy, self).OnStarted2(time)
self._prev_icai = None
self._prev_slope = None
self._ma = SimpleMovingAverage()
self._ma.Length = self.length
self._std = StandardDeviation()
self._std.Length = self.length
self.Indicators.Add(self._ma)
self.Indicators.Add(self._std)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
t = candle.OpenTime
ma_result = process_float(self._ma, price, t, True)
std_result = process_float(self._std, price, t, True)
if not self._ma.IsFormed or not self._std.IsFormed:
return
ma_val = float(ma_result)
std_val = float(std_result)
prev = self._prev_icai if self._prev_icai is not None else ma_val
diff = prev - ma_val
pow_dxma = diff * diff
pow_std = std_val * std_val
koeff = 0.0
if pow_dxma >= pow_std and pow_dxma != 0.0:
koeff = 1.0 - pow_std / pow_dxma
icai = prev + koeff * (ma_val - prev)
self._prev_icai = icai
if self._prev_slope is None:
self._prev_slope = 0.0
return
slope = icai - prev
if self._prev_slope <= 0 and slope > 0 and self.Position <= 0:
self.BuyMarket()
elif self._prev_slope >= 0 and slope < 0 and self.Position >= 0:
self.SellMarket()
self._prev_slope = slope
def CreateClone(self):
return icai_strategy()