| Overall Statistics |
|
Total Trades 2011 Average Win 0.71% Average Loss -0.71% Compounding Annual Return 24.147% Drawdown 19.300% Expectancy 0.401 Net Profit 1968.383% Sharpe Ratio 1.068 Probabilistic Sharpe Ratio 60.919% Loss Rate 30% Win Rate 70% Profit-Loss Ratio 1.00 Alpha 0.162 Beta 0.401 Annual Standard Deviation 0.183 Annual Variance 0.033 Information Ratio 0.576 Tracking Error 0.197 Treynor Ratio 0.486 Total Fees $7060.35 |
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using QuantConnect.Data.Fundamental;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
public class FundamentalAlgorithm : QCAlgorithm
{
Resolution resolution = Resolution.Daily;
int num_screener = 100;
int num_stocks = 10;
int formation_days = 200;
IEnumerable<Symbol> symbols = null;
Symbol spy;
Symbol ief;
bool rebalence_flag;
bool first_month_trade_flag;
bool trade_flag;
public override void Initialize()
{
SetStartDate(2004, 1, 1);
SetEndDate(2018, 1, 1);
SetCash(50000);
this.spy = AddEquity("SPY", Resolution.Minute).Symbol;
this.ief = AddEquity("IEF", resolution).Symbol;
UniverseSettings.Resolution = this.resolution;
SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
Schedule.On(DateRules.MonthStart("SPY"), TimeRules.At(0, 0), () =>
{
monthly_rebalance();
});
Schedule.On(DateRules.MonthStart("SPY"), TimeRules.At(10, 0), () =>
{
Rebalance();
});
// rebalance the universe selection once a month
this.rebalence_flag = false;
// make sure to run the universe selection at the start of the algorithm even it's not the manth start
this.first_month_trade_flag = true;
this.trade_flag = false;
}
private void monthly_rebalance()
{
this.rebalence_flag = true;
}
private void Rebalance()
{
var spy_hist = History(this.spy, 120, this.resolution);
var spyMeanPrice = spy_hist.Select(bar => bar.Close).ToArray().Average();
var spyPrice = Securities[this.spy].Price;
if (spyPrice < spyMeanPrice)
{
foreach (Symbol symbol in this.Portfolio.Keys)
{
if (symbol.Value != "IEF")
{
this.Liquidate();
}
}
SetHoldings("IEF", 1);
Plot("Investments", "IEF", 1);
Plot("Investments", "Stocks", 0);
return;
}
else
{
if (this.symbols == null)
return;
var chosen = this.calc_return(this.symbols);
foreach(Symbol symbol in this.Portfolio.Keys)
{
if(symbol.Value == "SPY")
{
continue;
}
if (!chosen.Contains(symbol))
{
this.SetHoldings(symbol, 0);
}
}
if (chosen.Count() > 0)
{
Plot("Investments", "IEF", 0);
Plot("Investments", "Stocks", 1);
decimal weight = 0.99m / chosen.Count();
foreach (var symbol in chosen)
{
this.SetHoldings(symbol, weight);
// weight = weight - 0.002m;
// if(weight <= 0)
// {
// break;
// }
}
}
}
}
private IEnumerable<Symbol> calc_return(IEnumerable<Symbol> stocks)
{
// calculate return for each stock
ConcurrentDictionary<Symbol, decimal> returnPerStock = new ConcurrentDictionary<Symbol, decimal>();
foreach (Symbol symbol in stocks)
{
var hist = History(symbol, this.formation_days, this.resolution);
if (hist.Count() > 0)
{
var last = hist.Last().Close;
var first = hist.First().Close;
var return1 = (last - first) / first;
returnPerStock.AddOrUpdate(symbol, return1);
}
}
var sortedDict = from entry in returnPerStock orderby entry.Value descending select entry;
return sortedDict.Select(entry => entry.Key).Take(this.num_stocks);
}
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
{
if (this.rebalence_flag || this.first_month_trade_flag)
{
var sortedByDollarVolume = coarse
.Where(x => x.HasFundamentalData)
.Where(x => x.Price > 5)
.OrderByDescending(x => x.DollarVolume);
// take the top entries from our sorted collection
var numberOfSymbolsCoarse = 200;
var top = sortedByDollarVolume.Take(numberOfSymbolsCoarse);
// we need to return only the symbol objects
return top.Select(x => x.Symbol);
}else{
return this.symbols;
}
}
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
{
if (this.rebalence_flag || this.first_month_trade_flag)
{
var selected = fine
.Where(x => x.EarningReports.BasicAverageShares.ThreeMonths * (x.EarningReports.BasicEPS.TwelveMonths * x.ValuationRatios.PERatio) > 2e9m)
.OrderByDescending(x => x.ValuationRatios.EVToEBITDA).Take(this.num_screener);
// .Where(x => x.CompanyReference.CountryId == "USA")
// .Where(x => !x.CompanyReference.IsLimitedPartnership)
// .Where(x => !x.CompanyReference.IsREIT)
// .Where(x => !x.SecurityReference.IsDepositaryReceipt)
// .Where(x => x.SecurityReference.IsPrimaryShare)
// .Where(x => x.SecurityReference.ShareClassStatus == "A")
// .Where(x => x.SecurityReference.CurrencyId == "USD")
// .Where(x => x.SecurityReference.SecurityType == "ST00000001")
// .Where(x => x.SecurityReference.ExchangeId != "OTC")
// .Where(x => !x.Symbol.Value.Contains("_WI"));
this.symbols = selected.Select(x => x.Symbol);
this.rebalence_flag = false;
this.first_month_trade_flag = false;
this.trade_flag = true;
return this.symbols;
}else{
return this.symbols;
}
}
public override void OnData(Slice data)
{
}
}
}