| Overall Statistics |
|
Total Trades 7003 Average Win 0.12% Average Loss -0.07% Compounding Annual Return 20.518% Drawdown 7.600% Expectancy 0.073 Net Profit 45.915% Sharpe Ratio 1.395 Probabilistic Sharpe Ratio 70.241% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.86 Alpha 0.144 Beta -0.002 Annual Standard Deviation 0.103 Annual Variance 0.011 Information Ratio -0.364 Tracking Error 0.211 Treynor Ratio -60.336 Total Fees $7395.24 Estimated Strategy Capacity $22000000.00 Lowest Capacity Asset CHV R735QTJ8XC9X |
namespace QuantConnect.Algorithm.CSharp
{
public class ClientAlgorithm : QCAlgorithm
{
// BACKTESTING PARAMETERS
// =================================================================================================================
// general settings:
// set starting cash
private int _startingCash = 100000;
// backtesting start date time:
// date setting variables
private int _startYear = 2020;
private int _startMonth = 3;
private int _startDay = 15;
// backtesting end date time:
// determines whether there is a specified end date
// if false it will go to the current date (if 'true' it will go to the specified date)
private bool _enableEndDate = false;
// date setting variables
private int _endYear = 2022;
private int _endMonth = 1;
private int _endDay = 4;
// universe settings:
// universe selection type
// determines whether securities are selected by QC's universe function
// or manually by the user
// manual = false; QC universe = true
private readonly bool _universeSelectionType = true;
// number of securities for the universe selection to select
private readonly int _stockCount = 25;
// use default values for universe
private readonly bool _useDefaultSymbolBase = true;
// stock list for equities
// list of equities you want in the universe
// used in manual selection of universe
// set selectionType = false for activation
private readonly SymbolBase[] _universeList = new SymbolBase[]{
new SymbolBase(
"AMZN", // symbol ticker
Resolution.Minute, // data resolution
30, // data resolution consolidation rate
1.0m // portfolio allocation percentage
),
};
// default symbol base settings
// used if using QC universe selection or a base is not detected for
// an added security
private readonly SymbolBase _defaultSymbolBase = new SymbolBase(
"<BASE>", // do not modify
// parameters to modify:
Resolution.Minute, // data resolution
30, // data resolution consolidation rate
1.0m // portfolio allocation percentage
);
// position settings:
// percent of portfolio to use for trading
// must be below 1
private readonly decimal _portfolioAllocation = 1m;
// indicator settings:
// conditional factors
private decimal Factor = 5m;
public int ResistanceDivisor = 3;
// indicator lengths
public static int AtrLength = 12;
public static int AdxLength = 12;
public static int MedianLength = 3;
public static int EmaLength = 20;
public static decimal EmaDivisor = 3;
// =================================================================================================================
// contains all SymbolBase definitions
private Dictionary<string, SymbolBase> _symbolBases = new Dictionary<string, SymbolBase>();
// creates new universe variable setting
private Dictionary<Symbol, SymbolData> _universe = new Dictionary<Symbol, SymbolData>();
// security changes variable
private SecurityChanges _securityChanges = SecurityChanges.None;
// define offset universe to avoid first candle
private bool offsetUniverse = true;
private int _offset = 50;
private int _offsetCounter = 0;
public override void Initialize()
{
// set start date
SetStartDate(_startYear, _startMonth, _startDay);
// set end date
if(_enableEndDate)
SetEndDate(_endYear, _endMonth, _endDay);
// set starting cash
SetCash(_startingCash);
// add default symbol base
_symbolBases.Add("<BASE>", _defaultSymbolBase);
Factor = Convert.ToDecimal(GetParameter("Factor"));
AtrLength = Int32.Parse(GetParameter("AtrLength"));
EmaDivisor = Convert.ToDecimal(GetParameter("EmaDivisor"));
// initialize universe
if(!_universeSelectionType) {
foreach(SymbolBase sb in _universeList) {
_symbolBases.Add(sb.Symbol, sb);
AddEquity(sb.Symbol, sb.SymbolResolution);
}
} else {
UniverseSettings.Resolution = _defaultSymbolBase.SymbolResolution;
AddUniverse(CoarseFilterFunction, FineFilterFunction);
}
}
// filter based on CoarseFundamental
public IEnumerable<Symbol> CoarseFilterFunction(IEnumerable<CoarseFundamental> coarse) {
// returns the highest DollarVolume stocks
// returns "totalNumberOfStocks" amount of stocks
if(_offsetCounter++ % _offset == 0) {
return (from stock in coarse
orderby stock.DollarVolume descending
select stock.Symbol).Take(_stockCount);
}
return Universe.Unchanged;
}
// filters out all symbols not contained in the NASDAQ exchange
public IEnumerable<Symbol> FineFilterFunction(IEnumerable<FineFundamental> fine) {
return (from stock in fine
select stock.Symbol).Take(_stockCount);
}
public void OnDataConsolidated(object sender, TradeBar bar) {
if(!_universe.ContainsKey(bar.Symbol))
return;
SymbolData sd = _universe[bar.Symbol];
sd.Close.Add(bar.Close);
sd.Atr.Update(bar);
sd.Adx.Update(bar);
if(!sd.IsReady) {
sd.Trend.Add(0);
sd.TrendUp.Add(bar.Close);
sd.TrendDown.Add(bar.Close);
return;
}
int medianLength = 3;
ArrayList arr = new ArrayList(medianLength);
for(int i = 0; i < medianLength; i++)
arr.Add(sd.Close[i]);
arr.Sort();
decimal median = (decimal)arr[(int)(medianLength / 2)];
decimal hl2 = (bar.High + bar.Low) / 2;
decimal up = hl2 - (Factor * sd.Atr);
decimal down = hl2 + (Factor * sd.Atr);
decimal upm = median - up;
decimal downm = median - down;
sd.UpEma.Update(Time, upm);
sd.DownEma.Update(Time, downm);
if(!sd.UpEma.IsReady || !sd.DownEma.IsReady)
return;
sd.TrendUp.Add(sd.Close[1] > sd.TrendUp[1] ? Math.Max(up, sd.TrendUp[1]) : up);
sd.TrendDown.Add(sd.Close[1] < sd.TrendDown[1] ? Math.Min(down, sd.TrendDown[1]) : down);
sd.Trend.Add(sd.Close[0] > sd.TrendDown[1] ? 1 : sd.Close[0] < sd.TrendUp[1] ? -1 : sd.Trend[1]);
//buySignal = Trend == 1 and Trend[1] == Trend and Trend[2] != Trend
//sellSignal= Trend == -1 and Trend[1] == Trend and Trend[2] != Trend
bool buySignal = sd.Trend[0] == 1 && sd.Trend[1] == sd.Trend[0] && sd.Trend[2] != sd.Trend[0];
bool sellSignal = sd.Trend[0] == -1 && sd.Trend[1] == sd.Trend[0] && sd.Trend[2] != sd.Trend[0];
//bool buySignal = upm < sd.UpEma / EmaDivisor;
//bool sellSignal = downm < sd.DownEma / EmaDivisor;
if(sd.Direction != 1 && buySignal) {
SetHoldings(sd.Symbol, 1m / _stockCount);
sd.Direction = 1;
}
else if(sd.Direction != -1 && sellSignal) {
SetHoldings(sd.Symbol, -1m / _stockCount);
sd.Direction = -1;
}
}
// OnSecuritiesChanged runs when the universe updates current securities
public override void OnSecuritiesChanged(SecurityChanges changes) {
_securityChanges = changes;
// remove stocks from list that get removed from universe
foreach (var security in _securityChanges.RemovedSecurities) {
if(Securities[security.Symbol].Invested) {
Log($"{Time}->Portfolio: Liquidated security {security.Symbol} on universe exit");
Liquidate(security.Symbol);
}
_universe.Remove(security.Symbol);
Log($"{Time}->Universe: Removed security {security.Symbol} from universe");
}
// add new securities to universe list
foreach(var security in _securityChanges.AddedSecurities) {
// creare new SymbolData object
SymbolData sd;
// if no base exists for symbol use default
if(!_symbolBases.ContainsKey(security.Symbol) || _useDefaultSymbolBase)
sd = new SymbolData(this, security.Symbol, _symbolBases["<BASE>"]);
// otherwise use defined base
else
sd = new SymbolData(this, security.Symbol, _symbolBases[security.Symbol]);
// initialize consolidator and store if needed
TickConsolidator tickConsolidator = null;
TradeBarConsolidator barConsolidator = null;
if(sd.SymbolBase.SymbolResolution == Resolution.Tick) {
var consolidator = sd.GetTickConsolidator();
if(consolidator != null) {
consolidator.DataConsolidated += OnDataConsolidated;
SubscriptionManager.AddConsolidator(sd.Symbol, consolidator);
tickConsolidator = consolidator;
}
} else {
var consolidator = sd.GetConsolidator();
if(consolidator != null) {
consolidator.DataConsolidated += OnDataConsolidated;
SubscriptionManager.AddConsolidator(sd.Symbol, consolidator);
barConsolidator = consolidator;
}
}
// add SymbolData to universe
_universe.Add(security.Symbol, sd);
Log($"{Time}->Universe: Added security {security.Symbol} to universe");
}
}
public class SymbolBase {
public readonly string Symbol;
public readonly Resolution SymbolResolution;
public readonly int SymbolConsolidationRate;
public readonly decimal PortfolioAllocation;
public SymbolBase(string symbol = "<BASE>", Resolution symbolResolution = Resolution.Minute, int symbolConsolidationRate = 1, decimal portfolioAllocation = 1.0m) {
Symbol = symbol;
SymbolResolution = symbolResolution;
SymbolConsolidationRate = symbolConsolidationRate;
PortfolioAllocation = portfolioAllocation;
}
}
// default class containing all symbol information
public class SymbolData {
// Variables:
// algorithm
public readonly ClientAlgorithm Algorithm;
// symbol
public readonly string Symbol;
// symbol base
public readonly SymbolBase SymbolBase;
public readonly RollingWindow<decimal> Close;
public readonly RollingWindow<decimal> Trend;
public readonly RollingWindow<decimal> TrendUp;
public readonly RollingWindow<decimal> TrendDown;
public AverageTrueRange Atr;
public AverageDirectionalIndex Adx;
public ExponentialMovingAverage UpEma;
public ExponentialMovingAverage DownEma;
public int Direction = 0;
public bool IsReady => /*UpEma.IsReady && DownEma.IsReady &&*/ Adx.IsReady && Atr.IsReady;
private readonly static int _length = 3;
public SymbolData(ClientAlgorithm algorithm, Symbol symbol, SymbolBase symbolBase) {
Algorithm = algorithm;
Symbol = symbol;
SymbolBase = symbolBase;
Close = new RollingWindow<decimal>(_length);
Trend = new RollingWindow<decimal>(_length);
TrendUp = new RollingWindow<decimal>(_length);
TrendDown = new RollingWindow<decimal>(_length);
Atr = new AverageTrueRange(ClientAlgorithm.AtrLength);
Adx = new AverageDirectionalIndex(ClientAlgorithm.AdxLength);
UpEma = new ExponentialMovingAverage(ClientAlgorithm.EmaLength);
DownEma = new ExponentialMovingAverage(ClientAlgorithm.EmaLength);
}
public TradeBarConsolidator GetConsolidator() {
TimeSpan timeSpan;
switch(SymbolBase.SymbolResolution) {
case Resolution.Second:
timeSpan = TimeSpan.FromSeconds(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Minute:
timeSpan = TimeSpan.FromMinutes(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Hour:
timeSpan = TimeSpan.FromHours(SymbolBase.SymbolConsolidationRate);
break;
case Resolution.Daily:
timeSpan = TimeSpan.FromDays(SymbolBase.SymbolConsolidationRate);
break;
default:
return null;
}
return new TradeBarConsolidator(timeSpan);
}
public TickConsolidator GetTickConsolidator() {
return new TickConsolidator(SymbolBase.SymbolConsolidationRate);
}
}
}
}