Announcement

Collapse
No announcement yet.

Indicators and classes

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

  • Indicators and classes

    Hi

    I've just started learning EFS (though I'm an experienced developer) and I have some questions about developing indicators.

    1. In the Knowledge Base article "Guide to Developing EFS Indicators", the examples use a class called MAStudy to create the moving averages, but this doesn't seem to be documented anywhere and appears to have been superseded by the sma(), ema(), wma() etc functions. Are object-oriented versions of other studies also available, and are they still supported?

    2. Although it seems clear that the main() function is called for every price bar, the MAStudy class is being instantiated every time. Is this a mistake? Would it even work as expected?

    3. The examples in this article each return either a single or an array of numeric values, but I've seen other examples that return either a single or an array of Series objects. Please can you explain when it is necessary to use Series objects and when it is acceptable to just return numeric values?

    4. I have a large library of Javascript classes that were originally written for Sharescope, but which I'd like to use in eSignal. My plan is to leave the library classes in their existing, generic format, and create a series of procedural wrapper functions to provide an eSignal-style interface. Do you have any examples of how best to do this?

    Thanks

    Marcus

  • #2
    Marcus
    1. MAStudy() as well as any other function of that type (commonly referred to as EFS1 or legacy functions) are still available (see for example this article in the EFS KnowledgeBase).
    You can find them listed in the Glossary of the EFS KnowledgeBase (they generally include Study in their name eg MAStudy(), MACDStudy(), StochStudy(), etc) or by just searching for a generic study name eg Average where the search will return articles for both the EFS2 version ie sma() and the EFS1/legacy version ie MAStudy(). FWIW article numbers that relate to the EFS1/legacy functions usually begin with 1 whereas those that relate to EFS2 functions begin with 2 or higher
    2. The main() function is called once on every historical bar and on every tick on the current/real time bar (unless you specifically add a statement in preMain() to compute on close only in which case it executes once at every new instance of a real time bar). Not quite sure what you are referring to with “… the MAStudy class is being instantiated every time. Is this a mistake?”
    3. In general the EFS1/legacy functions can return either a single value or an array of values but not a Series Object. The latter is returned only by the EFS2 functions ie sma(), macd() etc. There are some exceptions such as high(), low(), close() etc which can return either depending on the syntax being used.
    As to your question I am assuming you are referring to the EFS2 functions. In that case the only time I can think of when you would want to return a series rather than a value is if you are plotting a study based on an external symbol and/or interval and want to take advantage of the synchronization functionality that the series provides. In virtually any other instance you can just return the value. Anyhow, search the forums and you will find numerous threads on this topic as it has been covered at length before (I would suggest you use Google for your searches as the vBulletin search engine is close to useless)
    4. I do not recall seeing any examples but you could provide a specific example of what you are trying to accomplish and someone may be able to offer some pointers
    Alex

    Originally posted by marcusjhdon View Post
    Hi
    I've just started learning EFS (though I'm an experienced developer) and I have some questions about developing indicators.
    1. In the Knowledge Base article "Guide to Developing EFS Indicators", the examples use a class called MAStudy to create the moving averages, but this doesn't seem to be documented anywhere and appears to have been superseded by the sma(), ema(), wma() etc functions. Are object-oriented versions of other studies also available, and are they still supported?
    2. Although it seems clear that the main() function is called for every price bar, the MAStudy class is being instantiated every time. Is this a mistake? Would it even work as expected?
    3. The examples in this article each return either a single or an array of numeric values, but I've seen other examples that return either a single or an array of Series objects. Please can you explain when it is necessary to use Series objects and when it is acceptable to just return numeric values?
    4. I have a large library of Javascript classes that were originally written for Sharescope, but which I'd like to use in eSignal. My plan is to leave the library classes in their existing, generic format, and create a series of procedural wrapper functions to provide an eSignal-style interface. Do you have any examples of how best to do this?
    Thanks
    Marcus

    Comment


    • #3
      Thanks for the detailed reply Alex.

      I'll do some more testing of my own and come back with code samples if/when I get stuck!

      Regards

      Marcus

      Comment


      • #4
        Marcus
        You are welcome
        Alex


        Originally posted by marcusjhdon View Post
        Thanks for the detailed reply Alex.

        I'll do some more testing of my own and come back with code samples if/when I get stuck!

        Regards

        Marcus

        Comment


        • #5
          Despite numerous searches of this forum, I'm still struggling to find a complete explanation of how eSignal handles Series objects, and how I can replicate this behaviour in my own libraries. I'll try to explain what I mean in the following examples.

          Here is a bare-bones Hull moving average with a fixed length of 100 bars:

          Code:
          function preMain() {
              setPriceStudy(true);
          }
          
          function main() {
          	return wma(10, efsInternal("hullMa"));
          }
          
          function hullMa() {
              var ma1 = wma(50);
              var ma2 = wma(100);
              return 2 * ma1 - ma2;
          }
          In the example above, my understand so far is that each call to wma() within hullMa() will return a Series object, but I am still able to perform some arithmetic on the result as if they were returning numeric values - so presumably eSignal casts the result to a number in this context. Then, because I'm calling hullMa() using efsInternal(), the result is returned as a new Series object, which allows me to pass it to another instance of wma().

          What I've been unable to find in the forum is an example of how I could replicate this built-in behaviour in order to turn this into a completely self-contained function. Here is example to illustrate what I mean, though I know it's wrong:

          Code:
          function preMain() {
              setPriceStudy(true);
          }
          
          function main() {
              return hullMa();
          }
          
          function hullMa(){
              var ma1 = wma(50);
              var ma2 = wma(100);
              var seriesObj = new Series();
              seriesObj.setValue(2 * ma1 - ma2);
              return wma(10, seriesObj);;
          }
          Passing a numeric value to the 3rd call to wma() doesn't work, since it expects a Series object, but I can't find a function to generate a Series object containing a user-defined value. Perhaps I've just missed the relevant function, or I've misunderstood something?

          Thanks

          Marcus

          Comment


          • #6
            Incidentally, based on the manual this is what I would expect to work, but it doesn't and there's no error message to explain why:

            Code:
            function preMain() {
                setPriceStudy(true);
            }
            
            function main() {
                return hullMa();
            }
            
            function hullMa(){
                var ma1 = wma(50);
                var ma2 = wma(100);
                return wma(10, getSeries(2 * ma1 - ma2));
            }

            Comment


            • #7
              Shortly after my last post, I found this thread, which seems to confirm there is no way to create a custom Series object, as I feared. The implication of this is that it's only possible to make very limited use of the built-in EFS functions inside a custom function or library, which means I'll have to spend time rewriting the ones I need myself. Perhaps eSignal could consider providing this functionality in a future release?

              Fortunately for me, I've realised I'm better off keeping my code non-platform specific anyway, since I also use ShareScope for back-testing, and it's better if I only have one version to maintain. However, I also wanted to be able to provide a familiar eSignal-style procedural interface for anything I share, so I put together the following example that I hope others will find useful:

              Code:
              /**
               * Ema instance used by procedural function.
               * @type {Ema}
               */
              var emaObj;
              
              /**
               * Series instance used by procedural function.
               * @type {Series}
               */
              var seriesObj;
              
              /**
               * Data offset used by procedural function.
               * @type {Number}
               */
              var dataOffset = 0;
              
              /**
               * EMA Procedural Function.
               * 
               * @param {Number} length - The moving average lookback period.
               * @param {Series|Number} [seriesOrOffset] - Optional series object or data offset.
               * @param {Number} [offset] - Optional data offset.
               * @returns {Number}
               */
              function ema(length, seriesOrOffset, offset) {
              
              	// If first bar, initialise variables
              	if (getBarState() === BARSTATE_ALLBARS) {
              
              		// Set offset if provided
              		if (offset !== undefined && isNaN(offset) === false && isFinite(offset)) {
              			dataOffset = offset;
              		}
              	
              		// Set series or offset according to type of second parameter
              		if (seriesOrOffset !== undefined) {
              			if (seriesOrOffset instanceof Series) {
              				seriesObj = seriesOrOffset;
              			} else if (isNaN(seriesOrOffset) === false && isFinite(seriesOrOffset)) {
              				dataOffset = seriesOrOffset;
              			}
              		}
              	
              		// Set default data series
              		if (seriesObj === undefined) {
              			seriesObj = close();
              		}
              	
              		// Instantiate library class
              		emaObj = new Ema(length);
              	}
              	
              	// Calculate EMA
              	return emaObj.getNext(seriesObj.getValue(dataOffset));
              }
              
              /**
               * Exponential Moving Average.
               * 
               * @constructor
               * @param {Number} length - The moving average lookback period.
               * @returns {Ema}
               */
              function Ema(length) {
              	
              	// Check length
              	if (isNaN(length) || !isFinite(length) || !length) {
              		throw('Expected usage: new Ema(length)');
              	}
              	
              	// Initialise properties
              	this.alpha = 2 / (length + 1);
              	this.ema = null;
              }
              
              /**
               * Calculate next EMA value.
               * 
               * @param {Number} price - Next price value.
               * @returns {Number}
               */
              Ema.prototype.getNext = function(price) {
              	
              	// Ignore null price
              	if (price === null) {
              		return null;
              	}
              	
              	// Check price is valid number
              	if (isNaN(price) || !isFinite(price) || !price) {
              		throw('Expected usage: Ema.getNext(price)');
              	}
              	
              	// Use raw price for first value
              	if (this.ema == null) {
              		this.ema = price;
              		
              	// Calculate EMA
              	} else {
              		this.ema += (price - this.ema) * this.alpha;
              	}
              	
              	// Return new value
              	return this.ema;
              }
              As you can see, there is nothing eSignal-specific inside the Ema class that does the actual calculations, and the procedural function provides an interface that is identical to eSignal's built-in ema function.

              The "seriesOrOffset" variable is rather messy, but unfortunately Javascript doesn't support function overloading as such. However, it does provide an "arguments" array to every function, and this would provide a better solution in more complex examples.

              Here are some basic examples of how you could use the procedural function. Obviously, there would be no point in this case, because there's already a built-in ema function, but the same method could be applied to anything.

              Code:
              var emaLib = addLibrary("Ema.efsLib");
              
              function preMain() {
              	setPriceStudy(true);
              }
              
              function main() {
              	//return emaLib.ema(20, close(), 5);
              	//return emaLib.ema(20, close());
              	//return emaLib.ema(20, 5);
              	return emaLib.ema(20);
              }

              Comment

              Working...
              X