Untitled

 avatar
unknown
csharp
12 days ago
19 kB
6
Indexable
#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 System.Text.Json.Serialization;
using System.Text.Json;
using NinjaTrader.Gui.Updater;
using System.Collections;
using SharpDX;
#endregion

//This namespace holds Indicators in this folder and is required. Do not change it. 
namespace NinjaTrader.NinjaScript.Indicators
{
	public class BetterSwing : Indicator
	{

        #region Properties
        [NinjaScriptProperty]
        [Display(Name = "Enable Swing Range Info", GroupName = "Parameters", Order = 0)]
        public bool EnableSwingPowerDrawing { get; set; }

        [NinjaScriptProperty]
        [Display(Name = "Swing percentage", GroupName = "Swing Settings", Order = 1)]
        [Range(0.01, 1)]
        public double ZigZagPercentage { get; set; }

        [NinjaScriptProperty]
        [Display(Name = "Enable ZigZag", GroupName = "Parameters", Order = 2)]
        public bool EnableZigZag { get; set; }

        #endregion


        private double tickSize;


        private SwingLinkedList Swings;

        public SwingLinkedList GetSwings
        {
            get
            {
                Update();
                return Swings;
            }
        }

        private ZigZag ZigZagIndicator;


        // for fibo enum 
        private Dictionary<FibonacciLevels, double> fibDictionary;

        public enum SwingType
        {
            SWING_LONG,
            SWING_SHORT
        }

        public ZigZag GetZigZagInstance()
        {
            Update();
            return ZigZagIndicator;
        }

        #region ClassMySwing

        public class SwingLinkedList : IEnumerable<MySwing>
        {


            public MySwing Head { get; private set; }
            public MySwing Tail { get; private set; }

            public int Count { get; private set; }

            public SwingLinkedList()
            {
                Head = null;
                Tail = null;
            }

            public void Append(MySwing swing)
            {
                if (Head == null)
                {
                    Head = swing;
                    Tail = swing;
                }
                else
                {
                    Tail.Next = swing;
                    swing.Previous = Tail;
                    Tail = swing;
                    //if (owner != null)
                        //owner.Print("Dodano nowy Swing do listy");
                }
                Count++;
            }

            public override bool Equals(object obj)
            {
                if(obj is SwingLinkedList other)
                {
                    if(this.Count != other.Count)
                    {
                        return false;
                    }

                    var thisNode = this.Head;
                    var otherNode = other.Head;

                    // 3. Iteracyjne porównanie każdego węzła
                    while (thisNode != null && otherNode != null)
                    {
                        if (!thisNode.Equals(otherNode)) // <-- PORÓWNANIE PRZEZ MySwing
                            return false;

                        thisNode = thisNode.Next;
                        otherNode = otherNode.Next;
                    }
                    return true; // Jeśli wszystkie węzły są równe, listy są równe
                }
                return false;
            }

            public override int GetHashCode()
            {
                unchecked
                {
                    int hash = 17;
                    var node = this.Head;

                    while (node != null)
                    {
                        hash = hash * 31 + node.GetHashCode(); // Użycie hashcode MySwing
                        node = node.Next;
                    }

                    return hash;
                }
            }


            // Implementacja interfejsu IEnumerable<MySwing>
            public IEnumerator<MySwing> GetEnumerator()
            {
                MySwing current = Head;
                while (current != null)
                {
                    yield return current;
                    current = current.Next;
                }
            }

            // Wymagana implementacja interfejsu IEnumerable
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }


        }

        public class MySwing : IEquatable<MySwing>
		{

            
            public MySwing Previous { get; set; }

            
            private MySwing? _next;

            public MySwing Next
            {
                get
                {
                    return _next;   
                } 
                set
                {
                    if (value != null)
                    {
                        // Naprzemienność ekstremów w zależności od typu
                        if (this.SwingType == SwingType.SWING_LONG)
                        {
                            // Wymuszamy identyczny High
                            if (this.HighPrice != value.HighPrice)
                                throw new InvalidOperationException(
                                    "Jeśli poprzedni swing był LONG, to następny (SHORT) musi mieć ten sam High."
                                );
                        }
                        else if (this.SwingType == SwingType.SWING_SHORT)
                        {
                            // Wymuszamy identyczny Low
                            if (this.LowPrice != value.LowPrice)
                                throw new InvalidOperationException(
                                    "Jeśli poprzedni swing był SHORT, to następny (LONG) musi mieć ten sam Low."
                                );
                        }
                    }

                    // Link zwrotny
                    value.Previous = this;
                    _next = value;
                }
            }


            // Cechy Swingu
            public SwingType SwingType { get; }

            // Get‐only property; ustalana tylko w konstruktorze
            public double LowPrice { get; }
            public double HighPrice { get; }
            public double SwingRange { get; }

            public DateTime LowTime { get; }
            public DateTime HighTime { get; }

            public MySwing(SwingType swingType, double lowPrice,  double highPrice, double tickSize, DateTime lowTime, DateTime highTime)
            {
                SwingType = swingType;
                LowPrice = lowPrice;
                HighPrice = highPrice;
                SwingRange = (highPrice - lowPrice) / tickSize;
                LowTime = lowTime;
                HighTime = highTime;
             }




            public double GetSwingPower(TimeSpan totalMinutes)
            {
                double duration = totalMinutes.TotalMinutes;
                return (SwingRange / duration );
            }

            public SwingType GetSwingType()
            {
                return SwingType;
            }

            public static double GetFibLevel(SwingType swingType, double lowPrice, double highPrice, double level)
            {
                double priceAmplitude = Math.Abs(highPrice - lowPrice);

                if (swingType == SwingType.SWING_SHORT)
                {
                    // Swing short: poziom 0% jest na dole, 100% u g�ry
                    return highPrice - (priceAmplitude * (1 - level));
                }
                else if (swingType == SwingType.SWING_LONG)
                {
                    // Swing long: poziom 0% jest na dole, 100% u g�ry
                    return lowPrice + (priceAmplitude * (1 - level));
                }
                else
                {
                    throw new ArgumentException("Invalid swingType. Expected 'Up' or 'Down'.");
                }
            }

            public double GetFibLevel(double level)
            {
                double priceAmplitude = Math.Abs(HighPrice - LowPrice);
                if (SwingType == SwingType.SWING_SHORT)
                {
                    return HighPrice - (priceAmplitude * (1 - level));
                }
                else if (SwingType == SwingType.SWING_LONG)
                {
                    return LowPrice + (priceAmplitude * (1 - level)); ;
                }
                else
                {
                    throw new ArgumentException("Invalid swingType. Expected 'Up' or 'Down'.");
                }
            }

            // Equals + IEquatable
            public override bool Equals(object obj) => Equals(obj as MySwing);

            public bool Equals(MySwing other)
            {
                if (other == null)
                    return false;

                return SwingType == other.SwingType
                    && LowPrice.Equals(other.LowPrice)
                    && HighPrice.Equals(other.HighPrice)
                    && LowTime.Equals(other.LowTime)
                    && HighTime.Equals(other.HighTime);
            }

            public override int GetHashCode()
            {
                unchecked
                {
                    int hash = 17;
                    hash = hash * 23 + SwingType.GetHashCode();
                    hash = hash * 23 + LowPrice.GetHashCode();
                    hash = hash * 23 + HighPrice.GetHashCode();
                    hash = hash * 23 + LowTime.GetHashCode();
                    hash = hash * 23 + HighTime.GetHashCode();
                    return hash;
                }
            }

        }

        #endregion


        protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"Enter the description for your new custom Indicator here.";
				Name										= "BetterSwing";
				Calculate									= Calculate.OnBarClose;
				IsOverlay									= true;
				DisplayInDataBox							= true;
				DrawOnPricePanel							= true;
				DrawHorizontalGridLines						= true;
				DrawVerticalGridLines						= true;
				PaintPriceMarkers							= true;
				ScaleJustification							= NinjaTrader.Gui.Chart.ScaleJustification.Right;
				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
				//See Help Guide for additional information.
				IsSuspendedWhileInactive					= true;

                AddPlot(Brushes.Blue, "ZigZag");

                ZigZagPercentage = 0.25;
            }
			else if (State == State.Configure)
			{
			}
            else if (State == State.DataLoaded)
            {
                tickSize = Instrument.MasterInstrument.TickSize;

                Swings = new SwingLinkedList();


                ZigZagIndicator = ZigZag(DeviationType.Percent, ZigZagPercentage, true);
                

            }
        }

		protected override void OnBarUpdate()
		{
            if (BarsInProgress != 0) return;

            if (CurrentBars[0] < 3)
            {
                return;
            }

            int currentLowIdx = -1, currentHighIdx = -1;
            int previousLowIdx = -1, previousHighIdx = -1;

            double currentLowPrice = 0, currentHighPrice = 0;
            double previousLowPrice = 0, previousHighPrice = 0;


                // pobranie indeksow ostatnich high and low - szukamy od "teraz"
            currentLowIdx = ZigZagIndicator.LowBar(0, 1, CurrentBars[0] - 1);
            currentHighIdx = ZigZagIndicator.HighBar(0, 1, CurrentBars[0] - 1);

            // walidacja czy znaleziono indeksy
            if (currentLowIdx == -1 || currentHighIdx == -1)
            {
                return;
            }

            // pobranie wartosci cen dla current swing
            currentLowPrice = Low[currentLowIdx];
            currentHighPrice = High[currentHighIdx];

            DateTime currentLowTime = Time[currentLowIdx];
            DateTime currentHighTime = Time[currentHighIdx];

                // Tworzenie obiektu current Swing nie ma sensu, ponieważ aktualny Swing aktualizuje sie na bieżąco, więc tylko marnujemy pamięc na stercie

                //CurrentSwing = new MySwing(currentLowIdx, currentLowPrice, currentLowTime,  currentHighIdx, currentHighPrice, currentHighTime, tickSize);


            if(currentHighTime < currentLowTime) {
                    // Jezeli czas szczytu aktualnie rozwijajacego sie Swingu jest mniejszy (wczesniejszy) niz czas low to znaczy ze to ruch spadkowy
                previousHighIdx = currentHighIdx;
                previousLowIdx = ZigZagIndicator.LowBar(previousHighIdx - 1, 1, CurrentBars[0] - 1);
            } else {
                    // W odwrotnym przypadku jest to ruch wzrostowy
                previousLowIdx = currentLowIdx;
                previousHighIdx = ZigZagIndicator.HighBar(previousLowIdx - 1, 1, CurrentBars[0] - 1);
            }

            if (previousLowIdx < 0 || previousHighIdx < 0)
            {
                return;
            }

            previousLowPrice = Low[previousLowIdx];
            previousHighPrice = High[previousHighIdx];

            DateTime previousLowTime = Time[previousLowIdx];
            DateTime previousHighTime = Time[previousHighIdx];

            MySwing FormedSwing;

            if (previousLowTime < previousHighTime)
            {
                // Jezeli czas szczytu poprzedniego Swingu jest mniejszy (wczesniejszy) niz czas low to znaczy ze to ruch spadkowy
                FormedSwing = new MySwing(SwingType.SWING_LONG,
                          previousLowPrice,
                          previousHighPrice,
                          tickSize, previousLowTime, previousHighTime);

                //Print("Utworzono swing wzrostowy");
            }
            else
            {
                // W odwrotnym przypadku jest to ruch wzrostowy
                //PreviousSwing = new MySwing(SwingType.SWING_LONG, previousLowPrice, previousHighPrice, tickSize);

                FormedSwing = new MySwing(SwingType.SWING_SHORT,
                          previousLowPrice,
                          previousHighPrice,
                          tickSize, previousLowTime, previousHighTime);
                //Print("Utworzono swing spadkowy");

            }

            var lastSwing = Swings.Tail;
            if (lastSwing != null && lastSwing.Equals(FormedSwing))
            {
                    // Oznacza, że parametry (SwingType, LowPrice, HighPrice) są takie same,
                    // więc pomijamy dodanie do listy
                //Print("Znaleziono duplikat swingu, pomijamy...");
            }
            else
            {
                try
                {
                    Swings.Append(FormedSwing);
                }
                catch (ArgumentException e)
                {
                    //Print(e.Message);
                }
                catch (InvalidOperationException e)
                {
                    //Print(e.Message);
                
                }

                if (Swings.Count == 0)
                {
                    //Print("Lista jest pusta");
                }
            }
            
        }

        
    }
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private BetterSwing[] cacheBetterSwing;
		public BetterSwing BetterSwing(bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			return BetterSwing(Input, enableSwingPowerDrawing, zigZagPercentage, enableZigZag);
		}

		public BetterSwing BetterSwing(ISeries<double> input, bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			if (cacheBetterSwing != null)
				for (int idx = 0; idx < cacheBetterSwing.Length; idx++)
					if (cacheBetterSwing[idx] != null && cacheBetterSwing[idx].EnableSwingPowerDrawing == enableSwingPowerDrawing && cacheBetterSwing[idx].ZigZagPercentage == zigZagPercentage && cacheBetterSwing[idx].EnableZigZag == enableZigZag && cacheBetterSwing[idx].EqualsInput(input))
						return cacheBetterSwing[idx];
			return CacheIndicator<BetterSwing>(new BetterSwing(){ EnableSwingPowerDrawing = enableSwingPowerDrawing, ZigZagPercentage = zigZagPercentage, EnableZigZag = enableZigZag }, input, ref cacheBetterSwing);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.BetterSwing BetterSwing(bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			return indicator.BetterSwing(Input, enableSwingPowerDrawing, zigZagPercentage, enableZigZag);
		}

		public Indicators.BetterSwing BetterSwing(ISeries<double> input , bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			return indicator.BetterSwing(input, enableSwingPowerDrawing, zigZagPercentage, enableZigZag);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.BetterSwing BetterSwing(bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			return indicator.BetterSwing(Input, enableSwingPowerDrawing, zigZagPercentage, enableZigZag);
		}

		public Indicators.BetterSwing BetterSwing(ISeries<double> input , bool enableSwingPowerDrawing, double zigZagPercentage, bool enableZigZag)
		{
			return indicator.BetterSwing(input, enableSwingPowerDrawing, zigZagPercentage, enableZigZag);
		}
	}
}

#endregion
Editor is loading...
Leave a Comment