Overall Statistics |
Total Trades 4845 Average Win 0.35% Average Loss -0.01% Compounding Annual Return 187.814% Drawdown 6.400% Expectancy 6.181 Net Profit 189.486% Sharpe Ratio 1.585 Loss Rate 83% Win Rate 17% Profit-Loss Ratio 42.21 Alpha 0.947 Beta 0.594 Annual Standard Deviation 0.644 Annual Variance 0.415 Information Ratio 1.398 Tracking Error 0.641 Treynor Ratio 1.719 Total Fees $4996.10 |
//Copyright Warren Harding 2016, granted to the public domain. //Use entirely at your own risk. //Custom algorithm development: warrencharding@yahoo.com. //Do not remove this copyright notice. using System; using System.Collections.Generic; using System.Collections.Concurrent; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.Slippage; using QuantConnect.Securities; namespace QuantConnect { public class Algo4 : QCAlgorithm { //You can adjust this first set to optimize. decimal lowerBandRatio = 0.8m; decimal upperBandRatio = 1m; decimal rsiBuyCutoff=50; decimal dollarVolumeCutoff = 1000000; decimal maximumTrade=25000; decimal minimumTrade = 500m; private const int takeCount = 10; private SecurityChanges securityChanges = SecurityChanges.None; int shortMaPeriod=1; int longMaPeriod=20; int rsiPeriod=3; //Your broker is going to hate you if you set these too low as it will result in large amounts of unfilled //order cancellations. It bogs down LEAN as well. int barsToHoldBuyOrdersFor=0; int barsToHoldSellOrdersFor=0; Resolution resolution = Resolution.Daily; decimal ratioOfDollarVolumeForMaxTrade; ConcurrentDictionary<Symbol, StockData> universeDictionary = new ConcurrentDictionary<Symbol, StockData>(); List<OrderTicketWrapper> buyOrders=new List<OrderTicketWrapper>(); List<OrderTicketWrapper> sellOrders=new List<OrderTicketWrapper>(); public override void Initialize() { SetStartDate(2015, 9, 30); SetEndDate(2016, 9, 30); SetCash(100000); AddUniverse(coarse => { return (from c in coarse let stockData = universeDictionary.GetOrAdd(c.Symbol, sym => new StockData(c.Symbol,shortMaPeriod,longMaPeriod,rsiPeriod)) where stockData.Update(c.EndTime, c.Price) where c.DollarVolume > dollarVolumeCutoff where stockData.RSI>rsiBuyCutoff where (1-stockData.MovingAverageDifferenceRatio) < lowerBandRatio orderby stockData.MovingAverageDifferenceRatio descending select c.Symbol).Take(takeCount); }); //Be careful adjusting this next one, too high of a setting will result in unrealistically large //purchases being made with no regards for slippage. if (resolution == Resolution.Daily) { ratioOfDollarVolumeForMaxTrade = .25m / 6.5m / 60m; } else if (resolution==Resolution.Minute) { ratioOfDollarVolumeForMaxTrade = .25m; } } public void OnData(TradeBars data) { Buy(data); Sell(data); } public void Buy(TradeBars data) { CancelExpiredOrders(buyOrders,barsToHoldBuyOrdersFor); int quantity = 0; decimal buyPrice; TradeBar bar; string ticker; foreach (var security in securityChanges.AddedSecurities) { ticker=security.Symbol.ToString(); if (!Portfolio[security.Symbol].HoldStock & data.ContainsKey(ticker)) { bar=data[ticker]; quantity=SizePosition(bar); if (quantity > 0) { buyPrice=bar.Close; OrderTicketWrapper orderTicketWrapper=new OrderTicketWrapper(); orderTicketWrapper.orderTicket = LimitOrder(bar.Symbol, quantity,buyPrice); orderTicketWrapper.price=buyPrice; buyOrders.Add(orderTicketWrapper); } } } foreach (OrderTicketWrapper orderTickerWrapper in buyOrders) { orderTickerWrapper.count++; } } public void Sell(TradeBars data) { CancelExpiredOrders(sellOrders,barsToHoldSellOrdersFor); decimal sellPrice; TradeBar bar; foreach (SecurityHolding stock in Portfolio.Values) { if (Portfolio[stock.Symbol].Quantity > 0 & data.ContainsKey(stock.Symbol)) { bar = data[stock.Symbol]; sellPrice = universeDictionary[stock.Symbol].LongEMA * upperBandRatio; if (bar.Close > sellPrice) { sellPrice = bar.Close; OrderTicketWrapper orderTicketWrapper = new OrderTicketWrapper(); orderTicketWrapper.orderTicket = LimitOrder(stock.Symbol, -Portfolio[stock.Symbol].Quantity, sellPrice); orderTicketWrapper.price=sellPrice; sellOrders.Add(orderTicketWrapper); } } } foreach (OrderTicketWrapper orderTickerWrapper in sellOrders) { orderTickerWrapper.count++; } } public override void OnSecuritiesChanged(SecurityChanges changes) { securityChanges = changes; } static void CancelExpiredOrders(List<OrderTicketWrapper> orderTickerWrappers,int barsToHoldOrdersFor) { foreach (OrderTicketWrapper orderTickerWrapper in orderTickerWrappers) { if (orderTickerWrapper.count>barsToHoldOrdersFor) { orderTickerWrapper.orderTicket.Cancel(); } } orderTickerWrappers.RemoveAll(x=>x.orderTicket.Status==OrderStatus.Filled | x.orderTicket.Status==OrderStatus.Canceled); } static decimal SumBuyOrders(List<OrderTicketWrapper> buyOrders) { decimal sum=0; foreach (OrderTicketWrapper orderTickerWrapper in buyOrders) { sum += orderTickerWrapper.orderTicket.Quantity * orderTickerWrapper.price; } return sum; } int SizePosition(TradeBar bar) { decimal maxTrade=bar.Close*bar.Volume*ratioOfDollarVolumeForMaxTrade; if (maxTrade>maximumTrade) { maxTrade=maximumTrade; } int quantity =(int)Math.Floor(Math.Min(Portfolio.Cash-SumBuyOrders(buyOrders), maxTrade) / bar.Close); quantity = RoundLot(quantity); if (quantity * bar.Close < minimumTrade) { return 0; } return quantity; } static int RoundLot(int inOddLotQuantity) { decimal inQuantity = (decimal)inOddLotQuantity; if (inQuantity > 2000000) { decimal small = inQuantity / 1000000; small = Math.Floor(small); return (int)(small * 1000000); } if (inQuantity > 200000) { decimal small = inQuantity / 100000; small = Math.Floor(small); return (int)(small * 100000); } if (inQuantity > 20000) { decimal small = inQuantity / 10000; small = Math.Floor(small); return (int)(small * 10000); } if (inQuantity > 2000) { decimal small = inQuantity / 1000; small = Math.Floor(small); return (int)(small * 1000); } if (inQuantity > 200) { decimal small = inQuantity / 100; small = Math.Floor(small); return (int)(small * 100); } if (inQuantity > 20) { decimal small = inQuantity / 10; small = Math.Floor(small); return (int)(small * 10); } return inOddLotQuantity; } class OrderTicketWrapper { public OrderTicket orderTicket; public int count=0; public decimal price; } class StockData { public StockData(string ticker,int shortMAPeriod,int longMAPeriod,int RsiPeriod) { Ticker=ticker; ShortEMA = new ExponentialMovingAverage(shortMAPeriod); LongEMA = new ExponentialMovingAverage(longMAPeriod); RSI = new RelativeStrengthIndex(RsiPeriod,MovingAverageType.Exponential); } public string Ticker; public ExponentialMovingAverage ShortEMA; public ExponentialMovingAverage LongEMA; public RelativeStrengthIndex RSI; public decimal MovingAverageDifferenceRatio { get { return (LongEMA - ShortEMA)/ShortEMA; } } public bool Update(DateTime time, decimal value) { return ShortEMA.Update(time, value) && LongEMA.Update(time, value) && RSI.Update(time, value); } } } }