Page 1 of 2

NeoTicker Indicator JukeBox

Posted: Wed Jun 27, 2007 5:17 pm
by michal.kreslik
NeoTicker Indicator JukeBox


Hello, friends,

many of you have been asking me about what's the easiest way to write an IDL indicator in NeoTicker that retains its values - and generally its entire environment - between bars/ticks and individual instances of the same indicator.

The above task is fairly easy if you're not planning to have multiple instances of the same indicator applied to the same or different hosts (time charts, quote windows etc.). You simply use a class-level object.

The snag in the class-level objects, though, is that a NeoTicker IDL indicator is a class that is instantiated only once regardless of the actual number of individual instances of the same indicator applied to your function windows. All indicator instances then share the same class-level environment.

An obvious solution to this trouble is to set up a unique environment for every individual indicator instance. Every individual indicator instance is then granted access only to its own environment: its individual set of class-level objects.

I have been using this approach all the time, but I have never published it. Now I've written a standardized object for dealing with this issue and everyone can easily deploy this solution into his or her own IDL indicator code with minimal effort.

I'm calling this solution a NeoTicker Indicator JukeBox. When the indicator is first called, its class is being instantiated with an empty jukebox of songs (class level objects specific to an individual indicator instance). Then a new song is added to the jukebox for every individual indicator instance. Every song has got its unique ID, so the individual indicators know which song is theirs in the jukebox :) A song holds a unique environment for every unique instance of the same indicator.

How to add JukeBox to your project:

  1. you add a reference to the KreslikLib.Neo.JukeBox.DLL to your IDL project and then modify the code of your IDL indicator entry point class as follows:

    Code: Select all

        public class EntryPointClass : IDLIndicator
        {
            NTIndicatorJukeBox<MyIndicatorClass> JukeBox;

            public EntryPointClass()
            {
                JukeBox = new NTIndicatorJukeBox<MyIndicatorClass>();
            }

            public double IDLCallEx(NTIndicatorObjects N)
            {
                return JukeBox.IDLCallSong(N);
            }
        }

    MyIndicatorClass in the above code is your original IDL indicator class that must inherit a Song class, as it's the song that your individual indicator instance will be accessing in the jukebox :). Also, your original IDL indicator class no longer inherits the IIDLindicator interface.
  2. otherwise, your original indicator's code stays the same, except:
    • instead of having IDLCallEx() method as an entry point to your indicator, there's an IDLCall() method. I renamed that in order to prevent confusion as this IDLCall() method in your indicator is no longer called by NeoTicker directly
    • instead of having accessor public for the IDLCall() method, there's an additional override accessor. I wanted to make sure this method is overriden by the programmer, so I used an abstract member in a parent class
    • an example of an implementation in a simple IDL indicator:

      Code: Select all

          public class MyIndicatorClass : Song
          {
              double HighestHigh = 0;
              double CurrentHigh;

              public override double IDLCall(NTIndicatorObjects N)
              {
                  CurrentHigh = N.LinkSeries().get_Items(1).get_High(0);

                  HighestHigh = CurrentHigh > HighestHigh ? CurrentHigh : HighestHigh;

                  return HighestHigh;
              }
          }
The code for the JukeBox itself:

Code: Select all

using Interop.NeoTicker;
using System.Collections;
using System.Reflection;
using System;

namespace KreslikLib.Neo.JukeBox
{
    public class NTIndicatorJukeBox<T> where T : Song
    {
        private ArrayList Songs;
        private int SongIndex;
        private int UniqueID;

        public NTIndicatorJukeBox()
        {
            Songs = new ArrayList();
        }

        public double IDLCallSong(NTIndicatorObjects N)
        {
            UniqueID = N.ItSelf().UniqueId;
            SongIndex = GetSongIndexByID(UniqueID);

            if (SongIndex < 0)
            {
                Type songType = typeof(T);

                T newSong = (T)songType.InvokeMember(songType.Name,
                    BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
                    null, null, null);

                SongIndex = Songs.Add(newSong);

                ((Song)Songs[SongIndex]).UniqueID = UniqueID;
            }

            return ((Song)Songs[SongIndex]).IDLCall(N);
        }

        private int GetSongIndexByID(int ID)
        {
            for (int i = 0; i < Songs.Count; i++)
            {
                if (Songs[i] != null && ((Song)Songs[i]).UniqueID == ID)
                {
                    return i;
                }
            }

            return -1;
        }
    }

    public abstract class Song
    {
        private int uniqueID;

        public int UniqueID
        {
            get { return uniqueID; }
            set { uniqueID = value; }
        }

        public abstract double IDLCall(NTIndicatorObjects N);
    }
}



Simple and elegant isn't it? Way more versatile then NeoTicker's builtin heaps.

Attached you will find the KreslikLib.Neo.JukeBox.DLL, example and the source code.

Michal

Posted: Wed Jun 27, 2007 5:50 pm
by TheRumpledOne
You call that simple?

LOL!

Posted: Wed Jun 27, 2007 6:08 pm
by michal.kreslik
TheRumpledOne wrote:You call that simple?

LOL!


Simple and elegant, as I said :D

Posted: Thu Jun 28, 2007 9:39 pm
by Luke
Thanks for sharing Michal.

Posted: Thu Jun 28, 2007 10:57 pm
by mabon
Hi Michal,

I'm still trying to figure out why the use of the JukeBox makes this data persist. I'm in the process of building param collections and collections of data that I need to reference for an indicator and my garbage collector keeps throwing out my collections. I've even tried static collections and they also get dumped. What is is about your JukeBox that allows the data to persist?

Thanks,

- Richard

Posted: Fri Jun 29, 2007 12:05 am
by michal.kreslik
mabon wrote:Hi Michal,

I'm still trying to figure out why the use of the JukeBox makes this data persist. I'm in the process of building param collections and collections of data that I need to reference for an indicator and my garbage collector keeps throwing out my collections. I've even tried static collections and they also get dumped. What is is about your JukeBox that allows the data to persist?

Thanks,

- Richard


Richard,

the JukeBox object gets instantiated at the time when your IDL indicator class gets installed. This installation occurs either manually (via ScriptEditor > Indicator > Install) or automatically when your IDL indicator is first called on any of your function windows.

The JukeBox object then exists in memory until it gets disposed of, which happens either by manually uninstalling your IDL indicator in ScriptEditor or by exiting NeoTicker. An interesting twist to this, however, is that the IDL indicator object itself (its class) exists in memory even after all individual indicator instances have been removed from all function windows - NeoTicker does not dispose of this object. It creates the IDL indicator class automatically, but it does not dispose of it automatically.

The JukeBox object (third consecutive paragraph with the same beginning. Does it bring luck to the article or something?) thus exists independently of the individual indicator instances. In other words, the JukeBox exists even if your indicator is never called again and even after the individual instance of the indicator is removed from the chart. JukeBox will hold the environments of all individual indicator instances indefinitely until you either uninstall the IDL indicator itself or exit NeoTicker. (Or until Windows crashes.)

Michal

Posted: Fri Jun 29, 2007 12:02 pm
by mabon
Michal,

It appears that something unusual is going on in the namespace hosting the IDL class. If I add a static class to this namespace, NT *crashes* every time. My hope was to use a static class with static variables as a datastore. Why would this occur? Why should the .NET IDL interface do anything to interfere with a static class inside that namespace. I wonder if the reason you are able to make this data persist is that your class is in a *different* namespace? I'm still experimenting with this but my IDL object does not act like a COM object at all. The object that implements a COM interface acts like any other object. It can allocate and de-allocate memory and it stays alive until the last of it's users departs. This .NET IDL interface is something different and I need to know more about it's limitations.

- Richard

Posted: Mon Jul 02, 2007 10:37 pm
by michal.kreslik
mabon wrote:Michal,

It appears that something unusual is going on in the namespace hosting the IDL class. If I add a static class to this namespace, NT *crashes* every time. My hope was to use a static class with static variables as a datastore. Why would this occur? Why should the .NET IDL interface do anything to interfere with a static class inside that namespace. I wonder if the reason you are able to make this data persist is that your class is in a *different* namespace? I'm still experimenting with this but my IDL object does not act like a COM object at all. The object that implements a COM interface acts like any other object. It can allocate and de-allocate memory and it stays alive until the last of it's users departs. This .NET IDL interface is something different and I need to know more about it's limitations.

- Richard


Hi, Richard,

I have no problems with static classes declared in the same namespace as the entry point to the IDL indicator is at all.

Check whether you've got the latest NeoTicker and the latest .NET version/patches.

Clearly, Something is rotten in the state of Denmark. (your PC) :)

Michal

Posted: Mon Jul 02, 2007 10:48 pm
by michal.kreslik
Here is an updated version of JukeBox that makes it easy to not only refer to the unique environment for your IDL indicator instance, but also to create an object that holds information which is shared among these unique instances.

The unique indicator instances might then share information or even communicate with each other.

The shared object has to be of type class, so you can do pretty much anything with it.

Code:

Code: Select all

using Interop.NeoTicker;
using System.Collections;
using System.Reflection;
using System;

namespace KreslikLib.Neo.JukeBox
{
    // usage example:

    //public class EntryPointClass : IDLIndicator
    //{
    //    NTIndicatorJukeBox<MyIDLIndicator, MyCommonObject> JukeBox;

    //    public EntryPointClass()
    //    {
    //        JukeBox = new NTIndicatorJukeBox<MyIDLIndicator, MyCommonObject>();
    //    }

    //    public double IDLCallEx(NTIndicatorObjects N)
    //    {
    //        return JukeBox.IDLCallSong(N);
    //    }
    //}

    public class NTIndicatorJukeBox<T, U>
        where T : Song<U>
        where U : class
    {
        private ArrayList Songs;
        private U CommonObj;

        private int UniqueID;
        private int SongIndex;

        public NTIndicatorJukeBox()
        {
            Songs = new ArrayList();

            Type commonObjectType = typeof(U);
            CommonObj = (U)commonObjectType.InvokeMember(commonObjectType.Name,
                    BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
                    null, null, null);
        }

        public double IDLCallSong(NTIndicatorObjects N)
        {
            UniqueID = N.ItSelf().UniqueId;
            SongIndex = GetSongIndexByID(UniqueID);

            if (SongIndex < 0)
            {
                Type songType = typeof(T);
                T newSong = (T)songType.InvokeMember(songType.Name,
                    BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public,
                    null, null, null);

                SongIndex = Songs.Add(newSong);

                ((Song<U>)Songs[SongIndex]).SetupSong(UniqueID, CommonObj, Songs);
            }

            return ((Song<U>)Songs[SongIndex]).IDLCall(N);
        }

        private int GetSongIndexByID(int ID)
        {
            for (int i = 0; i < Songs.Count; i++)
            {
                if (Songs[i] != null && ((Song<U>)Songs[i]).UniqueID == ID)
                {
                    return i;
                }
            }

            return -1;
        }
    }

    /// <summary>
    /// Song is a class that an IDL indicator must inherit. It represents a unique instance of the IDL indicator.
    /// </summary>
    /// <typeparam name="V">V is a type of class that is shared among all unique indicator instances.</typeparam>
    public abstract class Song<V>
        where V : class
    {
        private int uniqueID;
        private V commonObj;
        private ArrayList songs;
        private bool locked;

        public Song()
        {
            // unlock the class so that we can set it up:
            locked = false;
        }

        public void SetupSong(int UniqueID, V CommonObj, ArrayList Songs)
        {
            // make sure that we don't accidentally assign a value or reference
            // to these vital objects later:
            if (!locked)
            {
                uniqueID = UniqueID;
                commonObj = CommonObj;
                songs = Songs;
                locked = true;
            }
            else
            {
                throw new Exception("The " + this.GetType().Name + " object is locked.");
            }
        }

        /// <summary>
        /// Read only property UniqueID is set upon the Song's instantiation.
        /// </summary>
        public int UniqueID
        {
            get { return uniqueID; }
        }

        /// <summary>
        /// Read only property CommonObj is set upon the Song's instantiation.
        /// </summary>
        protected V CommonObj
        {
            get { return commonObj; }
        }

        /// <summary>
        /// Read only property Songs is set upon the Song's instantiation.
        /// </summary>
        protected ArrayList Songs
        {
            get { return songs; }
        }

        public abstract double IDLCall(NTIndicatorObjects N);
    }
}


DLL & C# source attached.

Michal

Posted: Tue Jul 03, 2007 11:56 am
by mabon
michal.kreslik wrote:Michal,

Hi, Richard,

I have no problems with static classes declared in the same namespace as the entry point to the IDL indicator is at all.

Check whether you've got the latest NeoTicker and the latest .NET version/patches.

Clearly, Something is rotten in the state of Denmark. (your PC) :)

Michal


Michal,

The problem turned out to be with the COM reference. Apparently the recommendation is now to reference Interop.NeoTicker.dll in the indicators directory not the COM reference to the NeoTicker library. Once I started referencing Interop.NeoTicker.dll everything was fine.

- Richard