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("Dimensione Doji", DefaultValue = 0.05, MinValue = 0.01, Step = 0.01)]
public double DojiSize { get; set; }
[Parameter("Rapporto Candela Lunga", DefaultValue = 0.7, MaxValue = 1, Step = 0.1)]
public double LongCandleRatio { get; set; }
[Parameter("Usare Filtro Volume?", DefaultValue = false)]
public bool UseVolumeFilter { get; set; }
[Parameter("Periodo Media Mobile Volume", DefaultValue = 24)]
public int VolumeMA { get; set; }
[Parameter("Periodo RSI", DefaultValue = 14)]
public int RSIPeriod { get; set; }
[Parameter("Rapporto Wick-corpo", DefaultValue = 2.5, MinValue = 1.0, Step = 0.1)]
public double WickToBodyRatio { get; set; }
private MovingAverage volumeMA;
private RelativeStrengthIndex rsi;
[Output("Segnale Doji", 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]; // Segna il Doji sul grafico
Chart.DrawIcon("Doji" + TimeFrame.ToString() + index, ChartIconType.Diamond, index, MarketSeries.Close[index], Color.Orange);
// Evidenzia il massimo e il minimo della candela Doji con linee solide che si estendono sulle 3 candele successive
HighlightDojiHighLow(index, timeFrameNumber, label);
}
// La rilevazione della divergenza SMT è ora applicata a tutti i timeframe
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];
// Disegna linee orizzontali solide al massimo e minimo della candela Doji estese sulle 3 candele successive
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;
// Aggiungi il numero del timeframe o il testo dell'etichetta accanto alla linea blu
Chart.DrawText("TimeFrameHigh" + TimeFrame.ToString() + dojiIndex, label, dojiIndex + 3, dojiHigh, Color.Green).IsInteractive = true;
}
private void DetectSMTDivergence(int index)
{
// Controlla se il massimo o minimo corrente forma una divergenza con l'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)
{
// Divergenza ribassista: il prezzo fa un massimo più alto, l'RSI fa un massimo più basso
if (currentHigh > prevHigh && currentRSI < prevRSI)
{
// Segna la divergenza sul grafico con un identificatore unico per questo timeframe
Chart.DrawIcon("BearishDivergence" + TimeFrame.ToString() + index, ChartIconType.DownArrow, index, currentHigh, Color.Red);
}
// Divergenza rialzista: il prezzo fa un minimo più basso, l'RSI fa un minimo più alto
if (currentLow < prevLow && currentRSI > prevRSI)
{
// Segna la divergenza sul grafico con un identificatore unico per questo timeframe
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;
// Determina se la candela corrente ha un corpo piccolo e lunghe ombre
bool currentHasLongWicks = (currentHigh - MarketSeries.Close[index]) > currentBody * WickToBodyRatio &&
(MarketSeries.Open[index] - currentLow) > currentBody * WickToBodyRatio;
// Determina se la candela precedente ha un corpo piccolo e lunghe ombre
bool prevHasLongWicks = (prevHigh - MarketSeries.Close[index - 1]) > prevBody * WickToBodyRatio &&
(MarketSeries.Open[index - 1] - prevLow) > prevBody * WickToBodyRatio;
return currentHasLongWicks || prevHasLongWicks;
}
}
}