| Overall Statistics |
|
Total Trades 2269 Average Win 0.10% Average Loss -0.16% Compounding Annual Return 6.146% Drawdown 27.400% Expectancy 0.031 Net Profit 11.552% Sharpe Ratio 0.375 Probabilistic Sharpe Ratio 17.396% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 0.63 Alpha -0.048 Beta 0.656 Annual Standard Deviation 0.179 Annual Variance 0.032 Information Ratio -0.898 Tracking Error 0.121 Treynor Ratio 0.102 Total Fees $2301.46 |
/*
This program was developed by Quantify and is a template program.
Usage and marketing of this program is permitted.
www.quantify-co.com
*/
namespace QuantConnect.Algorithm.CSharp
{
public class BeginnerTemplate : QCAlgorithm
{
// BACKTESTING PARAMETERS
// =================================================================================================================
// general settings:
// set starting cash
private int starting_cash = 100000;
// backtesting start date time:
// date setting variables
private int start_year = 2019;
private int start_month = 1;
private int start_day = 1;
// 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 enable_end_date = false;
// date setting variables
private int end_year = 2020;
private int end_month = 1;
private int end_day = 1;
// universe settings:
// number of symbols you want to be observed by the universe at any given time
// updates based on the universe resolution set
// recommended universe resolution is daily
private int totalNumberOfStocks = 20;
// data update resolution
// changes how often the data updates and algorithm looks for entry
// determines how often the function OnData runs
// list of resolutions:
// Resolution.Tick; Resolution.Second; Resolution.Minute; Resolution.Hour; Resolution.Daily
private readonly Resolution resolution = Resolution.Hour;
// stock list
// list of stocks you want in the universe
private readonly String[] manual_universe = new String[]{"SPY", "AAPL"};
// use manual selection or automatic selection
// set to false for manual and true for automatic
private readonly bool selection_type = true;
// position settings:
// percent of portfolio to enter a position with
// note this value is 1 / totalNumberOfStocks
private decimal position_percent_portfolio = 0.05m;
// indicator settings:
// sets the period of the EMA fast indicator
private readonly int ema_fast_length = 10;
// sets the period of the EMA slow indicator
private readonly int ema_slow_length = 20;
// =================================================================================================================
// creates new universe variable setting
private List<StockData> universe = new List<StockData>();
// security changes variable
private SecurityChanges securityChanges = SecurityChanges.None;
public override void Initialize()
{
// set start date
SetStartDate(start_year, start_month, start_day);
// set end date
if(enable_end_date)
SetEndDate(end_year, end_month, end_day);
// set starting cash
SetCash(starting_cash);
if(selection_type) {
// universe updater w/ highest performing stocks
AddUniverse(CoarseFilterFunction, FineSelectionFunction);
} else {
foreach(string s in manual_universe)
AddEquity(s, resolution);
}
}
// filter based on CoarseFundamental
IEnumerable<Symbol> CoarseFilterFunction(IEnumerable<CoarseFundamental> coarse) {
// returns the highest DollarVolume stocks
// returns "totalNumberOfStocks" amount of stocks
return (from stock in coarse
orderby stock.DollarVolume descending
select stock.Symbol).Take(totalNumberOfStocks);
return Universe.Unchanged;
}
// filter based on FineFundamental
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine) {
return (from f in fine
select f.Symbol).Take(totalNumberOfStocks);
}
// 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) {
// loops through each stock in universe
foreach(StockData sd in universe) {
// condition where fast ema < slow ema
// makes sure position is not already open
if(sd.ema_fast < sd.ema_slow && !Portfolio[sd.ticker].Invested) {
SetHoldings(sd.ticker, position_percent_portfolio);
} else if(sd.ema_slow < sd.ema_fast && Portfolio[sd.ticker].Invested) {
// liquidates ticker if condition not met and position is open
Liquidate(sd.ticker);
}
}
}
// 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) {
List<StockData> stockDatas = universe.Where(x=>x.ticker == security.Symbol).ToList();
if (stockDatas.Count >= 1) {
// check to see if position is open and if so close position
if(Portfolio[stockDatas.First().ticker].Invested) {
// closes position
Liquidate(stockDatas.First().ticker);
}
// removes stock from list if it is removed from the universe
universe.Remove(stockDatas.First());
}
}
// add new securities to universe list
foreach(var security in securityChanges.AddedSecurities) {
// create StockData variable for security
StockData sd = new StockData();
// initalize all indicators
sd.ticker = security.Symbol;
sd.ema_fast = EMA(sd.ticker, ema_fast_length, resolution);
sd.ema_slow = EMA(sd.ticker, ema_slow_length, resolution);
// add historical data to tickers
// we use ema_slow_length since it will always be greater than ema_fast_length
var history = History(sd.ticker, ema_slow_length, resolution);
foreach(var bar in history) {
sd.ema_fast.Update(bar.EndTime, bar.Close);
sd.ema_slow.Update(bar.EndTime, bar.Close);
}
// add stockdata to universe
universe.Add(sd);
}
}
// default class containing all ticker information
public class StockData {
// stock ticker
public string ticker = "";
// defines EMA fast
public ExponentialMovingAverage ema_fast;
// defines EMA slow
public ExponentialMovingAverage ema_slow;
// DEFINE OTHER INDICATORS
}
}
}