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("Kích thước Doji", DefaultValue = 0.05, MinValue = 0.01, Step = 0.01)]
public double DojiSize { get; set; }
[Parameter("Tỷ lệ Nến dài", DefaultValue = 0.7, MaxValue = 1, Step = 0.1)]
public double LongCandleRatio { get; set; }
[Parameter("Sử dụng Bộ lọc Khối lượng?", DefaultValue = false)]
public bool UseVolumeFilter { get; set; }
[Parameter("Chu kỳ Trung bình Động Khối lượng", DefaultValue = 24)]
public int VolumeMA { get; set; }
[Parameter("Chu kỳ RSI", DefaultValue = 14)]
public int RSIPeriod { get; set; }
[Parameter("Tỷ lệ Bấc so với Thân nến", DefaultValue = 2.5, MinValue = 1.0, Step = 0.1)]
public double WickToBodyRatio { get; set; }
private MovingAverage volumeMA;
private RelativeStrengthIndex rsi;
[Output("Tín hiệu 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]; // Đánh dấu Doji trên biểu đồ
Chart.DrawIcon("Doji" + TimeFrame.ToString() + index, ChartIconType.Diamond, index, MarketSeries.Close[index], Color.Orange);
// Làm nổi bật mức cao và thấp của nến Doji bằng các đường thẳng liền mạch kéo dài qua 3 nến tiếp theo
HighlightDojiHighLow(index, timeFrameNumber, label);
}
// Phát hiện phân kỳ SMT hiện được áp dụng cho tất cả các khung thời gian
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];
// Vẽ các đường ngang liền mạch tại mức cao và thấp của nến Doji kéo dài qua 3 nến tiếp theo
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;
// Thêm số khung thời gian hoặc văn bản nhãn bên cạnh đường màu xanh
Chart.DrawText("TimeFrameHigh" + TimeFrame.ToString() + dojiIndex, label, dojiIndex + 3, dojiHigh, Color.Green).IsInteractive = true;
}
private void DetectSMTDivergence(int index)
{
// Kiểm tra xem mức cao hoặc thấp hiện tại có tạo thành phân kỳ với RSI không
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)
{
// Phân kỳ giảm giá: Giá tạo đỉnh cao hơn, RSI tạo đỉnh thấp hơn
if (currentHigh > prevHigh && currentRSI < prevRSI)
{
// Đánh dấu phân kỳ trên biểu đồ với định danh duy nhất cho khung thời gian này
Chart.DrawIcon("BearishDivergence" + TimeFrame.ToString() + index, ChartIconType.DownArrow, index, currentHigh, Color.Red);
}
// Phân kỳ tăng giá: Giá tạo đáy thấp hơn, RSI tạo đáy cao hơn
if (currentLow < prevLow && currentRSI > prevRSI)
{
// Đánh dấu phân kỳ trên biểu đồ với định danh duy nhất cho khung thời gian này
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;
// Xác định xem nến hiện tại có thân nhỏ và bấc dài không
bool currentHasLongWicks = (currentHigh - MarketSeries.Close[index]) > currentBody * WickToBodyRatio &&
(MarketSeries.Open[index] - currentLow) > currentBody * WickToBodyRatio;
// Xác định xem nến trước đó có thân nhỏ và bấc dài không
bool prevHasLongWicks = (prevHigh - MarketSeries.Close[index - 1]) > prevBody * WickToBodyRatio &&
(MarketSeries.Open[index - 1] - prevLow) > prevBody * WickToBodyRatio;
return currentHasLongWicks || prevHasLongWicks;
}
}
}