Main entry point
Every indicator in Indie has a Main entry point. It could be written in one of two forms: as a Main function
definition which is a form of syntactic sugar or as a Main class definition (which is not).
Main function definition
Here is an example of minimal indicator written in a form of a Main as a function definition:
Main decorated with @indicator it transforms it into a Main class definition (i.e. to the second form) according to a few simple rules:
- function definition is replaced with a
Mainclass definition which is inherited fromindie.MainContextbase class; - the body of
Mainfunction becomesMain.calc()method; Mainclass may optionally have__init__constructor (only if it is needed).
Main entry point is considered to
be a syntactic sugar. In some cases when indicator needs greater control over what is happening in the code the second form of writing the indicator’s Main entry point with classes syntax can be used explicitly.
Main written as a class definition
Indicator could be written using the second form of a Main entry point which uses class definition syntax:
__init__ constructor method and place some one-time initialization code there. Therefore for
indicators with complex logic you probably would like to use the second form. Constructor __init__ is optional though.
Functions decorated with @algorithm
Functions decorated with @algorithm are automatically transformed by Indie compiler into classes inherited from
indie.Algorithm. For example:
Main entry point,
the decorator-based form is preferred in most cases. Class form should be used only in more complex cases, where a
more fine-grained control is needed.
Decorator @algorithm is also a syntactic sugar in Indie language. Here are the simple rules that Indie compiler uses
to process it:
- function definition decorated with
@algorithmis replaced with a class with the same name inherited fromindie.Algorithmbase class; - original function becomes a
calcmethod of this class, function body does not change; __init__constructor is added to the generated class which acceptsctx: indie.Contextparameter and forwards it to the parent constructor;
self.ctx property of
indie.Context type. For example to get access to close prices use self.ctx.close expression.
The DoubleEma algorithm from the example above could be created and used in two ways:
- Using
DoubleEma.new()static method (newis a method of parent classindie.Algorithm, well… not exactly but kind of). This is a syntactic sugar that allows to use any algorithm right in the place where it is needed in the indicator (e.g. indef Mainfunction, or in the function body of some other algorithm). - Using
DoubleEma()constructor in__init__method of a correspondingMainContext,SecContextor otherAlgorithm. Then, in thecalcmethod of a correspondingMainContext,SecContextor otherAlgorithmyou should explicitly call theDoubleEma’scalc()method.
Algorithm.new() method
Method Algorithm.new() is used to create and use algorithm objects in Indie. It looks like a static method, but
it behaves very differently because it is also a syntactic sugar of the language. When Indie compiler sees a call, e.g.
res = Ema.new(src, length), it uses the following rules to transform such a piece of code:
- a class field is created in corresponding
__init__constructor of the enclosingMainContext,SecContextor otherAlgorithm. In our example it would be:self._ema1 = Ema(ctx)(in caseselfis aContext) orself._ema1 = Ema(self.ctx)(in caseselfis anAlgorithm). - the call of
Ema.newis replaced withself._ema1.calccall. In our example it would beres = self._ema1.calc(src, length).
Algorithm.new() works only inside methods of descendants of MainContext, SecContext
or other Algorithm classes (except the __init__ constructors). You cannot use this functionality in plain
Python-like functions.
Syntactic sugar of Algorithm.new() method can be used with any Indie algorithm regardless of the form in which the
algorithm was written (the decorator-based form or the class-based form).
MutSeries[T].new() method
Method MutSeries[T].new() (or MutSeriesF.new() where MutSeriesF is an alias for MutSeries[float]) is used to create and use mutable series objects in Indie. It looks like a static method, but
it behaves very differently because it is also a syntactic sugar of the language. When Indie compiler sees a call, e.g.
s = MutSeriesF.new((a + b) / 2), it uses the following rules to transform such a piece of code:
- a class field is created in corresponding
__init__constructor of the enclosingMainContext,SecContextorAlgorithm. In our example it would be:self._ms1 = self.new_mut_series_f()(in caseselfis aContext) orself.ctx.new_mut_series_f()(in caseselfis anAlgorithm). - the call of
MutSeriesF.newis replaced withself._ms1.calccall. In our example it would bes = self._ms1.calc((a + b) / 2). MethodMutSeriesF.calcwrites given expression(a + b) / 2into the last element of the mutable series object and returns a reference to the mutable series itself.
@algorithm and MutSeriesF.new() call.
Functions decorated with @sec_context
Functions decorated with @sec_context are used to create secondary entry-points (besides Main) which are needed when
indicator requests data of additional instruments. And decorator @sec_context is syntactic sugar, because every time
Indie compiler sees a function definition decorated with @sec_context it transforms it into a class, inherited from
indie.SecContext according to a few simple rules:
- function definition decorated with
@sec_contextis replaced with a class with the same name inherited fromindie.SecContextbase class; - the body of the function becomes a
calc()method of the generated class; - generated class may optionally have
__init__constructor (only if it is needed).
SecHighLow looks like this:
Context.calc_on only from __init__ constructors.