Skip to content

Backtest Strategies

  • Initialize a TimeDataSeries with historical data
  • Create and configure Strategy using StrategyConfigData
  • Create an Instrument class initializing lot and minimum tick size
  • Create a Strategy instance on which backtesting needs to be performed
  • Creates a backtesting engine executor TSExecutor and initialize it with above handles
  • Run the backtesting engine
  • Analyse the backtesting report
// Initialize 1 minute time data series from csv file. We had subtracted 1 minute while adding bar to time dataseries
// as in csv file bar time index is end of the bar and QXFinLib works on time index representing at start of a bar

int compressionSecond = 60;

TimeDataSeries timeDataSeries = new TimeDataSeries(
    BarCompression.MinuteBar,
    compressionSecond,
    BarType.CandleStick,
    new TimeSpan(9, 15, 0),  // Session Start time
    new TimeSpan(15, 30, 0)  // Session End time
);

string fileName = @"D:\\Data\\NIFTY-I_2020Futures.txt";

CSVOHLCLoader csvOHLCLoader = new CSVOHLCLoader(
    fileName,
    BarDataFileFormat.Symbol_Date_Time_O_H_L_C_V_O,
    DateFormat.Custom,
    DateSeparator.None,
    "yyyyMMdd HH:mm",
    true
);

foreach (IBarData barData in csvOHLCLoader)
{
    timeDataSeries.AddBarData(
        barData.DateTimeDT.AddSeconds(-1),
        barData.Open,
        barData.High,
        barData.Low,
        barData.Close,
        barData.Volume
    );
}

StrategyBase strategyBase = new EMAStrategy();

StrategyConfigData strategyConfigData = new StrategyConfigData();
strategyConfigData.InitialCapital = 500000;

strategyConfigData.Pyramiding = PyramidingType.Allow;
strategyConfigData.MaxPyramidingPosition = 1;

strategyConfigData.ContractMultiplier = 50;
strategyConfigData.StopLossTriggerPoint = StopLossTriggerPoint.High_Low_Bar;
strategyConfigData.OrderExecutionAt = OrderExecution.NextBar_Open;
strategyConfigData.CommissionType = CommissionType.Percentage;
strategyConfigData.FilledOrderCommission = .02;

InstrumentBase instrument = new EquityInstrument(
    ExchangeSegment.NSECM,
    111,
    "NIFTY-I",
    "NIFTY-I",
    "NIFTY-I",
    50,
    0.05
);

TSExecutor tsExecutor = new TSExecutor(
    strategyConfigData,
    instrument,
    timeDataSeries,
    strategyBase
);

strategyBase.SetFieldValue("FastLenthPeriod", 12);
strategyBase.SetFieldValue("SlowLenthPeriod", 15);

tsExecutor.Run();

// Print the backtesting result

ITradeStatistics tradeStatistics = tsExecutor.GetTradeStatistics();
List<CloseTradeDetail> closeTradeDetailList = tradeStatistics.GetAllCloseTradeDetails();
EquityDataSeries erquityDataSeries = tradeStatistics.GetEquityDataSeries();
List<TradeDetail> tradeList = tradeStatistics.GetAllTrades();

LogText(String.Format("============== TRADE LIST DATA =============="));
foreach (TradeDetail tradeDetail in tradeList)
{
    LogText(String.Format("{0}", tradeDetail.ToString()));
}

LogText(String.Format("\\n============== CLOSE TRADE DATA =============="));
int index = 1;

foreach (CloseTradeDetail closeTradeDetail in closeTradeDetailList)
{
    string test = closeTradeDetail.GetReport();
    LogText(index.ToString() + ".  " + closeTradeDetail.ToString());
    index++;
}

LogText(String.Format("\\n============== EQUITY CURVE DATA =============="));
foreach (EquityData equityData in erquityDataSeries)
{
    LogText(String.Format("{0, -50}{1,-100}", equityData.EquityPrice, equityData.SignalName));
}

LogText(String.Format("\\n============== STATISTICS =============="));

foreach (KeyValuePair<string, double> statsEntry in statistics)
{
    LogText(String.Format("{0, -50}{1,-100}", statsEntry.Key, statsEntry.Value));
}

Time Data Series

How to provide an additional TimeDataSeries within Strategy usage?

The function AddTimeDataSeries is used in your strategy initialization method to get a handle of custom TimeDataSeries.
This method must be implemented by your program through an interface ITimeDataSeriesAccessor.

Assign your implementation class to your strategy using TimeDataSeriesAccessor

// this returns highest value of close since the second most recent occurrence of the high being above 50

public TimeDataSeries AddTimeDataSeries(
    ExchangeSegment exchangeSegment,
    string scripIdentifier,
    DateTime fromDateTime,
    int compressionMultiplier = 1
)

// Implement your Fill TimeDataSeries bars through CSV or DB source here

public class TimeDataSeriesAccessorImpl : ITimeDataSeriesAccessor
{
    public TimeDataSeries GetTimeDataSeries(
        ExchangeSegment exchangeSegment,
        string instrumentIdentifer,
        DateTime fromDateTime,
        int compressionMultiplier
    )
    {
        TimeDataSeries timeDataSeries = new TimeDataSeries(
            BarCompression.MinuteBar,
            60 * compressionMultiplier,
            BarType.CandleStick,
            new TimeSpan(9, 15, 0),  // Session Start time
            new TimeSpan(15, 30, 0)  // Session End time
        );

        // Write code here to fill TimeDataSeries from CSV or DB

        return timeDataSeries;
    }
}

// Backtesting code

ITimeDataSeriesAccessor timeDataSeriesAccessorImpl = new TimeDataSeriesAccessorImpl();
strategyBase.TimeDataSeriesAccessor = timeDataSeriesAccessorImpl;

TSExecutor tsExecutor = new TSExecutor(
    strategyConfigData,
    instrument,
    timeDataSeries,
    strategyBase
);

tsExecutor.Run();