| Overall Statistics |
|
Total Trades 3180 Average Win 0.16% Average Loss -0.14% Compounding Annual Return 446.467% Drawdown 4.300% Expectancy 0.774 Net Profit 446.467% Sharpe Ratio 9.527 Loss Rate 19% Win Rate 81% Profit-Loss Ratio 1.19 Alpha 1.399 Beta 0.025 Annual Standard Deviation 0.147 Annual Variance 0.022 Information Ratio 6.575 Tracking Error 0.207 Treynor Ratio 56.006 Total Fees $3180.00 |
//Copyright HardingSoftware.com 2019, granted to the public domain.
//Use at your own risk. Do not remove this copyright notice.
namespace QuantConnect.Algorithm.CSharp
{
public class Pair : QCAlgorithm
{
Symbol symbol = QuantConnect.Symbol.Create("MLAB", SecurityType.Equity, Market.USA);
decimal limitRatio = 0m;
int priceDecimals = 2;
int period = 60 * 60;
decimal valueWeightExponent = 1m;
decimal timeWeightExponent = 1m;
Resolution resolution = Resolution.Second;
TimeSpan orderExpiryTime = new TimeSpan(0,0,0,59);
List<TradeBar> history = new List<TradeBar>();
decimal startCash = 5000;
public override void Initialize()
{
SetStartDate(2018, 9, 6); //Set Start Date
SetEndDate(2019, 9, 6);
SetCash(startCash); //Set Strategy Cash
AddEquity(symbol, resolution);
}
/// 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)
{
CancelExpiredOrders();
if (data.Bars.ContainsKey(symbol) && data.Bars[symbol] != null)
{
history.Add(data.Bars[symbol]);
if (history.Count > period)
{
history.RemoveAt(0);
}
else
{
return;
}
List<decimal> lows = history.Select(x => x.Low).ToList();
decimal buyPrice = Reversal.CalculateBuyPrice(lows, timeWeightExponent, valueWeightExponent);
List<decimal> highs = history.Select(x => x.High).ToList();
decimal sellPrice = Reversal.CalculateSellPrice(highs, timeWeightExponent, valueWeightExponent);
decimal range = sellPrice - buyPrice;
if (Portfolio[symbol].Quantity == 0)
{
decimal price = buyPrice - range * limitRatio;
price = Math.Round(price, priceDecimals);
if (price > 0)
{
//decimal quantity = tradeQuantity;
//decimal quantity = Math.Floor((Portfolio.Cash - 2) / price);
decimal quantity = startCash / price;
if (OrderIsPlaced(symbol, quantity) == false)
{
Transactions.CancelOpenOrders();
LimitOrder(symbol, quantity, price);
}
}
}
else if (Portfolio[symbol].Quantity > 0)
{
decimal price = sellPrice + range * limitRatio;
price = Math.Round(price, priceDecimals);
if (price > 0)
{
decimal quantity = -Portfolio[symbol].Quantity;
if (OrderIsPlaced(symbol, quantity) == false)
{
Transactions.CancelOpenOrders();
LimitOrder(symbol, quantity, price);
}
}
}
}
}
public bool OrderIsPlaced(Symbol symbol, decimal quantity)
{
List<Order> orders = Transactions.GetOpenOrders(symbol);
foreach (Order order in orders)
{
if (order.Symbol == symbol)
{
if (Math.Sign(quantity) == Math.Sign(order.Quantity))
{
return true;
}
}
}
return false;
}
public void CancelExpiredOrders()
{
List<Order> orders = Transactions.GetOpenOrders();
foreach (Order order in orders)
{
if (Time > order.Time + orderExpiryTime)
{
Transactions.CancelOrder(order.Id);
}
}
}
public class Reversal
{
//Copyright HardingSoftware.com 2019, granted to the public domain.
//Use at your own risk. Do not remove this copyright notice.
public int Index;
public decimal Value;
public static decimal CalculateBuyPrice(List<decimal> lows, decimal timeWeightExponent, decimal valueWeightExponent)
{
List<Reversal> minima = Reversal.Minima(lows).OrderByDescending(x => x.Value).ToList();
decimal[] timeWeights = minima.Select(x => (decimal)Math.Pow((double)x.Index, (double)timeWeightExponent)).ToArray();
decimal[] valueWeights = ExponentialWeights(minima.Count, valueWeightExponent);
decimal[] totalWeights = timeWeights.Zip(valueWeights, (t, v) => t * v).ToArray();
decimal[] values = minima.Select(x => x.Value).ToArray();
return WeightedAverage(values, totalWeights);
}
public static decimal CalculateSellPrice(List<decimal> highs, decimal timeWeightExponent, decimal valueWeightExponent)
{
List<Reversal> maxima = Reversal.Maxima(highs).OrderBy(x => x.Value).ToList();
decimal[] timeWeights = maxima.Select(x => (decimal)Math.Pow((double)x.Index, (double)timeWeightExponent)).ToArray();
decimal[] valueWeights = ExponentialWeights(maxima.Count, 2);
decimal[] totalWeights = timeWeights.Zip(valueWeights, (t, v) => t * v).ToArray();
decimal[] values = maxima.Select(x => x.Value).ToArray();
return WeightedAverage(values, totalWeights);
}
public static List<Reversal> Maxima(List<decimal> values)
{
List<Reversal> reversals = new List<Reversal>();
for (int i = 1; i < values.Count - 1; i++)
{
if (values[i -1] < values[i])
{
if (values[i + 1] < values[i])
{
Reversal reversal = new Reversal();
reversal.Index = i;
reversal.Value = values[i];
reversals.Add(reversal);
}
}
}
if (values[0] > values[1])
{
Reversal reversal = new Reversal();
reversal.Index = 0;
reversal.Value = values[0];
reversals.Add(reversal);
}
if (values.Last() > values[values.Count - 2])
{
Reversal reversal = new Reversal();
reversal.Index = values.Count;
reversal.Value = values.Last();
reversals.Add(reversal);
}
return reversals;
}
public static List<Reversal> Minima(List<decimal> values)
{
List<Reversal> reversals = new List<Reversal>();
for (int i = 1; i < values.Count - 1; i++)
{
if (values[i - 1] > values[i])
{
if (values[i + 1] > values[i])
{
Reversal reversal = new Reversal();
reversal.Index = i;
reversal.Value = values[i];
reversals.Add(reversal);
}
}
}
if (values[0] < values[1])
{
Reversal reversal = new Reversal();
reversal.Index = 0;
reversal.Value = values[0];
reversals.Add(reversal);
}
if (values.Last() < values[values.Count - 2])
{
Reversal reversal = new Reversal();
reversal.Index = values.Count;
reversal.Value = values.Last();
reversals.Add(reversal);
}
return reversals;
}
public static decimal WeightedAverage(decimal[] values, decimal[] weights)
{
decimal d = weights.Sum();
if (d != 0)
{
return values.Zip(weights, (x, y) => x * y).Sum() / d;
}
else
{
return 0;
}
}
public static decimal[] TriangularWeightsDecimal(int length)
{
int[] intWeights = Enumerable.Range(1, length).ToArray();
return intWeights.Select(x => Convert.ToDecimal(x)).ToArray();
}
public static decimal TriangularMovingAverage(decimal[] values)
{
return WeightedAverage(values, TriangularWeightsDecimal(values.Length));
}
public static decimal[] ExponentialWeights(int length, decimal exponent)
{
List<decimal> weights = new List<decimal>();
double exponentDouble = (double)exponent;
for (int i=0;i<length;i++)
{
double w = (double)(i + 1);
weights.Add((decimal)Math.Pow(w, exponentDouble));
}
return weights.ToArray();
}
}
}
}