Overall Statistics
Total Trades
1
Average Win
0%
Average Loss
0%
Compounding Annual Return
6.826%
Drawdown
57.300%
Expectancy
0
Net Profit
218.554%
Sharpe Ratio
0.378
Probabilistic Sharpe Ratio
0.073%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0.064
Beta
0.462
Annual Standard Deviation
0.159
Annual Variance
0.025
Information Ratio
0.429
Tracking Error
0.16
Treynor Ratio
0.13
Total Fees
$0.00
Estimated Strategy Capacity
$12000.00
Lowest Capacity Asset
SPX500USD 8I
from AlgorithmImports import *
#endregion
from datetime import date, timedelta, datetime
import numpy as np
import math
import statistics

class LogicalApricotCobra(QCAlgorithm):

    def Initialize(self):

        # set up
        self.SetStartDate(2005, 1, 1) # SPY cumulative return cca 410% for the period from 2005-01-01 to end
        # self.SetEndDate(2022, 8, 23)
        self.SetCash(1000000)
        self.SetBrokerageModel(BrokerageName.OandaBrokerage, AccountType.Margin)

        # parameters
        self.budget = 50000
        self.thresold = 2.9
        self.sma_width = 8
        self.close_window_length = 7
        self.buy_bond = False

        # add data
        self.spx = self.AddCfd("SPX500USD", Resolution.Hour).Symbol
        # self.spx = self.AddEquity("SPY", Resolution.Hour).Symbol

    def OnData(self, data):
        
        # check if data is available
        # if not data.Quotes.ContainsKey(self.spx):
        #     return

        # extract vars
        c = data[self.spx].Close

        # plots
        self.Plot("Cfd plot", "Cfd Close", c)

        # trading rule
        if not self.Portfolio[self.spx].Invested:
            self.SetHoldings(self.spx, 1)


"""
class LogicalApricotCobra(QCAlgorithm):

    def Initialize(self):
        
        # set up
        self.SetStartDate(2005, 1, 1) # SPY cumulative return cca 410% for the period from 2005-01-01 to end
        # self.SetEndDate(2022, 8, 23)
        self.SetCash(50000)
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        
        # parameters
        self.budget = 50000
        self.thresold = 2.9
        self.sma_width = 8
        self.close_window_length = 7
        # self.size = 0
        self.buy_bond = False
        
        # optimization parameters
        # self.thresold = float(self.GetParameter("radf-threshold"))
        # self.sma_width = int(self.GetParameter("radf-sma-width"))

        if self.LiveMode:
            self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol
            self.symbol = self.AddData(RadfAgg, "EXUBER", Resolution.Minute, TimeZones.Utc).Symbol
        else:
            # SPY data
            equity = self.AddCfd("SPX500USD", Resolution.Hour)
            # equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
            self.spy = equity.Symbol
            # self.SetBenchmark(lambda dt: self.Securities[self.spy].Price)
            # self.SetBenchmark("SPY")
            # exuber data
            self.symbol = self.AddData(RadfAgg, "EXUBER", Resolution.Hour).Symbol
            self.radf_sma = self.SMA("EXUBER", self.sma_width, Resolution.Hour)
        if self.buy_bond:
            bond = self.AddEquity("TLT", Resolution.Hour)
            bond.SetDataNormalizationMode(DataNormalizationMode.Raw)
            self.bond = bond.Symbol
            gold = self.AddEquity("GDX", Resolution.Hour)
            gold.SetDataNormalizationMode(DataNormalizationMode.Raw)
            self.gold = gold.Symbol
        
        # init
        self.bond_close_window = RollingWindow[float](7 * 5)
        self.gold_close_window = RollingWindow[float](7 * 5)

        # set and warmup rolling close
        self.close_radf = RollingWindow[float](self.close_window_length)
        history = self.History([self.spy], self.close_window_length, Resolution.Hour)
        for index, ohlcv in history.loc[self.spy].iterrows():
            self.close_radf.Add(ohlcv["close"])

        # warm up safe assets
        if self.buy_bond:
            history = self.History([self.bond, self.gold], 7 * 5, Resolution.Hour) # , self.gold     
            for index, ohlcv in history.loc[self.bond].iterrows():
                self.bond_close_window.Add(ohlcv["close"])
            for index, ohlcv in history.loc[self.gold].iterrows():
                self.gold_close_window.Add(ohlcv["close"])

    def OnData(self, data):
        
        # check market hours
        exchange = self.Securities[self.spy].Exchange

        if not exchange.ExchangeOpen:
            self.Debug("Exchange is closed.")
            return

        # extract radf
        if self.LiveMode:
            # check if SPY data is ready
            # if not data.ContainsKey(self.spy):
            #     self.Log(f'SPY is not ready')
            #     return
            # get SPY data
            spy = self.Securities[self.spy]
            o, h, l, c = spy.Open, spy.High, spy.Low, spy.Close
            if c == 0: 
                self.Log("SPY close price is 0.")    
                return
            # check if custom data is ready
            if not data.ContainsKey(self.symbol):
                self.Log(f'Radg agg is not ready')
                return
            # define radf value and spy close
            radf = data[self.symbol].Value
            self.close_radf.Add(c)
        else:
            if not self.radf_sma.IsReady:
                self.Log(f'Radf agg SMA is not ready')
                return
            if not data.Bars.ContainsKey("SPY"):
                self.Log(f'SPY is not ready')
                return
            if not data.ContainsKey(self.symbol):
                self.Log(f'Radg agg is not ready')
                return
            radf = self.radf_sma.Current.Value
            radf_current = data[self.symbol].Value
            c = data[self.spy].Close
            self.close_radf.Add(c)

            # safe assets
            if self.buy_bond:
                if not data.ContainsKey(self.bond) or not data.ContainsKey(self.gold):
                    self.Log(f'Safe assets are not ready')
                    return

        # safe assets data
        if self.buy_bond:
            self.bond_close_window.Add(data[self.bond].Close)
            self.Plot(f"Alternative assets", "Bond", self.bond_close_window[0])
            self.gold_close_window.Add(data[self.gold].Close)
            self.Plot(f"Alternative assets", "Gold", self.gold_close_window[0])

        # current return
        close_diff = self.close_radf[0] - self.close_radf[self.close_window_length - 1]
        self.Log(close_diff)
    
        # plot indicator
        self.Plot("Exuber Indicators", "Radf Sum SD", radf)
        self.Plot("Exuber Indicators", "Exuber Agg Threshold", self.thresold)
        
        # mannually set sizes
        # if self.size  == 0 and radf < self.thresold:
            # self.size = math.floor(self.budget / c)
            # self.buy_order = self.MarketOrder(self.spy, self.size)
            # self.Log(f"Buy Exuber. Exuber size after buy {self.size}")
        # elif self.size > 0 and radf >= self.thresold and close_diff < 0:
            # self.sell_order = self.MarketOrder(self.spy, -self.size)
            # self.last_pl = self.buy_order.AverageFillPrice - self.sell_order.AverageFillPrice
            # self.budget = self.budget + math.floor(self.last_pl)
            # self.size = 0
            # self.Log("Sell Exuber")

        ## buy or sell all
        if not self.Portfolio[self.spy].Invested and (radf < self.thresold):
            if self.buy_bond:
                if self.Portfolio[self.bond].Invested:
                    self.Liquidate(self.bond, "Sell bonds.")
                if self.Portfolio[self.gold].Invested and self.buy_bond:
                    self.Liquidate(self.gold, "Sell gold.")
            self.Log(f'Buy at radf {radf}')
            self.SetHoldings("SPY", 1)
        elif self.Portfolio["SPY"].Invested and (radf > self.thresold) and close_diff < 0:
            self.Log(f'Sell at radf {radf}')
            self.Liquidate("SPY", "radf greater than threshold")
            if self.buy_bond:

                # calculate momentum for alternative assets
                bond_prices = list(self.bond_close_window)
                bond_momentum = bond_prices[0] / bond_prices[-1] - 1
                bond_prices.reverse()
                bond_sd = statistics.stdev(bond_prices)
                bond_momentum = bond_momentum / bond_sd
                gold_prices = list(self.gold_close_window)
                gold_prices.reverse()
                gold_momentum = gold_prices[0] / gold_prices[-1] - 1
                gold_sd = statistics.stdev(gold_prices)
                gold_momentum = gold_momentum / gold_sd
                
                # trade alternative assets
                if bond_momentum > gold_momentum and bond_momentum > 0:
                    self.SetHoldings(self.bond, 1)
                elif gold_momentum > bond_momentum and gold_momentum > 0:
                    self.SetHoldings(self.gold, 1)


class RadfAgg(PythonData):
    
    def GetSource(self, config, date, isLive):
        # "https://www.dropbox.com/s/4b1vrn1v7i8nppj/1-200-2.csv?dl=1"
        # "https://www.dropbox.com/s/qmn7eqri0jlloy1/1-100-5.csv?dl=1"
        if isLive:
            return SubscriptionDataSource("https://contentiobatch.blob.core.windows.net/qc-live/exuber.csv", SubscriptionTransportMedium.RemoteFile) 
        else:
            return SubscriptionDataSource("https://www.dropbox.com/s/7pp26sqvnd2qg69/1-600-1.csv?dl=1", SubscriptionTransportMedium.RemoteFile)
        
    def Reader(self, config, line, date, isLive):
        # If first character is not digit, pass
        if not (line.strip() and line[0].isdigit()): return None

        if isLive:
            data = line.split(',') 
            index = RadfAgg()
            index.Symbol = config.Symbol
            index.EndTime = datetime.utcnow()
            index.Time = datetime.utcnow()
            index.Value = float(data[7])       # 7 sd_radf_sum_sma_8
            index["adf"] = float(data[1])
            index["sadf"] = float(data[2])
            index["gsadf"] = float(data[3])
            index["bsadf"] = float(data[5])
            index["radf_sum"] = float(data[6])

        else:
            data = line.split(',')
            index = RadfAgg()
            index.Symbol = config.Symbol
            index.Time = datetime.strptime(data[0], '%Y-%m-%d %H:%M:%S') + timedelta(hours=1) # Make sure we only get this data AFTER trading day - don't want forward bias.
            index.Value = float(data[6])       # 5 or 6
            index["adf"] = float(data[1])      # sd_adf 
            index["sadf"] = float(data[2])     # sd_sadf
            index["gsadf"] = float(data[3])    # sd_gsadf
            index["badf"] = float(data[4])     # sd_badf
            index["bsadf"] = float(data[5])    # sd_bsadf
            index["radf_sum"] = float(data[6]) # sd_radf_sum

        return index



"""