
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using System;
namespace cAlgo.Indicators
{
[Indicator(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class DojiStrategyIndicator : Indicator
{
[Parameter("Doji size", DefaultValue = 0.05, MinValue = 0.01, Step = 0.01)]
public double DojiSize { get; set; }
[Parameter("Long Candle Ratio", DefaultValue = 0.7, MaxValue = 1, Step = 0.1)]
public double LongCandleRatio { get; set; }
[Parameter("Use Volume Filter?", DefaultValue = false)]
public bool UseVolumeFilter { get; set; }
[Parameter("Volume Moving Average Period", DefaultValue = 24)]
public int VolumeMA { get; set; }
[Parameter("RSI Period", DefaultValue = 14)]
public int RSIPeriod { get; set; }
[Parameter("Wick-to-Body Ratio", DefaultValue = 2.5, MinValue = 1.0, Step = 0.1)]
public double WickToBodyRatio { get; set; }
private MovingAverage volumeMA;
private RelativeStrengthIndex rsi;
[Output("Doji Signal", Color = Colors.Orange, PlotType = PlotType.Points, Thickness = 2)]
public IndicatorDataSeries DojiSignal { get; set; }
protected override void Initialize()
{
if (UseVolumeFilter)
volumeMA = Indicators.MovingAverage(MarketSeries.TickVolume, VolumeMA, MovingAverageType.Simple);
rsi = Indicators.RelativeStrengthIndex(MarketSeries.Close, RSIPeriod);
}
public override void Calculate(int index)
{
double body = MarketSeries.Close[index] - MarketSeries.Open[index];
double range = MarketSeries.High[index] - MarketSeries.Low[index];
double abody = Math.Abs(body);
double ratio = abody / range;
bool isDoji = abody <= range * DojiSize;
bool goStar = isDoji && (!UseVolumeFilter || MarketSeries.TickVolume[index] > volumeMA.Result[index]);
if (goStar && IsHigherTimeFrame(out int timeFrameNumber, out string label))
{
DojiSignal[index] = MarketSeries.Close[index]; // Mark the Doji on the chart
Chart.DrawIcon("Doji" + TimeFrame.ToString() + index, ChartIconType.Diamond, index, MarketSeries.Close[index], Color.Orange);
// Highlight the high and low of the Doji candle with solid lines that extend over the next 3 candles
HighlightDojiHighLow(index, timeFrameNumber, label);
}
// SMT Divergence detection is now applied to all time frames
DetectSMTDivergence(index);
}
private bool IsHigherTimeFrame(out int timeFrameNumber, out string label)
{
timeFrameNumber = 0;
label = string.Empty;
if (TimeFrame == TimeFrame.Minute15)
{
timeFrameNumber = 15;
label = "0.25";
return true;
}
else if (TimeFrame == TimeFrame.Minute30)
{
timeFrameNumber = 30;
label = "0.5";
return true;
}
else if (TimeFrame == TimeFrame.Minute45)
{
timeFrameNumber = 45;
label = "0.75";
return true;
}
else if (TimeFrame == TimeFrame.Hour)
{
timeFrameNumber = 1;
label = "1";
return true;
}
else if (TimeFrame == TimeFrame.Hour2)
{
timeFrameNumber = 2;
label = "48";
return true;
}
else if (TimeFrame == TimeFrame.Hour4)
{
timeFrameNumber = 4;
label = "4";
return true;
}
else if (TimeFrame == TimeFrame.Daily)
{
timeFrameNumber = 24;
label = "24";
return true;
}
else if (TimeFrame == TimeFrame.Weekly)
{
timeFrameNumber = 168;
label = "W";
return true;
}
else if (TimeFrame == TimeFrame.Monthly)
{
timeFrameNumber = 720;
label = "M";
return true;
}
return false;
}
private void HighlightDojiHighLow(int dojiIndex, int timeFrameNumber, string label)
{
double dojiHigh = MarketSeries.High[dojiIndex];
double dojiLow = MarketSeries.Low[dojiIndex];
// Draw solid horizontal lines at the high and low of the Doji candle extending over the next 3 candles
Chart.DrawTrendLine("DojiHighLine" + TimeFrame.ToString() + dojiIndex, dojiIndex, dojiHigh, dojiIndex + 3, dojiHigh, Color.Blue, 2, LineStyle.Solid).IsInteractive = true;
Chart.DrawTrendLine("DojiLowLine" + TimeFrame.ToString() + dojiIndex, dojiIndex, dojiLow, dojiIndex + 3, dojiLow, Color.Red, 2, LineStyle.Solid).IsInteractive = true;
// Add time frame number or label text next to the blue line
Chart.DrawText("TimeFrameHigh" + TimeFrame.ToString() + dojiIndex, label, dojiIndex + 3, dojiHigh, Color.Green).IsInteractive = true;
}
private void DetectSMTDivergence(int index)
{
// Check if the current high or low forms a divergence with the RSI
double currentHigh = MarketSeries.High[index];
double currentLow = MarketSeries.Low[index];
double prevHigh = MarketSeries.High[index - 1];
double prevLow = MarketSeries.Low[index - 1];
double currentRSI = rsi.Result[index];
double prevRSI = rsi.Result[index - 1];
bool isWickDivergence = IsWickDivergence(index, currentHigh, currentLow, prevHigh, prevLow);
if (isWickDivergence)
{
// Bearish Divergence: Price makes a higher high, RSI makes a lower high
if (currentHigh > prevHigh && currentRSI < prevRSI)
{
// Mark the divergence on the chart with a unique identifier for this time frame
Chart.DrawIcon("BearishDivergence" + TimeFrame.ToString() + index, ChartIconType.DownArrow, index, currentHigh, Color.Red);
}
// Bullish Divergence: Price makes a lower low, RSI makes a higher low
if (currentLow < prevLow && currentRSI > prevRSI)
{
// Mark the divergence on the chart with a unique identifier for this time frame
Chart.DrawIcon("BullishDivergence" + TimeFrame.ToString() + index, ChartIconType.UpArrow, index, currentLow, Color.Green);
}
}
}
private bool IsWickDivergence(int index, double currentHigh, double currentLow, double prevHigh, double prevLow)
{
double currentBody = Math.Abs(MarketSeries.Close[index] - MarketSeries.Open[index]);
double currentRange = currentHigh - currentLow;
double prevBody = Math.Abs(MarketSeries.Close[index - 1] - MarketSeries.Open[index - 1]);
double prevRange = prevHigh - prevLow;
// Determine if the current candle has a small body and long wicks
bool currentHasLongWicks = (currentHigh - MarketSeries.Close[index]) > currentBody * WickToBodyRatio &&
(MarketSeries.Open[index] - currentLow) > currentBody * WickToBodyRatio;
// Determine if the previous candle has a small body and long wicks
bool prevHasLongWicks = (prevHigh - MarketSeries.Close[index - 1]) > prevBody * WickToBodyRatio &&
(MarketSeries.Open[index - 1] - prevLow) > prevBody * WickToBodyRatio;
return currentHasLongWicks || prevHasLongWicks;
}
}
}