HERE AN UPDATE
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
using NinjaTrader.NinjaScript.Indicators;
#endregion
namespace NinjaTrader.NinjaScript.Indicators
{
public class WolfeWaveVisual : Indicator
{
#region Variables
private List<WolfePoint> peakPoints = new List<WolfePoint>();
private List<WolfePoint> valleyPoints = new List<WolfePoint>();
private int lastProcessedBar = 0;
private bool isDebugMode = false;
private int patternCount = 0;
private class WolfePoint
{
public int BarIndex { get; set; } // Índice de la barra
public double Price { get; set; } // Precio del punto
public DateTime Time { get; set; } // Tiempo asociado
public bool IsPeak { get; set; } // Si es pico (true) o valle (false)
public WolfePoint(int barIndex, double price, DateTime time, bool isPeak)
{
BarIndex = barIndex;
Price = price;
Time = time;
IsPeak = isPeak;
}
}
private class WolfePattern
{
public WolfePoint Point1 { get; set; }
public WolfePoint Point2 { get; set; }
public WolfePoint Point3 { get; set; }
public WolfePoint Point4 { get; set; }
public WolfePoint Point5 { get; set; }
public bool IsBullish { get; set; }
public double TargetPrice { get; set; }
}
#endregion
#region Properties
[NinjaScriptProperty]
[Range(1, 10)]
[Display(Name="Fractal Strength", Description="Number of bars on each side to confirm fractals", Order=1, GroupName="Parameters")]
public int FractalStrength { get; set; }
[NinjaScriptProperty]
[Range(1, 500)]
[Display(Name="Max Lookback Bars", Description="Maximum number of bars to look back for patterns", Order=2, GroupName="Parameters")]
public int MaxLookback { get; set; }
[NinjaScriptProperty]
[Range(0.1, 1.0)]
[Display(Name="Pattern Tolerance", Description="Tolerance for validating patterns (0.1-1.0)", Order=3, GroupName="Parameters")]
public double PatternTolerance { get; set; }
[NinjaScriptProperty]
[Display(Name="Show All ZigZag Points", Description="Show all detected zigzag points", Order=4, GroupName="Visualization")]
public bool ShowAllPoints { get; set; }
[NinjaScriptProperty]
[Display(Name="Bullish Pattern Color", Order=5, GroupName="Visualization")]
public Brush BullishColor { get; set; }
[NinjaScriptProperty]
[Display(Name="Bearish Pattern Color", Order=6, GroupName="Visualization")]
public Brush BearishColor { get; set; }
[NinjaScriptProperty]
[Display(Name="ZigZag Line Color", Order=7, GroupName="Visualization")]
public Brush ZigZagColor { get; set; }
[NinjaScriptProperty]
[Display(Name="Triangle Size", Description="Size of triangles marking pattern completion", Order=8, GroupName="Visualization")]
public int TriangleSize { get; set; }
[NinjaScriptProperty]
[Display(Name="Show Target Projection", Description="Show target price projection", Order=9, GroupName="Visualization")]
public bool ShowTargetProjection { get; set; }
[NinjaScriptProperty]
[Display(Name="Min Points For Pattern", Description="Minimum number of zigzag points needed before checking patterns", Order=10, GroupName="Parameters")]
public int MinPointsForPattern { get; set; }
[NinjaScriptProperty]
[Display(Name="Debug Mode", Description="Show additional information for debugging", Order=11, GroupName="Parameters")]
public bool DebugMode { get; set; }
#endregion
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Wolfe Wave Indicator with Enhanced Visualization";
Name = "WolfeWaveVisual";
Calculate = Calculate.OnEachTick; // Vamos a calcular en cada tick para mayor precisión
IsOverlay = true;
DisplayInDataBox = true;
DrawOnPricePanel = true;
DrawHorizontalGridLines = true;
DrawVerticalGridLines = true;
PaintPriceMarkers = true;
ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
// Establecer valores predeterminados
FractalStrength = 2;
MaxLookback = 200;
PatternTolerance = 0.5;
ShowAllPoints = true;
BullishColor = Brushes.LimeGreen;
BearishColor = Brushes.Crimson;
ZigZagColor = Brushes.DodgerBlue;
TriangleSize = 8;
ShowTargetProjection = true;
MinPointsForPattern = 5;
DebugMode = true;
// Crear plots para asegurar que el indicador aparezca en el gráfico
AddPlot(new Stroke(ZigZagColor, 2), PlotStyle.Line, "ZigZag");
}
else if (State == State.Configure)
{
// No necesita configuración adicional
}
else if (State == State.DataLoaded)
{
// Inicializar variables
peakPoints.Clear();
valleyPoints.Clear();
lastProcessedBar = 0;
isDebugMode = DebugMode;
if (isDebugMode)
{
ClearOutputWindow();
Print("WolfeWaveVisual: Inicializado con Fractal=" + FractalStrength +
", Lookback=" + MaxLookback + ", Tolerance=" + PatternTolerance);
}
}
else if (State == State.Historical)
{
// Limpiar gráfico al empezar
RemoveDrawObjects();
}
}
protected override void OnBarUpdate()
{
// Asegurarse de que tenemos suficientes barras para el análisis
if (CurrentBar < FractalStrength * 2 + 1)
return;
// Actualizar plots para asegurar que el indicador se ve
Values[0][0] = Close[0];
// Solo procesar una vez por barra
if (lastProcessedBar == CurrentBar)
return;
lastProcessedBar = CurrentBar;
// Detectar y almacenar picos y valles
FindZigZagPoints();
// Mostrar ZigZag si está habilitado
if (ShowAllPoints)
{
DrawZigZag();
}
// Solo buscar patrones si tenemos suficientes puntos
if (peakPoints.Count >= MinPointsForPattern/2&&valleyPoints.Count >= MinPointsForPattern/2)
{
// Detectar patrones Wolfe Wave
FindWolfeWavePatterns();
}
}
#region ZigZag Detection
private void FindZigZagPoints()
{
// Primero, verificar si hay un nuevo pico
if (IsPeak(FractalStrength))
{
// Verificar si este pico ya fue agregado
bool alreadyExists = false;
foreach (var point in peakPoints)
{
if (point.BarIndex == CurrentBar - FractalStrength)
{
alreadyExists = true;
break;
}
}
if (!alreadyExists)
{
WolfePoint newPeak = new WolfePoint(
CurrentBar - FractalStrength,
High[FractalStrength],
Time[FractalStrength],
true
);
peakPoints.Add(newPeak);
if (isDebugMode)
{
Print("WolfeWaveVisual: Nuevo pico detectado en barra " + newPeak.BarIndex +
" a precio " + newPeak.Price);
}
// Marca el pico visualmente
string tagName = "Peak_" + newPeak.BarIndex;
Draw.Diamond(this, tagName, false, FractalStrength, High[FractalStrength], Brushes.Gold);
Draw.Text(this, "txt_" + tagName, false, "P", FractalStrength, High[FractalStrength] + 3*TickSize,
0, Brushes.White, new SimpleFont("Arial", 9), TextAlignment.Center, Brushes.DarkGoldenrod, Brushes.DarkGoldenrod, 80);
}
}
// Luego, verificar si hay un nuevo valle
if (IsValley(FractalStrength))
{
// Verificar si este valle ya fue agregado
bool alreadyExists = false;
foreach (var point in valleyPoints)
{
if (point.BarIndex == CurrentBar - FractalStrength)
{
alreadyExists = true;
break;
}
}
if (!alreadyExists)
{
WolfePoint newValley = new WolfePoint(
CurrentBar - FractalStrength,
Low[FractalStrength],
Time[FractalStrength],
false
);
valleyPoints.Add(newValley);
if (isDebugMode)
{
Print("WolfeWaveVisual: Nuevo valle detectado en barra " + newValley.BarIndex +
" a precio " + newValley.Price);
}
// Marca el valle visualmente
string tagName = "Valley_" + newValley.BarIndex;
Draw.Diamond(this, tagName, false, FractalStrength, Low[FractalStrength], Brushes.DarkOrange);
Draw.Text(this, "txt_" + tagName, false, "V", FractalStrength, Low[FractalStrength] - 3*TickSize,
0, Brushes.White, new SimpleFont("Arial", 9), TextAlignment.Center, Brushes.DarkOrange, Brushes.DarkOrange, 80);
}
}
// Limpia puntos antiguos fuera del lookback
CleanupOldPoints();
}
private bool IsPeak(int strength)
{
if (CurrentBar < strength * 2)
return false;
double middleHigh = High[strength];
// Verificar las barras a la izquierda (barras más antiguas)
for (int i = 1; i <= strength; i++)
{
if (High[strength + i] >= middleHigh)
return false;
}
// Verificar las barras a la derecha (barras más recientes)
for (int i = 1; i <= strength; i++)
{
if (High[strength - i] >= middleHigh)
return false;
}
return true;
}
private bool IsValley(int strength)
{
if (CurrentBar < strength * 2)
return false;
double middleLow = Low[strength];
// Verificar las barras a la izquierda (barras más antiguas)
for (int i = 1; i <= strength; i++)
{
if (Low[strength + i] <= middleLow)
return false;
}
// Verificar las barras a la derecha (barras más recientes)
for (int i = 1; i <= strength; i++)
{
if (Low[strength - i] <= middleLow)
return false;
}
return true;
}
private void CleanupOldPoints()
{
// Mantener solo los puntos dentro del lookback
int lookbackBar = Math.Max(0, CurrentBar - MaxLookback);
// Limpiar picos antiguos
peakPoints.RemoveAll(p => p.BarIndex < lookbackBar);
// Limpiar valles antiguos
valleyPoints.RemoveAll(v => v.BarIndex < lookbackBar);
}
private void DrawZigZag()
{
// Combinar todos los puntos
List<WolfePoint> allPoints = new List<WolfePoint>();
allPoints.AddRange(peakPoints);
allPoints.AddRange(valleyPoints);
// Ordenar por índice de barra (del más antiguo al más reciente)
allPoints = allPoints.OrderBy(p => p.BarIndex).ToList();
// Dibujar líneas ZigZag entre puntos consecutivos
for (int i = 0; i < allPoints.Count - 1; i++)
{
WolfePoint current = allPoints[i];
WolfePoint next = allPoints[i + 1];
// Solo conectar si son de tipos diferentes (pico a valle o valle a pico)
if (current.IsPeak != next.IsPeak)
{
string tagName = "ZigZag_" + current.BarIndex + "_" + next.BarIndex;
Draw.Line(this, tagName, false,
CurrentBar - current.BarIndex, current.Price,
CurrentBar - next.BarIndex, next.Price,
ZigZagColor, DashStyleHelper.Solid, 1);
}
}
}
#endregion
#region Wolfe Wave Pattern Detection
private void FindWolfeWavePatterns()
{
FindBullishWolfeWaves();
FindBearishWolfeWaves();
}
private void FindBullishWolfeWaves()
{
// Verificar si tenemos suficientes valles (necesitamos al menos 3) y picos (necesitamos al menos 2)
if (valleyPoints.Count < 3 || peakPoints.Count < 2)
return;
// Ordenar puntos de más recientes a más antiguos
List<WolfePoint> sortedValleys = valleyPoints.OrderByDescending(v => v.BarIndex).ToList();
List<WolfePoint> sortedPeaks = peakPoints.OrderByDescending(p => p.BarIndex).ToList();
// Si no tenemos al menos un valle reciente para el punto 5, no hay patrón
if (sortedValleys.Count == 0)
return;
// Comenzar con el valle más reciente como punto 5
WolfePoint point5 = sortedValleys[0];
// Buscar un pico antes del punto 5 para ser el punto 4
WolfePoint point4 = null;
foreach (var peak in sortedPeaks)
{
if (peak.BarIndex < point5.BarIndex&&peak.Price > point5.Price)
{
point4 = peak;
break;
}
}
if (point4 == null)
return;
// Buscar un valle antes del punto 4 para ser el punto 3
WolfePoint point3 = null;
foreach (var valley in sortedValleys)
{
if (valley.BarIndex < point4.BarIndex&&valley.Price < point4.Price)
{
point3 = valley;
break;
}
}
if (point3 == null)
return;
// Buscar un pico antes del punto 3 para ser el punto 2
WolfePoint point2 = null;
foreach (var peak in sortedPeaks)
{
if (peak.BarIndex < point3.BarIndex&&peak.Price > point3.Price)
{
point2 = peak;
break;
}
}
if (point2 == null)
return;
// Buscar un valle antes del punto 2 para ser el punto 1
WolfePoint point1 = null;
foreach (var valley in sortedValleys)
{
if (valley.BarIndex < point2.BarIndex&&valley.Price < point2.Price)
{
point1 = valley;
break;
}
}
if (point1 == null)
return;
// Verificar si es un patrón Wolfe Wave Bullish válido
if (IsBullishWolfeWaveValid(point1, point2, point3, point4, point5))
{
// Calcular el objetivo de precio
double targetPrice = CalculateBullishTarget(point1, point3, point5);
// Dibujar el patrón
DrawBullishWolfeWave(point1, point2, point3, point4, point5, targetPrice);
if (isDebugMode)
{
Print("WolfeWaveVisual: Patrón ALCISTA encontrado en barra " + CurrentBar);
}
}
}
private void FindBearishWolfeWaves()
{
// Verificar si tenemos suficientes picos (necesitamos al menos 3) y valles (necesitamos al menos 2)
if (peakPoints.Count < 3 || valleyPoints.Count < 2)
return;
// Ordenar puntos de más recientes a más antiguos
List<WolfePoint> sortedPeaks = peakPoints.OrderByDescending(p => p.BarIndex).ToList();
List<WolfePoint> sortedValleys = valleyPoints.OrderByDescending(v => v.BarIndex).ToList();
// Si no tenemos al menos un pico reciente para el punto 5, no hay patrón
if (sortedPeaks.Count == 0)
return;
// Comenzar con el pico más reciente como punto 5
WolfePoint point5 = sortedPeaks[0];
// Buscar un valle antes del punto 5 para ser el punto 4
WolfePoint point4 = null;
foreach (var valley in sortedValleys)
{
if (valley.BarIndex < point5.BarIndex&&valley.Price < point5.Price)
{
point4 = valley;
break;
}
}
if (point4 == null)
return;
// Buscar un pico antes del punto 4 para ser el punto 3
WolfePoint point3 = null;
foreach (var peak in sortedPeaks)
{
if (peak.BarIndex < point4.BarIndex&&peak.Price > point4.Price)
{
point3 = peak;
break;
}
}
if (point3 == null)
return;
// Buscar un valle antes del punto 3 para ser el punto 2
WolfePoint point2 = null;
foreach (var valley in sortedValleys)
{
if (valley.BarIndex < point3.BarIndex&&valley.Price < point3.Price)
{
point2 = valley;
break;
}
}
if (point2 == null)
return;
// Buscar un pico antes del punto 2 para ser el punto 1
WolfePoint point1 = null;
foreach (var peak in sortedPeaks)
{
if (peak.BarIndex < point2.BarIndex&&peak.Price > point2.Price)
{
point1 = peak;
break;
}
}
if (point1 == null)
return;
// Verificar si es un patrón Wolfe Wave Bearish válido
if (IsBearishWolfeWaveValid(point1, point2, point3, point4, point5))
{
// Calcular el objetivo de precio
double targetPrice = CalculateBearishTarget(point1, point3, point5);
// Dibujar el patrón
DrawBearishWolfeWave(point1, point2, point3, point4, point5, targetPrice);
if (isDebugMode)
{
Print("WolfeWaveVisual: Patrón BAJISTA encontrado en barra " + CurrentBar);
}
}
}
private bool IsBullishWolfeWaveValid(WolfePoint p1, WolfePoint p2, WolfePoint p3, WolfePoint p4, WolfePoint p5)
{
// Verificar la secuencia de tiempo
if (!(p1.BarIndex < p2.BarIndex&&p2.BarIndex < p3.BarIndex&&p3.BarIndex < p4.BarIndex&&p4.BarIndex < p5.BarIndex))
{
return false;
}
// Verificar que los puntos alternan entre picos y valles
if (!(p1.IsPeak == false&&p2.IsPeak == true&&p3.IsPeak == false&&p4.IsPeak == true&&p5.IsPeak == false))
{
return false;
}
// Para patrones bullish, el punto 5 debe ser más bajo que el punto 3
if (p5.Price >= p3.Price)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BULLISH rechazado - Punto 5 no es más bajo que Punto 3");
return false;
}
// El punto 4 debe ser más alto que el punto 2
if (p4.Price <= p2.Price)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BULLISH rechazado - Punto 4 no es más alto que Punto 2");
return false;
}
// Calcular pendientes
double slope13 = CalculateSlope(p1, p3);
double slope24 = CalculateSlope(p2, p4);
double slope14 = CalculateSlope(p1, p4);
// En un patrón bullish, la pendiente 1-3 debe ser menos positiva (o más negativa) que la pendiente 2-4
// Permitimos cierta tolerancia
double tolerance = Math.Abs(slope24 * PatternTolerance);
if (slope13 >= slope24 + tolerance)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BULLISH rechazado - Pendiente 1-3 no converge con 2-4");
return false;
}
return true;
}
private bool IsBearishWolfeWaveValid(WolfePoint p1, WolfePoint p2, WolfePoint p3, WolfePoint p4, WolfePoint p5)
{
// Verificar la secuencia de tiempo
if (!(p1.BarIndex < p2.BarIndex&&p2.BarIndex < p3.BarIndex&&p3.BarIndex < p4.BarIndex&&p4.BarIndex < p5.BarIndex))
{
return false;
}
// Verificar que los puntos alternan entre picos y valles
if (!(p1.IsPeak == true&&p2.IsPeak == false&&p3.IsPeak == true&&p4.IsPeak == false&&p5.IsPeak == true))
{
return false;
}
// Para patrones bearish, el punto 5 debe ser más alto que el punto 3
if (p5.Price <= p3.Price)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BEARISH rechazado - Punto 5 no es más alto que Punto 3");
return false;
}
// El punto 4 debe ser más bajo que el punto 2
if (p4.Price >= p2.Price)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BEARISH rechazado - Punto 4 no es más bajo que Punto 2");
return false;
}
// Calcular pendientes
double slope13 = CalculateSlope(p1, p3);
double slope24 = CalculateSlope(p2, p4);
double slope14 = CalculateSlope(p1, p4);
// En un patrón bearish, la pendiente 1-3 debe ser más positiva (o menos negativa) que la pendiente 2-4
// Permitimos cierta tolerancia
double tolerance = Math.Abs(slope24 * PatternTolerance);
if (slope13 <= slope24 - tolerance)
{
if (isDebugMode) Print("WolfeWaveVisual: Patrón BEARISH rechazado - Pendiente 1-3 no converge con 2-4");
return false;
}
return true;
}
private double CalculateSlope(WolfePoint start, WolfePoint end)
{
if (end.BarIndex == start.BarIndex)
return 0;
return (end.Price - start.Price) / (end.BarIndex - start.BarIndex);
}
#endregion
#region Visualization Methods
private void DrawBullishWolfeWave(WolfePoint p1, WolfePoint p2, WolfePoint p3, WolfePoint p4, WolfePoint p5, double targetPrice)
{
patternCount++;
string patternId = "Bull_" + CurrentBar + "_" + patternCount;
// Calcular las coordenadas de barra para el dibujo (distancia desde la barra actual)
int x1 = CurrentBar - p1.BarIndex;
int x2 = CurrentBar - p2.BarIndex;
int x3 = CurrentBar - p3.BarIndex;
int x4 = CurrentBar - p4.BarIndex;
int x5 = CurrentBar - p5.BarIndex;
// Dibujar líneas conectando los puntos
Draw.Line(this, patternId + "_1_2", false, x1, p1.Price, x2, p2.Price, BullishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_2_3", false, x2, p2.Price, x3, p3.Price, BullishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_3_4", false, x3, p3.Price, x4, p4.Price, BullishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_4_5", false, x4, p4.Price, x5, p5.Price, BullishColor, DashStyleHelper.Solid, 2);
// Dibujar líneas de proyección si está habilitado
if (ShowTargetProjection)
{
// Extender la línea 1-3 para proyectar el objetivo de precio
// Calculamos la barra donde la proyección alcanzaría el objetivo
double slope13 = CalculateSlope(p1, p3);
int barsFromPoint1 = p5.BarIndex - p1.BarIndex;
int projectionBars = barsFromPoint1 + Math.Min(50, barsFromPoint1 / 2);
// Dibujar proyección desde punto 1 hasta el objetivo
Draw.Line(this, patternId + "_target", false,
x1, p1.Price,
CurrentBar - (p1.BarIndex + projectionBars), targetPrice,
Brushes.Gold, DashStyleHelper.Dash, 2);
// Etiquetar el precio objetivo
Draw.Text(this, patternId + "_targetlabel", false,
"Target: " + FormatPrice(targetPrice),
CurrentBar - (p1.BarIndex + projectionBars/2), targetPrice,
0, Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGoldenrod, Brushes.DarkGoldenrod, 80);
}
// Etiquetar los puntos
Draw.Text(this, patternId + "_p1", false, "1", x1, p1.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGreen, Brushes.DarkGreen, 80);
Draw.Text(this, patternId + "_p2", false, "2", x2, p2.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGreen, Brushes.DarkGreen, 80);
Draw.Text(this, patternId + "_p3", false, "3", x3, p3.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGreen, Brushes.DarkGreen, 80);
Draw.Text(this, patternId + "_p4", false, "4", x4, p4.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGreen, Brushes.DarkGreen, 80);
Draw.Text(this, patternId + "_p5", false, "5", x5, p5.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGreen, Brushes.DarkGreen, 80);
// Dibujar un triángulo para marcar el patrón completo
Draw.TriangleUp(this, patternId + "_marker", false, x5, p5.Price - 2 * TickSize, BullishColor);
Alert("WolfeWave_Bull", Priority.Medium, "Wolfe Wave Bullish Pattern",
NinjaTrader.Core.Globals.InstallDir + @"\sounds\Alert1.wav",
0, Brushes.LimeGreen, Brushes.Black);
}
private void DrawBearishWolfeWave(WolfePoint p1, WolfePoint p2, WolfePoint p3, WolfePoint p4, WolfePoint p5, double targetPrice)
{
patternCount++;
string patternId = "Bear_" + CurrentBar + "_" + patternCount;
// Calcular las coordenadas de barra para el dibujo (distancia desde la barra actual)
int x1 = CurrentBar - p1.BarIndex;
int x2 = CurrentBar - p2.BarIndex;
int x3 = CurrentBar - p3.BarIndex;
int x4 = CurrentBar - p4.BarIndex;
int x5 = CurrentBar - p5.BarIndex;
// Dibujar líneas conectando los puntos
Draw.Line(this, patternId + "_1_2", false, x1, p1.Price, x2, p2.Price, BearishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_2_3", false, x2, p2.Price, x3, p3.Price, BearishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_3_4", false, x3, p3.Price, x4, p4.Price, BearishColor, DashStyleHelper.Solid, 2);
Draw.Line(this, patternId + "_4_5", false, x4, p4.Price, x5, p5.Price, BearishColor, DashStyleHelper.Solid, 2);
// Dibujar líneas de proyección si está habilitado
if (ShowTargetProjection)
{
// Extender la línea 1-3 para proyectar el objetivo de precio
// Calculamos la barra donde la proyección alcanzaría el objetivo
double slope13 = CalculateSlope(p1, p3);
int barsFromPoint1 = p5.BarIndex - p1.BarIndex;
int projectionBars = barsFromPoint1 + Math.Min(50, barsFromPoint1 / 2);
// Dibujar proyección desde punto 1 hasta el objetivo
Draw.Line(this, patternId + "_target", false,
x1, p1.Price,
CurrentBar - (p1.BarIndex + projectionBars), targetPrice,
Brushes.Gold, DashStyleHelper.Dash, 2);
// Etiquetar el precio objetivo
Draw.Text(this, patternId + "_targetlabel", false,
"Target: " + FormatPrice(targetPrice),
CurrentBar - (p1.BarIndex + projectionBars/2), targetPrice,
0, Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkGoldenrod, Brushes.DarkGoldenrod, 80);
}
// Etiquetar los puntos
Draw.Text(this, patternId + "_p1", false, "1", x1, p1.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkRed, Brushes.DarkRed, 80);
Draw.Text(this, patternId + "_p2", false, "2", x2, p2.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkRed, Brushes.DarkRed, 80);
Draw.Text(this, patternId + "_p3", false, "3", x3, p3.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkRed, Brushes.DarkRed, 80);
Draw.Text(this, patternId + "_p4", false, "4", x4, p4.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkRed, Brushes.DarkRed, 80);
Draw.Text(this, patternId + "_p5", false, "5", x5, p5.Price, 0,
Brushes.White, new SimpleFont("Arial", 10), TextAlignment.Center,
Brushes.DarkRed, Brushes.DarkRed, 80);
// Dibujar un triángulo para marcar el patrón completo
Draw.TriangleDown(this, patternId + "_marker", false, x5, p5.Price + 2 * TickSize, BearishColor);
Alert("WolfeWave_Bear", Priority.Medium, "Wolfe Wave Bearish Pattern",
NinjaTrader.Core.Globals.InstallDir + @"\sounds\Alert2.wav",
0, Brushes.Crimson, Brushes.Black);
}
private double CalculateBullishTarget(WolfePoint p1, WolfePoint p3, WolfePoint p5)
{
double slope = CalculateSlope(p1, p3);
int barDiff = p5.BarIndex - p1.BarIndex;
double targetPrice = p1.Price + slope * barDiff;
// Asegurar que el objetivo sea razonable (si es demasiado bajo, ajustarlo)
return Math.Max(targetPrice, p5.Price + 5 * TickSize);
}
private double CalculateBearishTarget(WolfePoint p1, WolfePoint p3, WolfePoint p5)
{
double slope = CalculateSlope(p1, p3);
int barDiff = p5.BarIndex - p1.BarIndex;
double targetPrice = p1.Price + slope * barDiff;
// Asegurar que el objetivo sea razonable (si es demasiado alto, ajustarlo)
return Math.Min(targetPrice, p5.Price - 5 * TickSize);
}
private string FormatPrice(double price)
{
return Instrument.MasterInstrument.FormatPrice(price);
}
#endregion
}
}