| Overall Statistics |
|
Total Trades 30 Average Win 0.34% Average Loss -0.28% Compounding Annual Return 77.018% Drawdown 1.400% Expectancy 0.067 Net Profit 0.418% Sharpe Ratio 14.757 Probabilistic Sharpe Ratio 0% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.21 Alpha 0.242 Beta -1.634 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio 12.8 Tracking Error 0.076 Treynor Ratio -0.424 Total Fees $30.00 Estimated Strategy Capacity $0 Lowest Capacity Asset AMZN R735QTJ8XC9X |
/*
This program was developed by Quantify and is property of the client
Usage and marketing of this program is permitted.
Quantify Developer(s): Conor Flynn
Date Created:
Client:
Client ID:
If you find a bug or an inconsistantcy please contact your assigned developer.
Contact: cflynn@quantify-co.com
To request a new copy of your program please contact support:
Contact: support@quantify-co.com
Note: Client ID is required for client verification upon requesting a new copy
*/
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 = 2022;
private int _startMonth = 2;
private int _startDay = 26;
// 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 typehttps://www.quantconnect.com/project/10886062#optimization-view-tab
// determines whether securities are selected by QC's universe function
// or manually by the user
// manual = false; QC universe = true
private readonly bool _universeSelectionType = false;
// number of securities for the universe selection to select
private readonly int _stockCount = 5;
// use default values for universe
private readonly bool _useDefaultSymbolBase = false;
// 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.Tick, // data resolution
512, // 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
1, // 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;
// =================================================================================================================
// 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;
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);
// 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
return (from stock in coarse
where !stock.HasFundamentalData
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];
if(!sd.EmaH.IsReady || !sd.EmaL.IsReady)
return;
DataPoint dp = new DataPoint();
dp.High = sd.EmaH;
dp.Low = sd.EmaL;
dp.Bar = bar;
sd.Zzhl.Update(dp);
if(!sd.Zzhl.IsReady)
return;
if(sd.Direction != 1 && sd.Zzhl.Switch && sd.Zzhl.Direction) {
SetHoldings(sd.Symbol, 1);
sd.Direction = 1;
}
else if(sd.Direction != -1 && sd.Zzhl.Switch && !sd.Zzhl.Direction) {
SetHoldings(sd.Symbol, -1);
sd.Direction = -1;
}
Plot("ZZHL", "SWITCH", sd.Zzhl.Switch ? 1 : 0);
Plot("ZZHL", "DIRECTION", sd.Zzhl.Direction ? 2 : -2);
}
// 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;
// direction
public int Direction = 0;
// ZigZagHighLow
public ZigZagHighLow Zzhl;
// High EMA
public ExponentialMovingAverage EmaH;
// Low Ema
public ExponentialMovingAverage EmaL;
public SymbolData(ClientAlgorithm algorithm, Symbol symbol, SymbolBase symbolBase) {
Algorithm = algorithm;
Symbol = symbol;
SymbolBase = symbolBase;
Zzhl = new ZigZagHighLow(algorithm, 0.01m, 0.05m, 5, 2.0m);
EmaH = algorithm.EMA(symbol, 5, symbolBase.SymbolResolution, Field.High);
EmaL = algorithm.EMA(symbol, 5, symbolBase.SymbolResolution, Field.Low);
}
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);
}
}
}
}namespace QuantConnect.Indicators {
public class ZigZagHighLow {
private readonly QCAlgorithm _algorithm;
private readonly decimal _percentageReversal;
private readonly decimal _absoluteReversal;
private readonly AverageTrueRange _atr;
private readonly decimal _atrReversal;
private readonly decimal _tickSize;
private bool _switch = false;
private bool _direction = false;
private decimal _value = 0.0m;
// 0 = init, 1 = undefined, 2 = downtrend, 3 = uptrend
private int _state;
private decimal _maxPriceH;
private decimal _minPriceL;
private decimal _prevMaxH;
private decimal _prevMinL;
private bool _newMax;
private bool _newMin;
private bool _priorDownConditional = false;
private bool _priorUpConditional = false;
public bool Switch => _switch;
public bool Direction => _direction;
public decimal Value => _value;
public bool IsReady => _atr.IsReady;
public int WarmUpPeriod => _atr.WarmUpPeriod;
public ZigZagHighLow(decimal percentageReversal, decimal absoluteReversal, int atrLength,
decimal atrReversal, decimal tickSize = 0) {
_percentageReversal = percentageReversal;
_atr = new AverageTrueRange(atrLength);
_atrReversal = atrReversal;
if(tickSize != 0)
_absoluteReversal = absoluteReversal;
else
_absoluteReversal = absoluteReversal * tickSize;
_state = 0;
_maxPriceH = 0;
_minPriceL = 0;
_prevMaxH = 0;
_prevMinL = 0;
_newMax = true;
_newMin = true;
}
public ZigZagHighLow(QCAlgorithm algorithm, decimal percentageReversal, decimal absoluteReversal, int atrLength,
decimal atrReversal, decimal tickSize = 0) {
_algorithm = algorithm;
_percentageReversal = percentageReversal;
_atr = new AverageTrueRange(atrLength);
_atrReversal = atrReversal;
if(tickSize != 0)
_absoluteReversal = absoluteReversal;
else
_absoluteReversal = absoluteReversal * tickSize;
_state = 0;
_maxPriceH = 0;
_minPriceL = 0;
_prevMaxH = 0;
_prevMinL = 0;
_newMax = true;
_newMin = true;
}
public void Update(DataPoint data) {
_atr.Update(data.Bar);
if(!_atr.IsReady)
return;
decimal _hlPivot = (_percentageReversal / 100) + (_atr / data.Bar.Close * _atrReversal);
_algorithm.Debug($"{_algorithm.Time}-> piv={_hlPivot} atr={_atr} dl={data.Low} dh={data.High} bo={data.Bar.Open} bh={data.Bar.High} bl={data.Bar.Low} bc={data.Bar.Close}");
// initialize data points
if(_state == 0) {
_maxPriceH = data.High;
_minPriceL = data.Low;
_newMax = true;
_newMin = true;
_state = 1;
}
// if undefined
else if(_state == 1) {
// check for uptrend
if(data.High >= _prevMaxH) {
_state = 3;
_maxPriceH = data.High;
_minPriceL = _prevMinL;
_newMax = true;
_newMin = false;
}
else if(data.Low <= _prevMinL) {
_state = 2;
_maxPriceH = _prevMaxH;
_minPriceL = data.Low;
_newMax = false;
_newMin = true;
}
else {
_state = 1;
_maxPriceH = _prevMaxH;
_minPriceL = _prevMinL;
_newMax = false;
_newMin = false;
}
}
// if down trend
else if(_state == 2) {
if(data.High >= _prevMinL + (_prevMinL * _hlPivot) + _absoluteReversal) {
_state = 3;
_maxPriceH = data.High;
_minPriceL = _prevMinL;
_newMax = true;
_newMin = false;
}
else {
_state = 2;
_maxPriceH = _prevMaxH;
_newMax = false;
if(data.Low <= _prevMinL) {
_minPriceL = data.Low;
_newMin = true;
} else {
_minPriceL = _prevMinL;
_newMin = false;
}
}
}
// if up trend
else if(_state == 3) {
if(data.Low <= _prevMaxH - (_prevMaxH * _hlPivot) - _absoluteReversal) {
_state = 2;
_maxPriceH = _prevMaxH;
_minPriceL = data.Low;
_newMax = false;
_newMin = true;
}
else {
_state = 3;
if(data.High >= _prevMaxH) {
_maxPriceH = data.High;
_newMax = true;
} else {
_maxPriceH = _prevMaxH;
_newMax = false;
}
_minPriceL = _prevMinL;
_newMin = false;
}
}
_prevMaxH = _maxPriceH;
_prevMinL = _minPriceL;
// calculate switch
var lowPoint = _state == 2 && data.Low == _minPriceL;
var highPoint = _state == 3 && data.High == _maxPriceH;
var downConditional = lowPoint || (_state == 3 && data.High < _maxPriceH);
var upConditional = highPoint || (_state == 2 && data.Low > _minPriceL);
if(downConditional && _priorDownConditional) {
_switch = true;
_direction = false;
}
else if(upConditional && _priorUpConditional) {
_switch = true;
_direction = true;
}
else {
_switch = false;
}
_priorDownConditional = downConditional;
_priorUpConditional = upConditional;
}
}
public struct DataPoint {
public TradeBar Bar;
public decimal High;
public decimal Low;
}
}