Overall Statistics |
Total Trades 1435 Average Win 0.29% Average Loss -0.27% Compounding Annual Return 5.968% Drawdown 35.000% Expectancy 0.060 Net Profit 58.596% Sharpe Ratio 0.416 Loss Rate 49% Win Rate 51% Profit-Loss Ratio 1.07 Alpha 0.047 Beta 0.132 Annual Standard Deviation 0.142 Annual Variance 0.02 Information Ratio -0.135 Tracking Error 0.221 Treynor Ratio 0.447 Total Fees $1489.20 |
using System; using System.Collections.Generic; using System.Linq; using System.Net; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Indicators; namespace QuantConnect.Algorithm.CSharp { public class YEARMOMENTUMMODEL : QCAlgorithm { //Set this number anywhere betweet 0.001 and 0.01 to decide how strict the rebalancing difference has to be. double rebalancingStrictness = 0.005; //This is how far a position can go from it default allocation so 0.5 would mean that if the defauly is 2%, the position would be allocated anywhere between 1% and 3%.. double allocationSmoothingStrictness = 0.7; //Can leverage your risk double leverage = 1.5; //List<string> _symbols = new List<string>() { "BAL", "BNO", "CORN", "COW", "DIA", "EWG", "EWH", "EWJ", "EWT", "FXA", "FXB", "FXC", "FXE", "FXF", "FXI", "FXY", "GLD", "IWD", "JJC", "JJG", "JO", "PALL", "PPLT", "QQQQ", "SGG", "SLV", "SOYB", "SPY", "UGA", "UHN", "UNG", "USO", "UUP", "VGK", "VXX", "WEAT" }; //FXE causes QC to break down. //List of all tickers List<string> _symbols = new List<string>() { "BAL", "BNO", "CORN", "COW", "DIA", "EWG", "EWH", "EWJ", "EWT", "FXA", "FXB", "FXC", "FXF", "FXI", "FXY", "GLD", "IWD", "JJC", "JJG", "JO", "PALL", "PPLT", "QQQQ", "SLV", "SOYB", "SPY", "UGA", "UHN", "UNG", "USO", "UUP", "VGK", "VXX", "WEAT" }; //Dictionary of stocks and their stored values. Dictionary<string, StoredStock> stockData = new Dictionary<string, StoredStock>() { }; //Used to save the current date to ensure algorithm does not run twice/day. DateTime now; public bool firstRun = true; //Ensures algorithm only begins after this data. It should be the same as the date in SetStartDate DateTime start = new DateTime(2010, 1, 1); //Initialize the data and resolution you require for your strategy: public override void Initialize() { //Initialize //Set start and end date (YYYY,MM,DD) SetStartDate(2010, 1, 1); SetEndDate(2016, 7, 5); //Set the amount of cash to start with SetCash(25000); //Warm up period should be rather large to store old stock prices. SetWarmup(365); //Loads data list on first run InitializeStockData(); //Updates average true range UpdateATR(); } /// <summary> /// Main function. Executed every day/minute and runs the trading algorith. /// </summary> /// <param name="data"></param> public void OnData(TradeBars data) { //Print header for CSV file in Log FirstRunHeaderLogger(); //Only run code once/day by checking if this day already executed if (!stockData["SPY"].ATR.IsReady || now.Date == Time.Date) return; //Update prices and save them UpdateValues(data); //Make sure we are in test period if (Time.Date > start && Time.DayOfWeek == DayOfWeek.Friday) { //Sell all stocks who currently trade at a price lower than 250 days ago //SellTradingFunction(data); //Buy all stocks whos current price is above 250 days ago (Based on average true range allocation) SellBuyTradingFuction(data); } now = Time.Date; } /// <summary> /// Prints header to log file /// </summary> private void FirstRunHeaderLogger() { if (firstRun == true) { var header = string.Format ("{0},{1},{2},{3},{4},{5}", "Date", "Stock", "New Allocation", "ATR", "Old Price", "Current Price" ); Log(header); firstRun = false; } } /// <summary> /// Buys all stocks that currently trade at a higher price than 250 days back. Uses an atr based allocation. /// Higher ATR leads to a lower allcoation. /// Average allocation = 1/35 /// Highest and lowest is the half or 50% more than the average allocation. /// IMPORTANT: Need to add trade logic to prevent small transaction size rebalancing. /// </summary> /// <param name="data"></param> private void SellBuyTradingFuction(TradeBars data) { var universeToTrade = stockData.Where(f => f.Value.OldValues.Count > 10); List<decimal> ATRSCORES = universeToTrade.Select(x => x.Value.ATR.Current.Value).ToList(); if (universeToTrade.Count() != 0) { var allocation = ((decimal)1 / _symbols.Count())*(decimal)leverage; foreach (var stock in universeToTrade) { var smoothedAdjuster = 0.0m; try { var max = allocation * (decimal)allocationSmoothingStrictness; smoothedAdjuster = (max-(-max)) / ((ATRSCORES.Max() - ATRSCORES.Min())) * (stock.Value.ATR.Current.Value - ATRSCORES.Max()) + (max); } catch (Exception err99) { smoothedAdjuster = allocation / 2; } var percentage = allocation - smoothedAdjuster; if (stock.Value.CompareValue.price > data[stock.Key].Close) { percentage = percentage * -1; } if (stock.Value.lastallocation == 0 || Math.Abs(stock.Value.lastallocation - (double)percentage) > rebalancingStrictness) { stock.Value.lastallocation = (double)percentage; SetHoldings(stock.Key, percentage); LineLogger(data, stock, percentage); } } } } /// <summary> /// Prints a one line to the LOG everytime a trade is executed. /// </summary> /// <param name="data"></param> /// <param name="stock"></param> /// <param name="smoothedAdjuster"></param> private void LineLogger(TradeBars data, KeyValuePair<string, StoredStock> stock, decimal smoothedAdjuster) { var line = string.Format ("{0},{1},{2},{3},{4},{5}", Time.Date, stock.Key, smoothedAdjuster * 100 + "%", stock.Value.ATR.Current.Value, stock.Value.CompareValue.price, data[stock.Key].Close ); Log(line); } /// <summary> /// Updates the 14 day exponential average true range /// </summary> private void UpdateATR() { foreach (var stock in stockData) { var todayATR = ATR(stock.Key, 14, MovingAverageType.Exponential, Resolution.Daily); stock.Value.ATR = todayATR; } } /// <summary> /// Initializes all the data in the stockData dictionary /// </summary> private void InitializeStockData() { if (stockData.Count() == 0) { foreach (var stock in _symbols) { AddSecurity(SecurityType.Equity, stock, Resolution.Daily,true,(decimal)leverage + 1,false); var tobeadded = new StoredStock(); var ticker = stock; stockData.Add(ticker, tobeadded); } } } /// <summary> /// Updates the prices into the dictionary /// </summary> /// <param name="dataset"></param> public void UpdateValues(TradeBars dataset) { var date = Time.Date; foreach (var stock in stockData.ToList()) { if (dataset.ContainsKey(stock.Key)) { try { OldValue data = new OldValue(); data.date = date; data.price = Securities[stock.Key].Price; stock.Value.OldValues.Add(data); stock.Value.CompareValue.price = stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault().price; stock.Value.CompareValue.date = stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault().date; if (stock.Value.OldValues.Count() > 250) { stock.Value.OldValues.Remove(stock.Value.OldValues.OrderBy(x => x.date).FirstOrDefault()); } } catch (Exception err) { //What should happen here? }; } } } /// <summary> /// Class saving a sample of an old price /// </summary> class OldValue { public DateTime date; public decimal price; } /// <summary> /// The value of the stockData dictionary for each stock /// </summary> class StoredStock { public double lastallocation; public AverageTrueRange ATR; public OldValue CompareValue = new OldValue() { }; public List<OldValue> OldValues = new List<OldValue> { }; } } }