Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Problem with the display of the Value Area

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Problem with the display of the Value Area

    hello everyone,
    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:
    1. Historical data are fully calculated when the indicator is added.
    2. New candles are only calculated dynamically without initially considering the entire session.
    Solution Approach: To fix the inconsistency, the Value Area for each new candle must be calculated based on the entire current session (including historical data). The calculations in the OnBarUpdate and OnMarketData methods should be adjusted to take into account the entire session, ensuring the Value Area is displayed correctly immediately.
    -----------------------
    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.
    ​​ Click image for larger version  Name:	Bild 2.jpg Views:	1 Size:	144.4 KB ID:	1324155
    Picture 2: This is how the value areas should actually be displayed by default
    Click image for larger version

Name:	Bild 3.jpg
Views:	77
Size:	125.6 KB
ID:	1324160
    Here are the most important methods of my code :
    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
    
    ​
    Last edited by IntyRocket; 11-10-2024, 02:32 PM.

    #2
    Hello IntyRocket,

    Is this script processing every tick with Calculate.OnEachTick?

    If so, have you enabled TickReplay?

    Note, OnMarketData() will only update in historical if TickReplay is enabled. Further, accessing the data is slightly different.


    Or are you adding a 1 tick series to process individual tick values?

    Or does this script only react to end of bar data?
    Chelsea B.NinjaTrader Customer Service

    Comment


      #3
      Thanks for the help. The indicator was set to Calculate.OnEachTick, but TickReplay was disabled. I have now switched the indicator to OnBarClose, and now the Value Area is correctly carried forward. Especially since I need the indicator for a strategy that will run in LineBreak, it would make more sense to set both the indicator and the strategy to Calculate.OnBarClose.

      Comment

      Latest Posts

      Collapse

      Topics Statistics Last Post
      Started by Geovanny Suaza, 02-11-2026, 06:32 PM
      0 responses
      646 views
      0 likes
      Last Post Geovanny Suaza  
      Started by Geovanny Suaza, 02-11-2026, 05:51 PM
      0 responses
      367 views
      1 like
      Last Post Geovanny Suaza  
      Started by Mindset, 02-09-2026, 11:44 AM
      0 responses
      108 views
      0 likes
      Last Post Mindset
      by Mindset
       
      Started by Geovanny Suaza, 02-02-2026, 12:30 PM
      0 responses
      569 views
      1 like
      Last Post Geovanny Suaza  
      Started by RFrosty, 01-28-2026, 06:49 PM
      0 responses
      573 views
      1 like
      Last Post RFrosty
      by RFrosty
       
      Working...
      X