Hi, I thought that you don't need additional samples after our corrections/discussions of the strategy above. Seems I was wrong.
Here is the "Unilateral Pairs Trading" strategy that shows how to use OnBarSlice techique and to implement a spread trading strategy. Note that it will work only in the next version because it requires tiny changes in the source code.
Code:
using System;
using System.Drawing;
using OpenQuant.API;
using OpenQuant.API.Indicators;
public class MyStrategy : Strategy
{
// ratio series
// Each day, the ratio is calculated and added to this series
private TimeSeries ratioSeries;
// sma of ratio series
// Then we take a simple moving average of the daily ratios
private SMA smaRatio;
// diff between ratio series and sma of ratio series
// Then we calculate the daily volatility of the ratio against the
// simple moving average of itself, to see how far off normal it is.
// Each day, the diff = ratio – moving average is put into this seies
private TimeSeries diffSeries;
// sma of diff between ratio series and sma of ratio series
// Next we calculate a moving average of the differences
private SMA smaDiff;
// standard deviation of diff between ratio series and sma of ratio series
// Finally we calculate a standard deviation for each daily diff value.
// This value says how many standard deviations the current diff value is
// from the normal mean (from the sma of the diff). If this number gets
// to be too big, then the strategy enters a trade.
private SMD smdDiff;
// Number of contracts to order
[Parameter("Order quantity (number of contracts to order)")]
public double Qty = 100;
// Upper and lower bounds of Standard Deviation
// Strategy opens a position when current ratio value is more
// than 1.5 standard deviations from the mean.
[Parameter("Entry StdDev Upper Bound")]
public double EntryUpperStdDev = 1.5;
[Parameter("Entry StdDev Lower Bound")]
public double EntryLowerStdDev = -1.5;
// Strategy closes a position when ratio value returns to
// being within 0.5 standard deviations from the mean
[Parameter("Exit StdDev Upper Bound")]
public double ExitUpperStdDev = 0.5;
[Parameter("Exit StdDev Lowe Bound")]
public double ExitLowerStdDev = -0.5;
// Upper and lower bounds of Percent
private double upPercent = 2;
private double downPercent = 2;
private double mainLast = -1;
private double secondLast = -1;
[Parameter("Main Symbol")]
public string MainSymbol = "QQQQ";
[Parameter("Second Symbol")]
public string SecondSymbol = "SPY";
private Instrument mainInstrument; // reference the QQQQs
private Instrument secondInstrument; // trade the SPYs
private double mainPrev;
// The initialization routine creates various objects and attaches
// them to the class variables. It also specifies what lines to
// draw on the QD bar chart.
public override void OnStrategyStart ()
{
if (Instrument.Symbol == MainSymbol)
{
// create series objects and moving average objects
ratioSeries = new TimeSeries ("Ratio");
diffSeries = new TimeSeries ();
smaRatio = new SMA (ratioSeries, 20);
smaDiff = new SMA (diffSeries, 20);
smdDiff = new SMD (diffSeries, 20);
// get references to SPY and QQQQ from instrument manager
mainInstrument = InstrumentManager.Instruments[MainSymbol];
secondInstrument = InstrumentManager.Instruments[SecondSymbol];
// specify drawing colors and barchart pad locations
smaDiff.Color = Color.Yellow;
smdDiff.Color = Color.Yellow;
smaRatio.Color = Color.Pink;
ratioSeries.Color = Color.Yellow;
Draw (ratioSeries, 2);
Draw (smaRatio, 2);
Draw (smdDiff, 3);
}
}
public override void OnBarSlice (long barSize)
{
if (Instrument == mainInstrument)
{
// wait for first bars for each instrument
if (mainInstrument.Bar == null || secondInstrument.Bar == null)
return;
mainPrev = mainLast; // yesterday’s SPY closing value
mainLast = mainInstrument.Bar.Close; // today’s SPY closing value
secondLast = secondInstrument.Bar.Close;
DateTime date = mainInstrument.Bar.DateTime;
// if we have ratio values to work with
if (mainLast != -1) {
// calculate today’s ratio and add the ratio to the ratio series
double ratio = mainLast / secondLast;
ratioSeries.Add (date, ratio);
// if there are values in the moving average series,
// calculate diff between today’s ratio and its moving average,
// then add the difference value to the diff series
if (smaRatio.Count > 0)
diffSeries.Add (date, ratio - smaRatio[date]);
}
// if we have no moving average of ratio-ratioSMA diff values,
// we can’t do anything yet, so return
if (smaDiff.Count == 0)
return;
// if we reach this point, we have a moving average of ratio-ratioSMA
// to work with. This moving average lets us calculate std deviations.
// compare todays diff (diffseries) with the SMA of diffs (smaDiff)
// and divide by the standard deviation (smdDiff) denominator
// Now we can tell if the current ratio is way off normal.
double stdDev = (diffSeries[date] - smaDiff[date]) / smdDiff[date];
// if we have no existing position, see if you can enter a trade
if (Portfolio.Positions[mainInstrument.Symbol] == null) {
// if current ratio-smaDiff is more than 1.5 standard deviations
// from the mean, open a position (either long or short)
if (stdDev > EntryUpperStdDev
&& (mainLast - mainPrev) / mainPrev >= upPercent / 100)
SendOrder (OrderSide.Sell);
if (stdDev < EntryLowerStdDev
&& (mainLast - mainPrev) / mainPrev <= -downPercent / 100)
SendOrder (OrderSide.Buy);
}
// else if we already have an open position, try to close it if
// current ratio-smaDiff value has returned inside 0.5 standard
// deviations from the mean
else {
if (Portfolio.Positions[mainInstrument.Symbol].Side ==
PositionSide.Short) {
// if you are short, buy to close the position
if (stdDev < ExitUpperStdDev)
SendOrder (OrderSide.Buy);
}
else {
if (stdDev > ExitLowerStdDev)
SendOrder (OrderSide.Sell);
}
}
}
}
// this is a helper method to open or close a position, using
// a market order
private void SendOrder (OrderSide side) {
Order order = MarketOrder(mainInstrument, side, Qty);
order.Send ();
}
}
Regards,
Sergey.