Hi all, 

Is there a way to return a dataframe from a custom indictor?

For context I have checked the code, all is working, I can print the DF within the custom indicator and the values are populating the dataframe, but I'm having trouble returning it. I need to be able to return it so I can see the visual output without it printing for each tradebar… 

I've been using “custom = customindicator()” and nothing prints. 

I do not know how to use a rollingwindow to capture dataframe values, if that's how this needs to be done. 

I'm using local platform (quantbook). 

 

# QuantBook Analysis Tool
# For more information see https://www.quantconnect.com/docs/research/overview

#Required QuantConnect Imports 
from AlgorithmImports import *
from collections import deque

#Supplementary Imports
import plotly.graph_objects as go
import pandas as pd
import numpy as np
import statistics as st
import math

#Quantbook data pulling 
qb = QuantBook()
symbol = qb.add_equity('TSLA').symbol
history = qb.history[TradeBar](symbol, 500, Resolution.DAILY)
data_df = qb.PandasConverter.GetDataFrame[TradeBar](history)
print(history)
print(data_df)

## START SUPPLEMENTARY FUNCTIONS --------------------------------------------------------------------------------------------------
def nz(x, y=None):
    '''
    RETURNS
    Two args version: returns x if it's a valid (not NaN) number, otherwise y
    One arg version: returns x if it's a valid (not NaN) number, otherwise 0
    ARGUMENTS
    x (val) Series of values to process.
    y (float) Value that will be inserted instead of all NaN values in x series.
    '''
    if isinstance(x, np.generic):
        return x.fillna(y or 0)
    if x != x:
        if y is not None:
            return y
        return 0
    return x

def wwma(values, n):
    """
    J. Welles Wilder's EMA 
    """
    return values.ewm(alpha=1/n, adjust=False).mean()

def rma(s: pd.Series, period: int) -> pd.Series:
    return s.ewm(alpha=1 / period).mean()

def crossover_series(x: pd.Series, y: pd.Series, cross_distance: int = None) -> pd.Series:
    shift_value = 1 if not cross_distance else cross_distance
    return (x > y) & (x.shift(shift_value) < y.shift(shift_value))

def crossunder_series(x: pd.Series, y: pd.Series, cross_distance: int = None) -> pd.Series:
    shift_value = 1 if not cross_distance else cross_distance
    return (x < y) & (x.shift(shift_value) > y.shift(shift_value))
## END SUPPLEMENTARY FUNCTIONS --------------------------------------------------------------------------------------------------

## START ATR FUNCTIONS --------------------------------------------------------------------------------------------------

def ATR_8(data: pd.DataFrame, window=14, use_nan=True) -> pd.Series:
    df_ = data.copy(deep=True)
    window_1 = 50
    window_2 = 10

    df_.loc[:, 'H_L'] = df_['high'] - df_['low']
    df_.loc[:, 'H_Cp'] = abs(df_['high'] - df_['close'].shift(1))
    df_.loc[:, 'L_Cp'] = abs(df_['low'] - df_['close'].shift(1))
    df_.loc[:, 'TR'] = df_[["H_L", "H_Cp", "L_Cp"]].max(axis=1)
    df_.loc[:, 'ATR_1'] = df_['TR'].rolling(window_1).mean()*3.5
    df_.loc[:, 'ATR_2'] = df_['TR'].rolling(window_2).mean()*3.5
    df_.loc[:, 'ATR'] = (df_['ATR_1'] + df_['ATR_2']) / 2 
    df_.loc[:, 'up'] = df_['ATR']+((df_['high'] + df_['low'] + df_['close'])/3)
    df_.loc[:, 'dn'] = ((df_['high'] + df_['low'] + df_['close'])/3)-df_['ATR']

    for i in range(window, len(df_)):
        df_.iloc[i, df_.columns.get_loc('ATR')] = (((df_.iloc[i - 1, df_.columns.get_loc('ATR')]) * (window - 1)) + df_.iloc[
            i, df_.columns.get_loc('TR')]) / window

    df_['upper'] = df_['close']
    df_['lower'] = df_['close']

    df_['upper_last'] = 0.0
    df_['lower_last'] = 0.0

    df_['upper_last'] = df_['upper'].shift(-1)
    df_['lower_last'] = df_['lower'].shift(-1)

    df_.loc[(df_['close'].shift(-1) < df_['upper'].shift(-1)), 'upper'] = df_['up']
    df_.loc[(df_['close'].shift(-1) > df_['upper'].shift(-1)), 'upper'] = df_[['up','upper_last']].min(axis=1)

    df_.loc[(df_['close'].shift(-1) < df_['lower'].shift(-1)), 'lower'] = df_['dn']
    df_.loc[(df_['close'].shift(-1) > df_['lower'].shift(-1)), 'lower'] = df_[['dn','lower_last']].max(axis=1)

    df_['os'] = 0.0
    df_.loc[(df_['close'] > df_['upper']), 'os'] = 1.0
    df_.loc[(df_['close'] < df_['lower']), 'os'] = 0.0
    df_.loc[((df_['close'] <= df_['upper']) & (df_['close'] >= df_['lower'])), 'os'] = df_['os'].shift(-1)

    df_['spt'] = 0.0
    df_.loc[(df_['os'] == 1.0), 'spt'] = df_['lower']
    df_.loc[(df_['os'] != 1.0), 'spt'] = df_['upper']

    df_['max'] = 0.0
    df_['min'] = 0.0

    df_['max_last'] = df_['max'].shift(-1)
    df_['min_last'] = df_['min'].shift(-1)

    df_.loc[((df_['os'] == 0.0) | ((df_['spt'].shift(-1) <= df_['close'].shift(-1)) & (df_['spt'] > df_['close']))), 'max'] = df_[['spt','max_last']].min(axis=1)
    df_.loc[((df_['os'] == 1.0) | ((df_['spt'].shift(-1) >= df_['close'].shift(-1)) & (df_['spt'] < df_['close']))), 'min'] = df_[['spt','min_last']].max(axis=1)

    df_.loc[(df_['os'] == 1.0), 'max'] = df_[['close','max_last']].max(axis=1)
    df_.loc[(df_['os'] == 0.0), 'min'] = df_[['close','min_last']].min(axis=1)

    df_.loc[((df_['spt'].shift(-1) >= df_['close'].shift(-1)) & (df_['spt'] < df_['close'])), 'max'] = df_[['close','max_last']].max(axis=1)
    df_.loc[((df_['spt'].shift(-1) <= df_['close'].shift(-1)) & (df_['spt'] > df_['close'])), 'min'] = df_[['close','min_last']].min(axis=1)

    df_['avg'] = df_[['max','min']].min(axis=1)

    #end new addition

    if use_nan:
        df_.iloc[:window, df_.columns.get_loc('ATR')] = np.nan

    return df_#['ATR']
## END ATR FUNCTIONS --------------------------------------------------------------------------------------------------

## Start Indicator Class --------------------------------------------------------------------------------------------------
class MYATR(PythonIndicator):
    def __init__(self, baselength, mult, data_df):
        self.mrma_value = None   # Attribute represents the indicator value
        self.value_at_risk = None
        self.deviation = None
        self.mult = mult
        self.baselength = baselength # renamed from alpha
        self.std = StandardDeviation(baselength)
        self.window_renamed = RollingWindow[float](baselength)
        self.data_df = data_df
        self.max = 0.0 
        self.avg = 0.0
        self.min = 0.0

    # Override the IsReady attribute to flag all attributes values are ready.
    @property
    def is_ready(self) -> bool:
        return self.mult 
    
    # all of the accessing OHLC will go here 
    # Method to update the indicator values. Note that it only receives 1 IBaseData object (Tick, TradeBar, QuoteBar) argument.
    def Update(self, input: BaseData) -> bool:
        count = self.window_renamed.Count
        self.window_renamed.Add(input.Close)
        # Update the Value and other attributes as the indicator current value.
        if count >= 10:
            ## Handle OHLC
            self.open = input.Open
            self.high = input.High
            self.low = input.Low
            self.close = input.Close

            ## Calculate the volatility as a percentage of the price ------------------------------------------------------------------------------
            self.testlist = list(self.window_renamed)[::-1]
            self.data_df = data_df
            self.data_top = self.data_df.head() 

            ##------------------------------------------------------------------------------

            self.hlc3 = (self.high+self.low+self.close)/3

            self.length = self.baselength
            self.mult   = self.mult

            self.upper = 0.0
            self.lower = 0.0
            self.os = 0.0
            self.max = 0.0
            self.min = 0.0

            self.src = self.close

            ##------------------------------------------------------------------------------
            
            self.full_atr = ATR_8(self.data_df, self.length, True)
            print(self.full_atr)

            #Python dequeuing
            self.queue = deque(maxlen=self.baselength)
            print('queue finished')
        # return a boolean to indicate IsReady.
        print('count finished')
        return self.full_atr if count >= 10 else False
## END Indicator Class --------------------------------------------------------------------------------------------------

## START Run Indicator and Instantiate Windows --------------------------------------------------------------------------------------------------
custom = MYATR(50, 3.5, data_df)
print(custom)

#tinkle
window = {}
window['dataframe'] = RollingWindow[pd.DataFrame](92)
## END Run Indicator and Instantiate Windows --------------------------------------------------------------------------------------------------

## START Pull Data and Route Data --------------------------------------------------------------------------------------------------
for bar in history:
    custom.Update(bar)
    print(custom)

    # The Updated event handler is not available for custom indicator in Python, RollingWindows are needed to be updated in here.
    #tinkle
    if custom.is_ready:
        print(custom)
        window['dataframe'].add(custom.full_atr)

custom_dataframe = pd.DataFrame(window).set_index('time')
print(custom_dataframe)
print('end custom dataframe')

 

The code below “# START Run Indicator and Instantiate Windows….” is where I'm having issues. 

 

Warm regards,

Kevin