Run the aggregating algorithm 
        
        Parameters
        ----------
        h : int
            How many steps to run the aggregating algorithm on 

        recalculate: boolean
            Whether to recalculate the predictions or not

        Returns
        ----------
        - np.ndarray of normalized weights, np.ndarray of losses for each model

def run(self, h, recalculate=False):
        """ Run the aggregating algorithm 
        
        Parameters
        ----------
        h : int
            How many steps to run the aggregating algorithm on 

        recalculate: boolean
            Whether to recalculate the predictions or not

        Returns
        ----------
        - np.ndarray of normalized weights, np.ndarray of losses for each model
        """

        data = self.data[-h:] 
        predictions = self._model_predict_is(h, recalculate=recalculate).values
        weights = np.zeros((h, len(self.model_list)))
        normalized_weights = np.zeros((h, len(self.model_list)))
        ensemble_prediction = np.zeros(h)
        
        for t in range(h):
            if t == 0:
                weights[t,:] = 100000
                ensemble_prediction[t] = np.dot(weights[t,:]/weights[t,:].sum(), predictions[t,:])
                weights[t,:] = weights[t,:]*np.exp(-self.learning_rate*self.loss_type(data[t], predictions[t,:]))
                normalized_weights[t,:] = weights[t,:]/weights[t,:].sum()
            else:
                ensemble_prediction[t] = np.dot(weights[t-1,:]/weights[t-1,:].sum(), predictions[t,:])
                weights[t,:] = weights[t-1,:]*np.exp(-self.learning_rate*self.loss_type(data[t], predictions[t,:]))
                normalized_weights[t,:] = weights[t,:]/weights[t,:].sum()

        return normalized_weights, self._construct_losses(data, predictions, ensemble_prediction), ensemble_prediction