in volatile market, any how choose two price levels and start rolling also can win, I guess Anton is not keen in marketing OpenQuant.
I have modified the code as follows, basically to adjust the range to account for slippage and also change the lot size to 1, 3, 7, 15, 21....
Code:
using System;
using System.Drawing;
using OpenQuant.API;
using OpenQuant.API.Indicators;
public class MyStrategy : Strategy
{
[Parameter("High")]
public double High;
[Parameter("Low")]
public double Low;
[Parameter("Close positions on strategy stop")]
public bool CloseOnStop;
private double slippageAdjust = 1;
private double Range = 10;
private double delta = 10;
private int ocaCount = 0;
private double High_dyn = 0;
private double Low_dyn = 0;
private int n = 1;
private double nQty = 0;
private int ntrade = 0;
private double tickSize = 0.01;
private double profitmultiply = 1;
private double filledprice = 0;
private int Qty = 1;
private int longshortdecimals = 1;
private int rangedecimals = 5;
private int qtydecimals = 1;
private int slippagedecimals = 1;
private int smadecimals = 30;
private double sma1length = 100;
private int longshort = 1;
private bool getDecimals = false;
Order order1;
Order order2;
Order limitOrder;
Order stopOrder;
private bool started;
public override void OnStrategyStart()
{
if (Instrument.TickSize != 0)
tickSize = Instrument.TickSize;
getDecimals = int.TryParse(Instrument.Description.Substring(0,1), out longshortdecimals);
if (getDecimals)
{
longshort = longshortdecimals; // 1 is long, 0 is short
}
getDecimals = int.TryParse(Instrument.Description.Substring(2,2), out rangedecimals);
if (getDecimals)
{
Range = tickSize*rangedecimals;
}
High_dyn = High;
Low_dyn = Low;
High_dyn = Math.Round(High_dyn/tickSize) * tickSize;
Low_dyn = Math.Round(Low_dyn/tickSize) * tickSize;
Range = High_dyn - Low_dyn;
Range = Math.Round(Range/tickSize) * tickSize;
delta = profitmultiply*Range;
delta = Math.Round(delta/tickSize) * tickSize;
getDecimals = int.TryParse(Instrument.Description.Substring(5,5), out qtydecimals);
if (getDecimals)
{
Qty = qtydecimals;
}
getDecimals = int.TryParse(Instrument.Description.Substring(11,1), out slippagedecimals);
if (getDecimals)
{
slippageAdjust = slippagedecimals;
}
getDecimals = int.TryParse(Instrument.Description.Substring(13,3), out smadecimals);
if (getDecimals)
{
sma1length = smadecimals;
}
started = false;
}
public override void OnStrategyStop()
{
if (CloseOnStop)
{
if (HasPosition)
{
if (Position.Side == PositionSide.Long)
MarketOrder(OrderSide.Sell, Position.Qty).Send();
else
MarketOrder(OrderSide.Buy, Position.Qty).Send();
}
}
}
public override void OnBar(Bar bar)
{
if (!HasPosition && !started)
{
started = true;
order1 = StopOrder(OrderSide.Buy, Qty, High_dyn);
order2 = StopOrder(OrderSide.Sell, Qty, Low_dyn);
ocaCount++;
string id = Clock.Now.Ticks.ToString();
order1.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
order2.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
order1.Send();
order2.Send();
}
}
public override void OnPositionChanged()
{
if (HasPosition)
{
ntrade++;
if (ntrade == 1)
{
if (order1.AvgPrice > order2.AvgPrice)
{
filledprice = order1.AvgPrice;
if (filledprice > High_dyn)
{
High_dyn = filledprice;
}
}
else
{
filledprice = order2.AvgPrice;
if (filledprice < Low_dyn)
{
Low_dyn = filledprice;
}
}
}
else
{
if (limitOrder.AvgPrice > stopOrder.AvgPrice)
{
filledprice = limitOrder.AvgPrice;
if (filledprice > High_dyn)
{
High_dyn = filledprice;
}
if (filledprice < Low_dyn)
{
Low_dyn = filledprice;
}
}
else
{
filledprice = stopOrder.AvgPrice;
if (filledprice > High_dyn)
{
High_dyn = filledprice;
}
if (filledprice < Low_dyn)
{
Low_dyn = filledprice;
}
}
}
High_dyn = Math.Round(High_dyn/tickSize) * tickSize;
Low_dyn = Math.Round(Low_dyn/tickSize) * tickSize;
Range = High_dyn - Low_dyn;
Range = Math.Round(Range/tickSize) * tickSize;
delta = profitmultiply*Range;
delta = Math.Round(delta/tickSize) * tickSize;
filledprice = Math.Round(filledprice/tickSize) * tickSize;
n++;
nQty = (Math.Pow(2, n)-1);
Console.WriteLine("{0} ntrade: {1} nQty: {2} filledprice = {3} High: {4} Low: {5} Range: {6} delta: {7} Qty: {8}", DateTime.Now, ntrade, nQty, filledprice, High_dyn, Low_dyn, Range, delta, Position.Qty);
if (Position.Side == PositionSide.Long)
{
ocaCount++;
limitOrder = LimitOrder(OrderSide.Sell, Position.Qty, High_dyn + delta);
stopOrder = StopOrder (OrderSide.Sell, Position.Qty + (Qty * nQty), Low_dyn);
string id = Clock.Now.Ticks.ToString();
limitOrder.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
stopOrder.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
limitOrder.Send();
stopOrder.Send();
}
else
{
ocaCount++;
stopOrder = StopOrder (OrderSide.Buy, Position.Qty + (Qty * nQty), High_dyn);
limitOrder = LimitOrder(OrderSide.Buy, Position.Qty, Low_dyn - delta);
string id = Clock.Now.Ticks.ToString();
limitOrder.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
stopOrder.OCAGroup = id + ": " + Instrument.Symbol + " " + ocaCount;
limitOrder.Send();
stopOrder.Send();
}
}
else
{
if (limitOrder.AvgPrice > stopOrder.AvgPrice)
filledprice = limitOrder.AvgPrice;
else
filledprice = stopOrder.AvgPrice;
filledprice = Math.Round(filledprice/tickSize) * tickSize;
Console.WriteLine("{0} ntrade: {1} nQty: {2} filledprice = {3} High: {4} Low: {5} Range: {6} delta: {7} STOP", DateTime.Now, ntrade, nQty, filledprice, High_dyn, Low_dyn, Range, delta, Position.Qty);
}
}
}