| Overall Statistics |
|
Total Trades 210 Average Win 0.06% Average Loss -0.06% Compounding Annual Return 216.373% Drawdown 1.200% Expectancy 0.642 Net Profit 3.206% Sharpe Ratio 6.366 Loss Rate 17% Win Rate 83% Profit-Loss Ratio 0.97 Alpha 1.161 Beta -1.518 Annual Standard Deviation 0.126 Annual Variance 0.016 Information Ratio 3.849 Tracking Error 0.148 Treynor Ratio -0.529 Total Fees $1408.42 |
namespace QuantConnect.Algorithm.CSharp
{
public class ValuedSecurity
{
public FineFundamental sec;
public decimal roc;
public decimal EY;
public int rocRank;
public int EYRank;
}
public class LittleBookAlgorithm : QCAlgorithm
{
public override void Initialize()
{
SetStartDate(2019, 9, 10); //Set Start Date 1998 is the minimum start date
SetCash(1000000); //Set Strategy Cash
// AddEquity("SPY", Resolution.Minute);
SetAlpha(new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromDays(365)));
SetExecution(new ImmediateExecutionModel());
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
//SetRiskManagement(new MaximumDrawdownPercentPerSecurity(0.01m));
SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
}
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
/// Slice object keyed by symbol containing the stock data
public override void OnData(Slice data)
{
// if (!Portfolio.Invested)
// {
// SetHoldings(_spy, 1);
// Debug("Purchased Stock");
//}
}
// sort the data by daily dollar volume and take the top 'NumberOfSymbols'
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
var numberOfSymbolsCoarse = 3500;
// select only symbols with fundamental data and sort descending by daily dollar volume
var sortedByDollarVolume = coarse
.Where(x => x.HasFundamentalData && !x.Symbol.Value.EndsWith("ADR"))
.OrderByDescending(x => x.DollarVolume);
// take the top entries from our sorted collection
var top5 = sortedByDollarVolume.Take(numberOfSymbolsCoarse);
// we need to return only the symbol objects
return top5.Select(x => x.Symbol);
}
// sort by roc + earning_yield, take the top 20
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
var numberOfSymbolsFine = 30;
// take the top entries from our sorted collection
var topFine = ValueSecurities(fine).Take(numberOfSymbolsFine);
// we need to return only the symbol objects
return topFine.Select(x => x.sec.Symbol);
}
public IEnumerable<ValuedSecurity> ValueSecurities(IEnumerable<FineFundamental> fine)
{
// first calculate the values for each
var valuedList = fine
//ensure all the needed variables exist
.Where(x =>
x.FinancialStatements.IncomeStatement.EBIT != 0.0m
&& x.FinancialStatements.BalanceSheet.WorkingCapital != 0.0m
&& x.FinancialStatements.BalanceSheet.NetTangibleAssets != 0.0m
&& x.ValuationRatios.EarningYield != 0.0m
// some limits set by the book
&& x.OperationRatios.ROA > 0.25m
&& x.ValuationRatios.PERatio >= 5m
&& x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.Utilities
&& x.AssetClassification.MorningstarIndustryGroupCode != MorningstarSectorCode.FinancialServices
)
.Select((x) => new ValuedSecurity()
{
sec = x,
roc =
(x.FinancialStatements.IncomeStatement.EBIT /
(x.FinancialStatements.BalanceSheet.WorkingCapital + x.FinancialStatements.BalanceSheet.NetTangibleAssets)
),
EY = x.ValuationRatios.EarningYield
});
// next get the rank number for each
valuedList = valuedList.OrderByDescending((v) => v.roc).Select((val, i) => { val.rocRank = i; return val; });
valuedList = valuedList.OrderByDescending((v) => v.EY).Select((val, i) => { val.EYRank = i; return val; });
return valuedList.OrderBy(v => v.rocRank + v.EYRank).ToList();
}
}
}