| 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);
}
}
}
}