with the help of chatgpt i have programmed an indicator that calculates a dynamic value area and displays it dynamically in the chart. I am testing the value area in the 1 minute chart. As soon as I load the indicator into the chart, the value area is displayed correctly with the historical data for the current session. If new candles are now formed in the chart, the Value Area is contracted, as the calculation is applied to each individual bar and not to the entire session. This means that the lines from the Value Area with the historical data are not continued correctly. I then always have to press F5 so that the chart is updated and displayed correctly. According to ChatGPT I have received the following answer or problem solution:
-----------------------
An issue with inconsistency between historical data and live incoming data when calculating the Value Area in my indicator.
Problem Summary: When adding the indicator, the Value Areas for historical data are calculated and displayed correctly. However, when new candles form live, the Value Area is only calculated based on the new candle, leading to inconsistent results. This means that the Value Area for new candles is only displayed correctly after a manual refresh.
Cause: The calculation occurs in two steps:
- Historical data are fully calculated when the indicator is added.
- New candles are only calculated dynamically without initially considering the entire session.
-----------------------
I have tried thousands of variations of code to properly align the current candles with the historical Value Area, but without success. I am in despair. That's why I'm reaching out to you for help, asking for tips or advice so that I can finally solve this problem. I'm slowly getting desperate. Thank you very much.
Here are some pictures for visual clarification:
Picture 1: After the indicator has been added to the chart and the current bars are calculated, the value areas are displayed in the chart. The value areas adapt to each individual candle and not to the historical value area.
Picture 2: This is how the value areas should actually be displayed by default
Here are the most important methods of my code :
protected override void OnBarUpdate()
{
if (BarsInProgress != 0) return;
// Initialisiere SessionIterator und erhalte das aktuelle Session-Datum
SessionIterator sessionIterator = new SessionIterator(Bars);
DateTime sessionDate = sessionIterator.GetTradingDay(Time[0]);
ComputeSessionAndTimezones();
if (sessionDate != lastSessionDate)
{
if (lastSessionDate != DateTime.MinValue)
{
sessionEndBarIndex = CurrentBar - 1; // Ende der vorherigen Session setzen und Session-Daten aktualisieren
UpdateCurrentSessionValues(); // Session-Werte in Liste speichern
currentProfileVA1 = new Profile(); // Neue Session starten, Profile zurücksetzen für Value Area
currentProfileVA2 = new Profile(); // Neue Session starten, Profile zurücksetzen für Value Area 2
}
lastSessionDate = sessionDate;
sessionStartBarIndex = CurrentBar;
}
if (Bars.IsLastBarOfSession)
{
sessionEndBarIndex = CurrentBar;
UpdateCurrentSessionValues(); // Finalisiere die Session-Werte
}
UpdateVolumeProfile(Close[0], Volume[0], currentProfileVA1); // Update für VA1
UpdateVolumeProfile(Close[0], Volume[0], currentProfileVA2); // Update für VA2
CalculateDynamicValueArea1(currentProfileVA1, volatilityFactor);
CalculateDynamicValueArea2(currentProfileVA2, volatilityFactor);
if (Bars.IsLastBarOfSession)
{
vahLevels.Add((float)vah);
valLevels.Add((float)val);
pocLevels.Add((float)poc);
vahLevels2.Add((float)vah2); // Hinzufügen für VAH der zweiten Value Area
valLevels2.Add((float)val2); // Hinzufügen für VAL der zweiten Value Area
// Optional: pocLevels2.Add((float)poc2); // Wenn POC2 aufgezeichnet werden soll
}
ExtendValueAreaSegments();
ForceRefresh();
}
#region UpdateVolumeProfile() --- Anpassung UpdateVolumeProfile zur separaten Nutzung für beide Profiles
private void UpdateVolumeProfile(double price, double volume, Profile profile)
{
if (!profile.l.ContainsKey(price);
profile.l[price] = new RowData();
profile.l[price].tv += volume;
}
#endregion
#region Berechnung für VAH, VAL und POC
#region CalculateDynamicValueArea1() --- Dynamische Value Area 1 berechnen
private void CalculateDynamicValueArea1(Profile profile, double volatilityFactor)
{
// Berechnung des POC auf Basis des höchsten Volumens
poc = CalculatePOC(profile);
// Berechne das Zielvolumen für die Value Area basierend auf dem `volatilityFactor`
double totalVolume = profile.l.Sum(x => x.Value.tv);
double targetVolume = totalVolume * volatilityFactor;
double cumulativeVolume = 0;
// Sortiere die Preislevels nach der Nähe zum POC, um die Value Area robust zu berechnen
var priceLevels = profile.l.OrderBy(x => Math.Abs(x.Key - poc)).ToList();
List<double> pricesWithinValueArea = new List<double>();
foreach (var level in priceLevels)
{
cumulativeVolume += level.Value.tv;
pricesWithinValueArea.Add(level.Key);
if (cumulativeVolume >= targetVolume)
break;
}
// Setze die `VAH` und `VAL` basierend auf dem umschlossenen Bereich
vah = pricesWithinValueArea.Max();
val = pricesWithinValueArea.Min();
}
#endregion
#region CalculateDynamicValueArea2() --- Dynamische Value Area 2 berechnen
private void CalculateDynamicValueArea2(Profile profile, double volatilityFactor)
{
double totalVolume = profile.l.Sum(x => x.Value.tv);
var priceVolumePairs = profile.l.ToList();
priceVolumePairs.Sort((pair1, pair2) => pair2.Value.tv.CompareTo(pair1.Value.tv));
double targetVolume = totalVolume * volatilityFactor;
List<double> pricesWithinValueArea = new List<double>();
double cumulativeVolume = 0;
foreach (var pair in priceVolumePairs)
{
cumulativeVolume += pair.Value.tv;
pricesWithinValueArea.Add(pair.Key);
if (cumulativeVolume >= targetVolume)
break;
}
vah2 = pricesWithinValueArea.Max();
val2 = pricesWithinValueArea.Min();
//poc2 = priceVolumePairs[0].Key;
}
#endregion
#endregion
#region ExtendValueAreaSegments() -- Value Area Segmente (VAH, VAL, POC) dynamisch aktualisieren und erweitern
private void ExtendValueAreaSegments()
{
// Value Area 1
if (!sessionSegmentsVA1.ContainsKey(lastSessionDate))
{
sessionSegmentsVA1[lastSessionDate] = new List<ValueAreaSegment>();
}
if (sessionSegmentsVA1[lastSessionDate].Any())
{
var lastSegmentVA1 = sessionSegmentsVA1[lastSessionDate].Last();
if (lastSegmentVA1.EndBarIndex < CurrentBar - 1) // Überprüfen, ob die aktuelle Bar weiter ist
{
var extendedSegmentVA1 = new ValueAreaSegment
{
StartBarIndex = lastSegmentVA1.EndBarIndex + 1,
EndBarIndex = CurrentBar,
Vah = vah,
Val = val,
Poc = poc
};
sessionSegmentsVA1[lastSessionDate].Add(extendedSegmentVA1);
Print($"Segment VA1 erweitert: StartBar={extendedSegmentVA1.StartBarIndex}, EndBar={extendedSegmentVA1.EndBarIndex}");
}
}
else
{
sessionSegmentsVA1[lastSessionDate].Add(new ValueAreaSegment
{
StartBarIndex = CurrentBar,
EndBarIndex = CurrentBar,
Vah = vah,
Val = val,
Poc = poc
});
Print($"Neues Segment VA1 erstellt: StartBar={CurrentBar}, EndBar={CurrentBar}");
}
// Value Area 2
if (!sessionSegmentsVA2.ContainsKey(lastSessionDate))
{
sessionSegmentsVA2[lastSessionDate] = new List<ValueAreaSegment>();
}
if (sessionSegmentsVA2[lastSessionDate].Any())
{
var lastSegmentVA2 = sessionSegmentsVA2[lastSessionDate].Last();
if (lastSegmentVA2.EndBarIndex < CurrentBar - 1) // Überprüfen, ob die aktuelle Bar weiter ist
{
var extendedSegmentVA2 = new ValueAreaSegment
{
StartBarIndex = lastSegmentVA2.EndBarIndex + 1,
EndBarIndex = CurrentBar,
Vah2 = vah2,
Val2 = val2,
Poc2 = poc2
};
sessionSegmentsVA2[lastSessionDate].Add(extendedSegmentVA2);
Print($"Segment VA2 erweitert: StartBar={extendedSegmentVA2.StartBarIndex}, EndBar={extendedSegmentVA2.EndBarIndex}");
}
}
else
{
sessionSegmentsVA2[lastSessionDate].Add(new ValueAreaSegment
{
StartBarIndex = CurrentBar,
EndBarIndex = CurrentBar,
Vah2 = vah2,
Val2 = val2,
Poc2 = poc2
});
Print($"Neues Segment VA2 erstellt: StartBar={CurrentBar}, EndBar={CurrentBar}");
}
}
#endregion
#region UpdateCurrentSessionValues() --- Update der aktuellen Session Values
private void UpdateCurrentSessionValues()
{
// Das aktuelle Session-Datum ermitteln
var sessionDate = GetSessionStartDate(Time[0]);
var currentSession = sessionValuesList.FirstOrDefault(sv => sv.SessionDate.Date == sessionDate.Date);
// Falls keine Sitzung gefunden wird, eine neue hinzufügen
if (currentSession == null)
{
currentSession = new SessionValues { SessionDate = sessionDate };
sessionValuesList.Add(currentSession);
}
// Setze die Werte für die aktuelle Session (Value Area 1)
currentSession.POC = poc;
currentSession.VAH = vah;
currentSession.VAL = val;
// Setze die Werte für Value Area 2
currentSession.POC2 = poc2;
currentSession.VAH2 = vah2;
currentSession.VAL2 = val2;
}
#endregion

Comment