| Overall Statistics |
|
Total Trades 1237 Average Win 1.00% Average Loss -0.90% Compounding Annual Return 28.225% Drawdown 39.400% Expectancy 0.218 Net Profit 247.573% Sharpe Ratio 0.995 Probabilistic Sharpe Ratio 38.496% Loss Rate 42% Win Rate 58% Profit-Loss Ratio 1.11 Alpha 0.118 Beta 1.246 Annual Standard Deviation 0.276 Annual Variance 0.076 Information Ratio 0.832 Tracking Error 0.179 Treynor Ratio 0.22 Total Fees $24126.68 |
//Copyright Warren Harding 2020.
//Granted to the public domain.
//Use at your own risk.
namespace QuantConnect.Algorithm.CSharp
{
public class FactorOfTheDay : QCAlgorithm
{
int TotalHighDollarVolumeStocks = 10;
int TotalStocksToHold = 5;
int FactorPeriod = 20;
List<Symbol> LongSymbols = new List<Symbol>();
List<decimal> FCFYieldFitnesses = new List<decimal>();
List<decimal> BookYieldFitnesses = new List<decimal>();
List<decimal> EarningYieldFitnesses = new List<decimal>();
List<decimal> SalesYieldFitnesses = new List<decimal>();
List<decimal> CFYieldFitnesses = new List<decimal>();
public override void Initialize()
{
SetStartDate(2015, 11, 19);
SetCash(1000000);
AddUniverse(CoarseSelectionFunction, FineSelectionFunction);
}
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
return coarse
.Where(x => x.HasFundamentalData)
.OrderByDescending(x => x.DollarVolume)
.Take(TotalHighDollarVolumeStocks)
.Select(x => x.Symbol);
}
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
List<StockData> stockDatas = new List<StockData>();
foreach (FineFundamental f in fine)
{
List<TradeBar> history = History(f.Symbol, 2, Resolution.Daily).ToList();
if (history != null && history.Count == 2)
{
StockData stockData = new StockData();
stockData.Change = (history.Last().Close - history.First().Close) / history.First().Close;
stockData.FCFYield = f.ValuationRatios.FCFYield;
stockData.BookYield = f.ValuationRatios.BookValueYield;
stockData.EarningYield = f.ValuationRatios.EarningYield;
stockData.SalesYield = f.ValuationRatios.SalesYield;
stockData.CFYield = f.ValuationRatios.CFYield;
stockData.Symbol = f.Symbol;
stockDatas.Add(stockData);
}
}
decimal[] changes = stockDatas.Select(x => x.Change).ToArray();
decimal[] fcfYields = stockDatas.Select(x => x.FCFYield).ToArray();
decimal[] bookYields = stockDatas.Select(x => x.BookYield).ToArray();
decimal[] earningYields = stockDatas.Select(x => x.EarningYield).ToArray();
decimal[] salesYields = stockDatas.Select(x => x.SalesYield).ToArray();
decimal[] cfYields = stockDatas.Select(x => x.CFYield).ToArray();
decimal fcfYieldFitness = WeightedAverage(changes, fcfYields);
decimal bookYieldFitness = WeightedAverage(changes, bookYields);
decimal earningYieldFitness = WeightedAverage(changes, earningYields);
decimal salesYieldFitness = WeightedAverage(changes, salesYields);
decimal cfYieldFitness = WeightedAverage(changes, cfYields);
FCFYieldFitnesses.Add(fcfYieldFitness);
BookYieldFitnesses.Add(bookYieldFitness);
EarningYieldFitnesses.Add(earningYieldFitness);
SalesYieldFitnesses.Add(salesYieldFitness);
CFYieldFitnesses.Add(cfYieldFitness);
Remove0(FCFYieldFitnesses);
Remove0(BookYieldFitnesses);
Remove0(EarningYieldFitnesses);
Remove0(SalesYieldFitnesses);
Remove0(CFYieldFitnesses);
decimal fcfYieldFitnessTMA = TriangularMovingAverage(FCFYieldFitnesses.ToArray());
decimal bookYieldFitnessTMA = TriangularMovingAverage(BookYieldFitnesses.ToArray());
decimal earningYieldFitnessTMA = TriangularMovingAverage(EarningYieldFitnesses.ToArray());
decimal salesYieldFitnessTMA = TriangularMovingAverage(SalesYieldFitnesses.ToArray());
decimal cfYieldFitnessTMA = TriangularMovingAverage(CFYieldFitnesses.ToArray());
foreach (StockData stockData in stockDatas)
{
stockData.Fitness = stockData.FCFYield * fcfYieldFitnessTMA
+ stockData.BookYield * bookYieldFitnessTMA
+ stockData.EarningYield * earningYieldFitnessTMA
+ stockData.SalesYield * salesYieldFitnessTMA
+ stockData.CFYield * cfYieldFitnessTMA;
}
LongSymbols = stockDatas.OrderByDescending(x => x.Fitness)
.Take(TotalStocksToHold)
.Select(x => x.Symbol)
.ToList();
return LongSymbols;
}
public void Remove0(List<decimal> list)
{
if (list.Count > FactorPeriod)
{
list.RemoveAt(0);
}
}
public override void OnData(Slice data)
{
if (data.Time.DayOfWeek != DayOfWeek.Wednesday)
{
return;
}
foreach (var stock in Portfolio.Values)
{
if (stock.Invested)
{
if (LongSymbols.Exists(x => x == stock.Symbol) == false)
{
Liquidate(stock.Symbol);
}
}
}
decimal positionSize = 1m / TotalStocksToHold;
foreach (Symbol symbol in LongSymbols)
{
if (Portfolio[symbol].Invested == false && data.ContainsKey(symbol))
{
SetHoldings(symbol, positionSize);
}
}
}
public class StockData
{
public Symbol Symbol;
public decimal Change;
public decimal FCFYield;
public decimal BookYield;
public decimal EarningYield;
public decimal SalesYield;
public decimal CFYield;
public decimal Fitness;
}
public static decimal WeightedAverage(decimal[] values, decimal[] weights)
{
decimal weightsSum = weights.Sum();
if (weightsSum != 0)
{
return values.Zip(weights, (x, y) => x * y).Sum() / weights.Sum();
}
else
{
return 0;
}
}
public static decimal TriangularMovingAverage(decimal[] values)
{
return WeightedAverage(values, TriangularWeightsDecimal(values.Length));
}
public static decimal[] TriangularWeightsDecimal(int length)
{
int[] intWeights = Enumerable.Range(1, length).ToArray();
return intWeights.Select(x => Convert.ToDecimal(x)).ToArray();
}
}
}