Overall Statistics
Total Trades
37
Average Win
9.14%
Average Loss
-0.19%
Compounding Annual Return
24.007%
Drawdown
18.000%
Expectancy
29.520
Net Profit
197.857%
Sharpe Ratio
1.688
Probabilistic Sharpe Ratio
86.366%
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
49.14
Alpha
0.191
Beta
0.322
Annual Standard Deviation
0.151
Annual Variance
0.023
Information Ratio
0.299
Tracking Error
0.188
Treynor Ratio
0.793
Total Fees
$1111.45
Estimated Strategy Capacity
$120000000.00
Lowest Capacity Asset
TLT SGNKIKYGE9NP
import numpy as np
import pandas as pd


class NaturalResources(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2016, 7, 1)
        self.SetCash(1000000)
        self.SetBrokerageModel(BrokerageName.AlphaStreams)
        rebalance_period = timedelta(days=7)

        self.SetPortfolioConstruction(InsightWeightingPortfolioConstructionModel(rebalance_period))
        self.Settings.RebalancePortfolioOnInsightChanges = False

        self.SetExecution(ImmediateExecutionModel())
        res = Resolution.Hour
        self.UniverseSettings.Resolution = res

        market = ['SPY']
        bonds = ['TLT']

        signals = ['IGE']

        self.market = [self.AddEquity(ticker, res).Symbol for ticker in market]
        self.bonds = [self.AddEquity(ticker, res).Symbol for ticker in bonds]
        self.signals = [self.AddEquity(ticker, res).Symbol for ticker in signals]

        self.AddAlpha(NaturalResourcesEvent(self.market,
                                            self.bonds,
                                            self.signals,
                                            ))


class NaturalResourcesEvent(AlphaModel):
    """

    """

    def __init__(self,
                 market,
                 bonds,
                 signals):

        self.Name = 'Natural Resources Event'
        self.market = market
        self.bonds = bonds
        self.signals = signals

        # Rolling mean of signal:
        self.rolling_mean = 55
        self.past_days = 365

        # Out of market waiting days:
        self.wait_days = 15

        # Percentiles of extreme:
        self.extreme_p = 5

        self.be_in = True
        self.hold_days = timedelta(days=1)
        self.gain = 0.02
        self.dcount = False

    def Update(self, algorithm, data):

        if algorithm.Time.hour != 10 or algorithm.Time.minute != 0:
            return []

        insights = []

        # Sample to detect extreme observations:
        hist = algorithm.History(self.signals, self.past_days,
                                 Resolution.Daily)['close']
        hist = hist.unstack(level=0).dropna()

        # Rolling mean smoothing out of values:
        hist_shift = hist.rolling(window=self.rolling_mean).mean().dropna()
        # As returns:
        returns_sample = (hist / hist_shift - True)

        # Extreme observations at given percentile
        pctl_b = np.nanpercentile(returns_sample,
                                  self.extreme_p,
                                  axis=int(False))
        extreme_b = returns_sample.iloc[-True] < pctl_b
        alarms_on = extreme_b[self.signals].any()

        # Determine whether 'in' or 'out' of the market
        if alarms_on:
            self.be_in = False
        if self.dcount >= self.wait_days:
            self.be_in = True

        if self.be_in:
            for symbol in self.market:
                weight = int(True)
                insights.append(Insight(symbol, self.hold_days,
                                        InsightType.Price,
                                        InsightDirection.Up,
                                        self.gain, int(True),
                                        self.Name, weight))

            self.dcount = False
            return insights

        else:
            algorithm.Log('Market Event incoming. Entering safe positions.')
            for symbol in self.bonds:
                insights.append(Insight(symbol, self.hold_days,
                                        InsightType.Price,
                                        InsightDirection.Up,
                                        self.gain, int(True),
                                        self.Name, int(True)))
            self.dcount += True
            return insights