Overall Statistics
import numpy as np

### <summary>
### Example of a simple class that acts as a manualy updated indicator using pandas to
### calculate the rolling std of percent returns of the close price for each asset.
### </summary>
class BasicOOPAlgorithm(QCAlgorithm):
    '''
      Example of a simple class that acts as a manualy updated indicator using pandas to
      calculate the rolling std of percent returns of the close price for each asset.
    '''

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.SetStartDate(2013,10,1)  #Set Start Date
        self.SetEndDate(2013,10,5)   #Set End Date
        self.SetCash(100000)          #Set Strategy Cash
        
        self.resolution = Resolution.Daily
        
        self.universe = [
            self.AddEquity("SPY", self.resolution).Symbol,
            self.AddEquity("AAPL", self.resolution).Symbol,
            self.AddEquity("C", self.resolution).Symbol,
        ]

        # Store per-asset indicators in a dictionary
        self.std_of_returns = {}
        for symbol in self.universe:
            # initialize the object
            self.std_of_returns[symbol] = StdOfReturns(self, symbol, 10, self.resolution)

    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.

        Arguments:
            data: Slice object keyed by symbol containing the stock data
        '''
        for symbol in self.universe:
            
            # update the rolling metric with new data
            self.std_of_returns[symbol].update(self.Securities[symbol].Price)
            
            self.Log("%s\t:\t%0.3f"%(symbol, self.std_of_returns[symbol].value))

class StdOfReturns():
    
    def __init__(self, algo, symbol, window, resolution):
        # set up params of per-asset rolling metric calculation
        self.symbol = symbol
        self.window = window
        self.resolution = resolution
        
        # download the window. Prob not great to drag algo scope in here. Could get outside and pass in.
        self.history = algo.History([symbol], window, self.resolution).close.values
        
        # calulate the metrics for the current window
        self.compute()

    def update(self, value):
        # update history, retain length
        self.history = np.append(self.history, float(value))[1:]
        
        # calulate the metrics for the current window
        self.compute()
    
    def compute(self):
        # calc percent returns
        r_p = np.diff(self.history)/self.history[:-1]
        
        # calc std of returns for current widow
        std_r = np.std(r_p)
        
        # update value
        self.value = std_r