Overall Statistics
Total Trades
478
Average Win
2.02%
Average Loss
-1.65%
Compounding Annual Return
-0.383%
Drawdown
66.600%
Expectancy
0.136
Net Profit
-2.620%
Sharpe Ratio
0.22
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.23
Alpha
0.074
Beta
0.457
Annual Standard Deviation
0.514
Annual Variance
0.265
Information Ratio
0.054
Tracking Error
0.516
Treynor Ratio
0.247
Total Fees
$0.00
namespace QuantConnect
{
    public enum EntryMarketDirection
    {
        GoShort  = -1,
        DoNothing = 0,
        GoLong = 1
    }
}                        
namespace QuantConnect
{
    /// <summary>
    ///     Every month, the investor considers whether the excess return of each asset over the past 12
    ///     months is positive or negative and goes long on the contract if it is positive and short if
    ///     negative. The position size is set to be inversely proportional to the instrument’s volatility.
    ///     Source: http://quantpedia.com/Screener/Details/118
    /// </summary>
    /// <seealso cref="QuantConnect.Algorithm.QCAlgorithm" />
    public class TimeSeriesMomentumEffect : QCAlgorithm
    {
        #region Algorithm Parameters

        private const decimal maxExposure = 0.5m;
        private readonly string forexMarket = "oanda";
        private readonly string cfdMarket = "oanda";

        private const int cfdLeverage = 20;
        private const int forexLeverage = 20;

        private const int volatilityWindow = 30;

        #endregion

        #region Investment Universe

        /*
         The investment universe consists of:
            - 22 commodity futures
            - 12 cross-currency pairs (with 9 underlying currencies)
            - 9 developed equity indexes
            - 4 developed government bond futures. 
        */

        // The 10 year maturity Treasury Yield Curve is used as a proxy of the risk free rate.
        private const string riskFreeReturnQuandlCode = "USTREASURY/YIELD";

        private readonly string[] comoditiesFuturesTickers =
        {
            Futures.Metals.Gold,
            Futures.Metals.Platinum,
            Futures.Metals.Silver,
            Futures.Metals.Palladium,
            Futures.Energies.CrudeOilWTI,
            Futures.Energies.HeatingOil,
            Futures.Energies.Gasoline,
            Futures.Energies.NaturalGas,
            Futures.Grains.Corn,
            Futures.Grains.Soybeans,
            Futures.Grains.SoybeanMeal,
            Futures.Grains.SoybeanOil,
            Futures.Grains.Wheat,
            Futures.Grains.Oats,
            Futures.Meats.LeanHogs,
            Futures.Meats.LiveCattle,
            Futures.Meats.FeederCattle,
            Futures.Softs.OrangeJuice,
            Futures.Softs.Cocoa,
            Futures.Softs.Coffee,
            Futures.Softs.Cotton2,
            Futures.Softs.Sugar11
        };

        private readonly string[] forexTickers =
        {
            "EURUSD", "USDJPY", "USDCHF", "GBPUSD", "USDCAD", "AUDUSD",
            "USDCNH", "NZDUSD", "EURJPY", "EURCHF", "EURGBP", "GBPJPY"
        };

        private readonly string[] cfdTickers =
        {
            "AU200AUD", // Australia 200 - Australian Dollar
            "DE30EUR", // Germany 30 - Euro Dollar
            "EU50EUR", // Europe 50 - Euro Dollar
            "CH20CHF", // Swiss 20 - Swiss Frank
            "FR40EUR", // France 40 - Euro Dollar
            "HK33HKD", // Hong Kong 33 - Hk Dollar
            "JP225USD", // Japan 225 - Us Dollar
            "UK100GBP", // Uk 100 - English Pound
            "SPX500USD" // S&p 500 - Us Dollar
        };

        private readonly string[] bondsFuturesTickers =
        {
            "SCF/CME_TU1_ON", // CBOT 2-year US Treasury Note Futures
            "SCF/CME_FV1_ON", // CBOT 5-year US Treasury Note Futures
            "SCF/CME_TY1_ON", // CBOT 10-year US Treasury Note Futures
            "SCF/CME_US2_ON", // CBOT 30-year US Treasury Bond Futures
            "SCF/EUREX_FGBL2_EN", // EUREX Euro-Bund Futures (German debt security)
            "SCF/EUREX_FBTP2_EN", // EUREX Euro-BTP Futures (Italian debt security)
            "SCF/EUREX_FOAT2_EN" // EUREX Euro-OAT Futures (French debt security)
        };

        #endregion

        #region Fields

        private readonly List<Symbol> symbols = new List<Symbol>();
        private readonly Dictionary<Symbol, decimal> excessReturns = new Dictionary<Symbol, decimal>();

        private readonly Dictionary<SecurityType, decimal> portfolioShareToAssetType =
            new Dictionary<SecurityType, decimal>();

        private bool liquidateAllPositions;
        private bool monthlyRebalance;

        private decimal riskFreeRetun;

        #endregion

        #region QCAlgorithm Methods

        public override void Initialize()
        {
            // Set the basic algorithm parameters.
            SetStartDate(2010, 06, 01);
            SetEndDate(2017, 03, 30);
            SetCash(100000);

            var brokerage = forexMarket == "fxcm" ? BrokerageName.FxcmBrokerage : BrokerageName.OandaBrokerage;
            SetBrokerageModel(brokerage);

            AddData<QuandlUSTeasuryYield>(riskFreeReturnQuandlCode, Resolution.Daily);

            foreach (var ticker in forexTickers)
            {
                var security = AddForex(ticker, Resolution.Daily, forexMarket, leverage: forexLeverage);
                symbols.Add(security.Symbol);
            }

            foreach (var ticker in cfdTickers)
            {
                var security = AddCfd(ticker, Resolution.Daily, cfdMarket, leverage: cfdLeverage);
                symbols.Add(security.Symbol);
            }

            foreach (var symbol in symbols)
            {
                // Given the big diversity in currencies and prices,, variance can't be used as volatility proxy because the values will not be comparable.
                // That's why I use https://en.wikipedia.org/wiki/Coefficient_of_variation
                Securities[symbol].VolatilityModel =
                    new IndicatorVolatilityModel<IndicatorDataPoint>(
                        STD(symbol, volatilityWindow, Resolution.Daily)
                            .Over(SMA(symbol, volatilityWindow, Resolution.Daily)));
                if (!portfolioShareToAssetType.ContainsKey(symbol.SecurityType))
                    portfolioShareToAssetType[symbol.SecurityType] = 1;
            }


            /*
            foreach (var ticker in comoditiesFuturesTickers)
                symbols.Add(AddFuture(ticker, Resolution.Daily).Symbol);
            */

            var assets = portfolioShareToAssetType.Keys.ToArray();
            foreach (var assetType in assets)
            {
                portfolioShareToAssetType[assetType] = 1m / assets.Length;
            }

            Schedule.On(DateRules.MonthStart(), TimeRules.At(0, 0), () =>
            {
                UpdateAssetsReturns();
                liquidateAllPositions = true;
            });

            SetWarmUp(TimeSpan.FromDays(volatilityWindow));
        }

        public override void OnData(Slice slice)
        {
            if (IsWarmingUp) return;

            if (liquidateAllPositions)
            {
                // Liquidate all existing holdings.
                Liquidate();
                liquidateAllPositions = false;
                monthlyRebalance = true;
            }

            if (monthlyRebalance && !Portfolio.Invested)
            {
                var newOrders = EstimateNewOrders();
                foreach (var order in newOrders)
                {
                    var unitValue = new MarketOrder(order.Symbol, 1, Time).GetValue(Securities[order.Symbol]);
                    if (unitValue == 0) continue;
                    var orderValue = Portfolio.TotalPortfolioValue * order.TargetHolding;
                    var quantity = (int) (orderValue / unitValue);
                    if (quantity != 0)
                    {
                        MarketOrder(order.Symbol, quantity);
                    }
                    //SetHoldings(order.Symbol, order.TargetHolding);
                }
                monthlyRebalance = false;
            }
        }

        public void OnData(Quandl data)
        {
            riskFreeRetun = data.Price;
        }

        #endregion

        #region Auxiliary Methods

        private List<SecuritiesOrders> EstimateNewOrders()
        {
            var orders = new List<SecuritiesOrders>();

            var volatilitySumByAsset = from s in symbols
                group s by s.SecurityType
                into grouped
                select new
                {
                    AssetType = grouped.Key,
                    VolatilitySum = grouped.Sum(s => Securities[s].VolatilityModel.Volatility)
                };

            foreach (var symbol in symbols)
            {
                var volatility = Securities[symbol].VolatilityModel.Volatility;
                var weightedVolatility = volatility / volatilitySumByAsset
                                             .First(v => v.AssetType == symbol.SecurityType)
                                             .VolatilitySum;
                var leverage = Securities[symbol].Leverage;
                var portfolioShareAsAsset = portfolioShareToAssetType[symbol.SecurityType];
                var orderDirection = Math.Sign(excessReturns[symbol]);
                var targetHoldings = maxExposure * portfolioShareAsAsset * orderDirection * weightedVolatility *
                                     leverage;
                orders.Add(new SecuritiesOrders
                {
                    Symbol = symbol,
                    Direction = (EntryMarketDirection) orderDirection,
                    TargetHolding = targetHoldings
                });
            }
            return orders;
        }

        private void UpdateAssetsReturns()
        {
            var dateRequest = new DateTime(Time.Year - 1, Time.Month, Time.Day);
            // I ask for some days before just in case the selected day hasn't historical prices record.
            var history = History(symbols, dateRequest.AddDays(-5), dateRequest.AddDays(1), Resolution.Daily);
            foreach (var symbol in symbols)
                try
                {
                    var slice = history.Last(s => s.ContainsKey(symbol));
                    excessReturns[symbol] = (Securities[symbol].Price / slice[symbol].Price - 1m) * 100m -
                                            riskFreeRetun;
                }
                catch (Exception e)
                {
                    Console.WriteLine(symbol + " hasn't data to estimate excess returns.");
                    excessReturns[symbol] = 0m;
                }
        }

        #endregion
    }

    public struct SecuritiesOrders
    {
        public Symbol Symbol;
        public EntryMarketDirection Direction;
        public decimal TargetHolding;
    }

    public class QuandlBondFutures : Quandl
    {
        public QuandlBondFutures()
            : base("Settle")
        {
        }
    }

    public class QuandlUSTeasuryYield : Quandl
    {
        public QuandlUSTeasuryYield()
            : base("10 YR")
        {
        }
    }
}