Dear Quants,

In the past few weeks, I've been heavily working in LEAN, locally, browsing the LEAN C# source code. My goal is to build a solid understanding of the inner workings of the engine, so I can understand 1) what it offers and 2) how to properly use it.

My first project has been to work on an indicator implemented in Python (I may release the code later - it's done, I need to backtest it with local data first). While doing this, I've developed what I believe is a complete view of how one can develop indicators for LEAN in Python. I wanted to share that view with you.

So here is a complete (I believe) template to LEAN Python indicators:

# Of course, choose a better name for your indicator class than "YourIndicator".
class YourIndicator(PythonIndicator):

    # You can pass as many parameters to your indicator as needed.

    # Pass a name (type str) and assign it to self.Name if you want your indicator to have a non-default name in LEAN.
    def __init__(self, name, <your parameters>):
    	# Initialize your indicator as needed.
    	# In particular:

        self.Name = name # If you do not set the self.Name attribute, the default will be the name of the class.
        self.Value = None # You need to have a self.Value attribute. It holds the current value of your indicator - I prefer setting it
        				  # first to None instead of 0 as 0 could be interpreted as an indicator value.

        self.WarmUpPeriod = <warmup period> # The self.WarmUpPeriod attribute is optional. If present, it tells LEAN how many bar(s) your

        									# indicator needs before being ready. This is needed when using some WarmUpIndicator() calls in the

        									# QCAlgorithm. If you don't add it, the value will be 0.


	# IsReady is a read-only property used by LEAN. If you do not define it, your Update method (below) MUST return a bool stating if your

	# indicator is ready or not.
    @property
    def IsReady(self):
        return <a boolean result from computations to determine if your indicator is ready or not>


	# The Update method is called in the QCAlgorithm to update the indicator with new data.

	# I like to type hint all my Python code so I can check it with mypy but it is also very useful to help your Python IDE provide suggestions.

	# Notice that I have typed hint the input parameter (input: IBaseDataBar) so the IDE (most) can tell you what attributes are in input.

	# This is not mandatory. You can also just use "def Update(self, input):" as a method signature.
    def Update(self, input: IBaseDataBar):
		# In Update, you need to:
		# 1- of course, make necessary computations to update your indicator,
		# 2- update self.Value with the latest value of your indicator, and
		# 3- either:
		#	3.1- make sure that the computations in IsReady (if you defined it) will return the current state of readiness of your indicator, or
		#	3.2- return a bool to state if your indicator is ready or not.

That's it. The bulk of that information was already available in other posts but the IsReady property was not properly documented (at least I did not find that information). The necessary and optional attributes were also not very clearly documented, in my opinion.

In my current indicators, I have a self.Value attribute but I do not use it. I leave it to None. My current indicators rather use events (callbacks) to signal new values to the QCAlgorithm. It is a different model. And also a different subject. 😊

Fred

Author