Overall Statistics
Total Trades
2640
Average Win
0.08%
Average Loss
-0.05%
Compounding Annual Return
11.815%
Drawdown
31.700%
Expectancy
1.008
Net Profit
144.571%
Sharpe Ratio
0.727
Probabilistic Sharpe Ratio
17.216%
Loss Rate
25%
Win Rate
75%
Profit-Loss Ratio
1.69
Alpha
0.125
Beta
-0.127
Annual Standard Deviation
0.149
Annual Variance
0.022
Information Ratio
-0.093
Tracking Error
0.225
Treynor Ratio
-0.854
Total Fees
$2695.54
namespace QuantConnect.Algorithm.CSharp
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using QuantConnect;
    using QuantConnect.Algorithm.Framework.Alphas;
    using QuantConnect.Algorithm.Framework.Execution;
    using QuantConnect.Algorithm.Framework.Portfolio;
    using QuantConnect.Algorithm.Framework.Selection;
    using QuantConnect.Brokerages;
    using QuantConnect.Data;
    using QuantConnect.Data.Fundamental;
    using QuantConnect.Data.UniverseSelection;
    using QuantConnect.Interfaces;
    using QuantConnect.Orders;
    using QuantConnect.Securities.Equity;
    using Period = Data.Fundamental.Period;
    using stat = MathNet.Numerics.Statistics.Statistics;

    //import statistics as stat
    //import pickle
    //from collections import deque

    //class DynamicCalibratedGearbox(QCAlgorithm):
    public class G_ScoreInvesting : QCAlgorithm
    {
        private G_ScoreInvesting __this;
        private string tech_ROA_key;
        private int curr_month;

        private Dictionary<Symbol, Queue<decimal>> tech_ROA;
        private int quarters;

        public static class C
        {
            public static readonly CultureInfo en_us = new CultureInfo("en-us");
        }
        //    def Initialize(self):
        public override void Initialize()
        {
            __this = this;
            //        ### IMPORTANT: FOR USERS RUNNING THIS ALGORITHM IN LIVE TRADING,
            //        ### RUN THE BACKTEST ONCE

            //self.tech_ROA_key = 'TECH_ROA'
            __this.tech_ROA_key = "TECH_ROA";
            // we need 3 extra years to warmup our ROA values
            //self.SetStartDate(2012, 9, 1)
            __this.SetStartDate(2012, 9, 1);
            //self.SetEndDate(2020, 9, 1)
            __this.SetEndDate(2020, 9, 1);


            //self.SetCash(100000)  # Set Strategy Cash
            __this.SetCash(100000);

            //self.SetBrokerageModel(    AlphaStreamsBrokerageModel())
            __this.SetBrokerageModel(new AlphaStreamsBrokerageModel());
            //self.SetAlpha(    ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(days = 31)))
            __this.SetAlpha(new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromDays(31)));
            //self.SetExecution(    ImmediateExecutionModel())
            __this.SetExecution(new ImmediateExecutionModel());
            //self.SetPortfolioConstruction(    EqualWeightingPortfolioConstructionModel(lambda time: None))
            __this.SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel(time => null));


            //self.AddUniverseSelection(
            __this.AddUniverseSelection(
            //      FineFundamentalUniverseSelectionModel(self.CoarseFilter, self.FineFilter)
                new FineFundamentalUniverseSelectionModel(this.CoarseFilter, this.FineFilter)
            //)
            );
            //self.UniverseSettings.Resolution = Resolution.Daily
            __this.UniverseSettings.Resolution = Resolution.Daily;

            //self.curr_month = -1
            __this.curr_month = -1;

            //store ROA of tech stocks
            //self.tech_ROA = { }
            __this.tech_ROA = new Dictionary<Symbol, Queue<decimal>>();

            //self.symbols = None
            object symbols = null;

            //        if self.LiveMode and not self.ObjectStore.ContainsKey(self.tech_ROA_key):
            if (LiveMode && !ObjectStore.ContainsKey(tech_ROA_key))
            {
                //self.Quit('QUITTING: USING LIVE MOVE WITHOUT TECH_ROA VALUES IN OBJECT STORE')
                __this.Quit("QUITTING: USING LIVE MOVE WITHOUT TECH_ROA VALUES IN OBJECT STORE");
            }

            //self.quarters = 0
            __this.quarters = 0;
            ObjectStore.Delete(tech_ROA_key);
#if DEBUG
            //If error happened in testing get rid of bad data.  Comment this out once close to production
            //ObjectStore.Delete(tech_ROA_key);
#endif
        }

        public override void OnEndOfAlgorithm()
        {
            //self.Log('Algorithm End')
            __this.Log("Algorithm End");

            //self.SaveData()
            __this.SaveData();
        }

        //    def    SaveData(self):
        public void SaveData()
        {
            //        '''
            //        Saves the tech ROA data to ObjectStore
            //        '''

            // Symbol objects aren't picklable, hence why we use the ticker string
            //  tech_ROA    = {symbol.Value:ROA for symbol, ROA in self.tech_ROA.items()}
            var tech_ROAsav = new Dictionary<string, Queue<decimal>>();
            foreach (Symbol key in tech_ROA.Keys)
            {
            	if(tech_ROAsav.ContainsKey(key.Value)) continue;
                tech_ROAsav.Add(key.Value, this.tech_ROA[key]);
            }

            //self.ObjectStore.SaveBytes(self.tech_ROA_key, pickle.dumps(tech_ROA))
            //__this.ObjectStore.SaveJson(tech_ROA_key, tech_ROAsav);
        }

        //    def CoarseFilter(self, coarse):
        public IEnumerable<Symbol> CoarseFilter(IEnumerable<CoarseFundamental> coarse)
        {
            //        # load data from ObjectStore
            //        if len(self.tech_ROA) == 0 and self.ObjectStore.ContainsKey(self.tech_ROA_key):
            if (this.tech_ROA.Count == 0 && ObjectStore.ContainsKey(tech_ROA_key))
            {
                //            tech_ROA = self.ObjectStore.ReadBytes(self.tech_ROA_key)
                var tech_ROAstr = ObjectStore.ReadJson<Dictionary<string, Queue<decimal>>>(tech_ROA_key);
                //            tech_ROA = pickle.loads(bytearray(tech_ROA))
                //            self.tech_ROA = { Symbol.Create(ticker, SecurityType.Equity, Market.USA):ROA for ticker, ROA in tech_ROA.items()}
                //Don't have pickle in C# so instead, deserialize a Dictionary with string tickers and convert them into Symbol keys
                this.tech_ROA.Clear();
                foreach (string key in tech_ROAstr.Keys)
                {
                    this.tech_ROA.Add(QuantConnect.Symbol.Create(key, SecurityType.Equity, Market.USA), tech_ROAstr[key]);
                }
                //return list(self.tech_ROA.keys())
                return this.tech_ROA.Keys.ToArray();
            }
            //if self.curr_month == self.Time.month:            
            if (this.curr_month == this.Time.Month)
            {
                //return Universe.Unchanged
                return Universe.Unchanged;
            }
            //self.curr_month = self.Time.month
            __this.curr_month = this.Time.Month;

            // we only want to update our ROA values every three months
            //if self.Time.month % 3 != 1:
            if (this.Time.Month % 3 != 1)
            {
                //return Universe.Unchanged
                return Universe.Unchanged;
            }

            //self.quarters += 1
            __this.quarters += 1;

            //return [c.Symbol for c in coarse if c.HasFundamentalData]
            return coarse.Where(c => c.HasFundamentalData).Select(c => c.Symbol);
        }

        //    def FineFilter(self, fine):
        public IEnumerable<Symbol> FineFilter(IEnumerable<FineFundamental> fine)
        {
            const int maxlen = 12;
            //Debug("Fine count: " + fine.Count());
            //book value == FinancialStatements.BalanceSheet.NetTangibleAssets (book value and NTA are synonyms)
            //BM (Book-to-Market) == book value / MarketCap
            //ROA == OperationRatios.ROA
            //CFROA == FinancialStatements.CashFlowStatement.OperatingCashFlow / FinancialStatements.BalanceSheet.TotalAssets
            //R&D to MktCap == FinancialStatements.IncomeStatement.ResearchAndDevelopment / MarketCap
            //CapEx to MktCap == FinancialStatements.CashFlowStatement.CapExReported / MarketCap
            //Advertising to MktCap == FinancialStatements.IncomeStatement.SellingGeneralAndAdministration / MarketCap
            //  note: this parameter may be slightly higher than pure advertising costs

            //        tech_securities = [f for f in fine if f.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology and
            //                                                f.OperationRatios.ROA.ThreeMonths]
            var tech_securities = fine.Where(f => f.AssetClassification.MorningstarSectorCode == MorningstarSectorCode.Technology &&
                f.OperationRatios.ROA.ThreeMonths != 0.0m);
            //       for security in tech_securities:
            foreach (var security in tech_securities)
            {
				//Debug("Fine security: " + security.Symbol.Value + "; " + security.Value);

                //we use deques instead of RWs since deques are picklable
                //actually in C#, LinkedList would be a good choice, but here we use Queue, though a custom object with its own serialization would be good
                //  symbol = security.Symbol
                var symbol = security.Symbol;
                //if symbol not in self.tech_ROA:
                if (this.tech_ROA.Keys.Contains(symbol) == false)
                {
                    //3 years * 4 quarters = 12 quarters of data
                    //self.tech_ROA[symbol] = deque(maxlen = 12)
                    //note: cannot not set maxlen on LinkedList, but there is a const int defined as 12 so set it on Queue
                    tech_ROA[symbol] = new Queue<decimal>(maxlen);
                }
                //self.tech_ROA [symbol].append(security.OperationRatios.ROA.ThreeMonths)
                this.tech_ROA[symbol].Enqueue(Convert.ToDecimal(security.OperationRatios.ROA.ThreeMonths));
                if(this.tech_ROA[symbol].Count > maxlen) this.tech_ROA[symbol].Dequeue();

            }

            //        if self.LiveMode:
            if (LiveMode)
            {
                //this ensures we don't lose new data from an algorithm outage
                //            self.SaveData()
                SaveData();
            }
            //we want to rebalance in the fourth month after the (fiscal) year ends
            //so that we have the most recent quarter's data
            //if self.Time.month != 4 or(self.quarters < 12 and not self.LiveMode):
            if (Time.Month != 4 || (quarters < 12 && LiveMode == false))
            {
                //return Universe.Unchanged
                return Universe.Unchanged;
            }
            //make sure our stocks has these fundamentals
            //tech_securities = [x for x in tech_securities if x.OperationRatios.ROA.OneYear and
            //                                                        x.FinancialStatements.CashFlowStatement.OperatingCashFlow.TwelveMonths and
            //                                                        x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths and
            //                                                        x.FinancialStatements.IncomeStatement.ResearchAndDevelopment.TwelveMonths and
            //                                                        x.FinancialStatements.CashFlowStatement.CapExReported.TwelveMonths and
            //                                                        x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.TwelveMonths and
            //                                                        x.MarketCap]
            tech_securities = tech_securities.Where(x => x.OperationRatios.ROA.OneYear != 0.0m &&
                                                                    x.FinancialStatements.CashFlowStatement.OperatingCashFlow.TwelveMonths != 0.0m &&
                                                                    x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths != 0.0m &&
                                                                    x.FinancialStatements.IncomeStatement.ResearchAndDevelopment.TwelveMonths != 0.0m &&
                                                                    x.FinancialStatements.CashFlowStatement.CapExReported.TwelveMonths != 0.0m &&
                                                                    x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.TwelveMonths != 0.0m &&
                                                                    x.MarketCap != 0.0m);

            //compute the variance of the ROA for each tech stock
            //  tech_VARROA = {symbol:stat.variance(ROA) for symbol, ROA in self.tech_ROA.items() if len(ROA) == ROA.maxlen}
            var tech_VARROA = new Dictionary<Symbol, double>();
            foreach (var key in tech_ROA.Keys)
            {
                var item = tech_ROA[key];
                if (item.Count != maxlen) continue;
                var variance = stat.Variance(item.Select(i => (double)i));
                tech_VARROA[key] = variance;
            }
            //if len(tech_VARROA) < 2:
            if (tech_VARROA.Count < 2)
            {
                //return Universe.Unchanged
                return Universe.Unchanged;
            }
            //  tech_VARROA_median = stat.median(tech_VARROA.values())
            var tech_VARROA_median = stat.Median(tech_VARROA.Values);
            //we will now map tech Symbols to various fundamental ratios, 
            //  and compute the median for each ratio

            //ROA 1-year
            //  tech_ROA1Y = { x.Symbol:x.OperationRatios.ROA.OneYear for x in tech_securities}
            var tech_ROA1Y = MakeDictionary(tech_securities, x => x.OperationRatios.ROA.OneYear);
            //tech_ROA1Y_median = stat.median(tech_ROA1Y.values())
            var tech_ROA1Y_median = stat.Median(tech_ROA1Y.Values.Select(v => (double)v));
            /*
            Cash Flow ROA
            tech_CFROA = {
                x.Symbol: (
                x.FinancialStatements.CashFlowStatement.OperatingCashFlow.TwelveMonths
                    / x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths
                    ) for x in tech_securities
            }
            */
            var tech_CFROA = MakeDictionary(tech_securities, x => x.FinancialStatements.CashFlowStatement.OperatingCashFlow.TwelveMonths
                    / x.FinancialStatements.BalanceSheet.TotalAssets.TwelveMonths);
            //  tech_CFROA_median = stat.median(tech_CFROA.values())
            var tech_CFROA_median = stat.Median(tech_CFROA.Values.Select(v => (double)v));
            //R&D to MktCap
            //tech_RD2MktCap = {
            //    x.Symbol: (
            //x.FinancialStatements.IncomeStatement.ResearchAndDevelopment.TwelveMonths / x.MarketCap
            //) for x in tech_securities}
            var tech_RD2MktCap = MakeDictionary(tech_securities, x => x.FinancialStatements.IncomeStatement.ResearchAndDevelopment.TwelveMonths / x.MarketCap);

            //  tech_RD2MktCap_median = stat.median(tech_RD2MktCap.values())
            var tech_RD2MktCap_median = stat.Median(tech_RD2MktCap.Values.Select(v => (double)v));
            //CapEx to MktCap
            //tech_CaPex2MktCap = {
            //    x.Symbol: (
            //x.FinancialStatements.CashFlowStatement.CapExReported.TwelveMonths / x.MarketCap
            //) for x in tech_securities}
            var tech_CaPex2MktCap = MakeDictionary(tech_securities, x => x.FinancialStatements.CashFlowStatement.CapExReported.TwelveMonths / x.MarketCap);
            //  tech_CaPex2MktCap_median = stat.median(tech_CaPex2MktCap.values())
            var tech_CaPex2MktCap_median = stat.Median(tech_CaPex2MktCap.Values.Select(v => (double)v));
            //Advertising to MktCap
            //tech_Ad2MktCap = {
            //    x.Symbol: (
            //x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.TwelveMonths / x.MarketCap
            //) for x in tech_securities}
            var tech_Ad2MktCap = MakeDictionary(tech_securities, x => x.FinancialStatements.IncomeStatement.SellingGeneralAndAdministration.TwelveMonths / x.MarketCap);
            //  tech_Ad2MktCap_median = stat.median(tech_Ad2MktCap.values())
            var tech_Ad2MktCap_median = stat.Median(tech_Ad2MktCap.Values.Select(v => (double)v));
            //sort fine by book-to-market ratio, get lower quintile
            //  has_book = [f for f in fine if f.FinancialStatements.BalanceSheet.NetTangibleAssets.TwelveMonths and f.MarketCap]
            var has_book = fine.Where(f => f.FinancialStatements.BalanceSheet.NetTangibleAssets.TwelveMonths != 0.0m && f.MarketCap != 0.0m);
            //  sorted_by_BM = sorted(has_book, key = lambda x: x.FinancialStatements.BalanceSheet.NetTangibleAssets.TwelveMonths / x.MarketCap)[:len(has_book)//4]
            var sorted_by_BM = has_book.OrderBy(x => x.FinancialStatements.BalanceSheet.NetTangibleAssets.TwelveMonths / x.MarketCap).Take((int)Math.Floor(has_book.Count() / 4.0m));
            //choose tech stocks from lower quintile
            //  tech_symbols = [f.Symbol for f in sorted_by_BM if f in tech_securities]
            var tech_symbols = sorted_by_BM.Where(f => tech_securities.Any(ts => ts.Symbol == f.Symbol)).Select(x => x.Symbol);

            //        ratioDicts_medians = [(tech_ROA1Y, tech_ROA1Y_median),
            //                                (tech_CFROA, tech_CFROA_median), (tech_RD2MktCap, tech_RD2MktCap_median),
            //                                (tech_CaPex2MktCap, tech_CaPex2MktCap_median), (tech_Ad2MktCap, tech_Ad2MktCap_median)]
            var ratioDicts_medians = new Dictionary<Dictionary<Symbol, decimal>, double>();
            ratioDicts_medians.Add(tech_ROA1Y, tech_ROA1Y_median);
            ratioDicts_medians.Add(tech_CFROA, tech_CFROA_median); ratioDicts_medians.Add(tech_RD2MktCap, tech_RD2MktCap_median);
            ratioDicts_medians.Add(tech_CaPex2MktCap, tech_CaPex2MktCap_median); ratioDicts_medians.Add(tech_Ad2MktCap, tech_Ad2MktCap_median);

            //def compute_g_score(symbol):
            var compute_g_score = new Func<Symbol, decimal>(symbol =>
            {
                //  g_score = 0
                var g_score = 0.0m;
                //if tech_CFROA[symbol] > tech_ROA1Y[symbol]:
                if (tech_CFROA[symbol] > tech_ROA1Y[symbol])
                {
                    //g_score += 1
                    g_score += 1.0m;
                }
                //if symbol in tech_VARROA          and tech_VARROA[symbol] < tech_VARROA_median:
                if (tech_VARROA.ContainsKey(symbol) && tech_VARROA[symbol] < tech_VARROA_median)
                    //g_score += 1
                    g_score += 1.0m;
                //     for   ratio_dict, median in ratioDicts_medians:
                foreach (var ratio_dict in ratioDicts_medians.Keys)
                {
                    var median = ratioDicts_medians[ratio_dict];
                    //if symbol in ratio_dict          and ratio_dict[symbol] > median:
                    if (ratio_dict.ContainsKey(symbol) && ratio_dict[symbol] > (decimal)median)
                        //g_score += 1
                        g_score += 1;
                }
                //return g_score
                return g_score;
            });
            //compute g-scores for each symbol    
            //  g_scores = { symbol: compute_g_score(symbol) for symbol in tech_symbols}
            var g_scores = new Dictionary<Symbol, decimal>();
            foreach (var symbol in tech_symbols)
            {
                g_scores.Add(symbol, compute_g_score(symbol));
            }

            //return [symbol for symbol, g_score in g_scores.items() if g_score >= 5]
            return g_scores.Keys.Where(key => g_scores[key] >= 5.0m);
        }
        private static Dictionary<Symbol, decimal> MakeDictionary(IEnumerable<FineFundamental> tech_securities, Func<FineFundamental, decimal> dec)
        {
            var tech_dict = new Dictionary<Symbol, decimal>();
            foreach (var x in tech_securities)
            {
                tech_dict.Add(x.Symbol, dec(x));
            }
            return tech_dict;
        }
    }
}