Overall Statistics
Total Orders
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Start Equity
100000
End Equity
100000
Net Profit
0%
Sharpe Ratio
0
Sortino Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-0.73
Tracking Error
0.142
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
# region imports
from AlgorithmImports import *

from sklearn.linear_model import LinearRegression
# endregion

class FuturesIntradayTrendFollowingWithRealizedGammaAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2011, 1, 1)
        self.set_end_date(2024, 10, 1)
        self._future = self.add_future(
            Futures.Indices.SP_500_E_MINI,
            data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO,
            data_mapping_mode=DataMappingMode.OPEN_INTEREST,
            contract_depth_offset=0
        )
        self._future.set_filter(lambda universe: universe.front_month())
        self._future.realized_gamma_by_time = {}
        self._future.yesterdays_close = None
        self._future.previous_interval_close = None
        self._trading_interval = timedelta(minutes=60)
        self._lookback = 120 # trading days
        date_rule = self.date_rules.every_day(self._future.symbol)
        self.schedule.on(date_rule, self.time_rules.midnight, self._record_close_price)
        self.schedule.on(date_rule, self.time_rules.every(self._trading_interval), self._rebalance)
        self.set_warm_up(timedelta(int(1.5*self._lookback)))

    def _record_close_price(self):
        self._future.yesterdays_close = self._future.price

    def _rebalance(self):
        # Wait until the market is open.
        t = self.time
        if (not self._future.yesterdays_close or
            not self._future.exchange.hours.is_open(t - self._trading_interval, False)):
            return
        # Create a realized Gamma indicator for this time interval if it doesn't already exist.
        time_period = (t.hour, t.minute)
        if time_period not in self._future.realized_gamma_by_time:
            self._future.realized_gamma_by_time[time_period] = RealizedGamma(time_period, self._lookback)
        # Get the indicator value for this time interval.
        realized_gamma = self._future.realized_gamma_by_time[time_period]
        return_since_last_close = self._future.price / self._future.yesterdays_close - 1
        if realized_gamma.update(IndicatorDataPoint(t, return_since_last_close)):
            self.plot('Realized Gamma', str(time_period), realized_gamma.value)
        # Update the training data of the previous interval's indicator.
        if self._future.previous_interval_close:
            previous_t = t - self._trading_interval
            previous_time_period = (previous_t.hour, previous_t.minute)
            if previous_time_period in self._future.realized_gamma_by_time:
                self._future.realized_gamma_by_time[previous_time_period].add_label(
                    self._future.price / self._future.previous_interval_close - 1
                )
        # Record the interval close price.
        self._future.previous_interval_close = self._future.price
        # Check if we can rebalance.
        if not self._future.exchange.hours.is_open(t + self._trading_interval - timedelta(seconds=1), False):
            return
        # Rebalance


class RealizedGamma(PythonIndicator):

    def __init__(self, time_period, lookback):
        self.name = f'RealizedGamma{time_period}'
        self.time = datetime.min
        self.value = 0
        self._X = np.array([]) # Return from previous close to t.
        self._y = np.array([]) # Return from t to t+trading_interval.
        self._lookback = lookback
        self._model = LinearRegression(fit_intercept=True)
    
    def update(self, input):
        # Check if there is sufficient training data.
        is_ready = len(self._y) == self._lookback
        if is_ready:
            # Fit model.
            self._model.fit(self._X.reshape(-1, 1), self._y.reshape(-1, 1))
            # Set the value to the predicted the return from t to t+trading_interval.
            # `input.value` is the return from previous close to t.
            self.value = self._model.predict([[input.value]])[0][0]
        # Add the sample of the independent variable to the training data. 
        self._X = np.append(self._X, input.value)[-self._lookback:] 
        self.time = input.time
        return is_ready
    
    def add_label(self, label):
        self._y = np.append(self._y, label)[-self._lookback:]