Strategy Development


This section provides an overview of using BlitzTrader API to develop proprietary trading strategies and quant developers learn about on how plug-in architecture allows easy extensibility of system to develop and integrate powerful trading ideas into BlitzTrader system.

The framework allowed custom strategy to have access of following outlined features.

  • Define any number of markets across exchange adapters
  • User defined parameter to be changed on fly
  • Output parameter to update some important state of the strategy to trading console
  • Get access of real-time market, reference, news data and order event
  • Parameterized custom command to interact with system from trading console
  • Initialize, Start and Stop of Strategy control
  • Access of historical market data for technical Indicator evaluation
  • APIs for trade statistics and order routing
  • Options pricing library to be used to build Strategy using IV and Greeks.
  • Smart order command for easy management of order handling and routing
  • Custom user interface framework for strategy portfolio creation and parameter change
  • The quant developers wishing to create custom strategy application take advantage of services provided by framework

    Requirements

    Before you start developing your custom proprietary trading strategy, you need following prerequisites:
  • Latest BlitzTrader Framework components files (QX.Blitz.Core.dll, QX.Base.Common.dll, QX.Base.Core.dll, QX.Base.Data.dll, QX.Base.Financial.dll)
  • A C# 4.0-5.0 development IDE (Microsoft Visual Studio, SharpDevelop or any other IDE).
  • Step-By-Step Tutorial

    Start Visual Studio, start a new project and choose Class Library as a type of a project.


    In this tutorial the example strategy we're developing an example strategy called SpreadScalping. The first thing you need to do now is to add a reference to BlitzTrader framework components: so choose Add references...


    Select the Browse tab, then navigate to the BlitzTrader installation folder {BlitzTraderInstallationFolder}\SDK and choose the framework DLL files QX.Blitz.Core.dll, QX.Base.Common.dll, QX.Base.Core.dll, QX.Base.Data.dll, QX.Base.Financial.dll from SDK folder. This will allow you to use the classes/interfaces defined by BlitzTrader API.

    Keep it noted that all framework dll references should have Copy Local attribute to be false.

    After you added the reference, the following namespaces should be available and added to your strategy code.

    As a minimum add the following using statements to your strategy implementation source files:

     
    using QX.Base.Common;
    using QX.Base.Core;
    using QX.Blitz.Core;
    using QX.Base.Common.Message;
    using QX.Base.Common.InstrumentInfo;
    

    Then we rename the default Class1.cs to SpreadScalping.cs and implement your strategy according to following example code:

     
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Collections.Generic;
    
    using QX.Base.Common;
    using QX.Base.Common.InstrumentInfo;
    using QX.Base.Common.Message;
    using QX.Base.Core;
    using QX.Blitz.Core;
    using QX.Common.Lib;
    
    namespace QX.Blitz.Strategy.ATExecution
    {
    
    
    namespace QX.Blitz.Strategy.SpreadScalpingX2
    {
        [StrategyAttribute("{7CA0AE08-3363-4BCF-92D2-0FE2A59A06A9}",
                            "Spread-Scalping-X2",
                            "Spread Scalping Strategy",
                            "Quantum Hedge Fund")]
        class SpreadScalping : StrategyBase
        {
            [StrategyParameterAttribute("BenchMarkSpread", DefaultValue = 5,  
                Description = "Benchmark Spread", 
                CategoryName = "Standard", 
                DisplayName = "Benchmark Spread")]
            private double BenchMarkSpread = 5;
    
            [StrategyParameterAttribute("OrderLotSize", 
                DefaultValue = 1,  
                Description = "Order Quantity", 
                CategoryName = "Standard", 
                DisplayName = "Order Size in Lots")]
            private int OrderQty = 5;
    
            [StrategyParameterAttribute("Rounds", 
                DefaultValue = 1,  
                Description = "Total number of Rounds to perform", 
                CategoryName = "Standard", 
                DisplayName = "Rounds")]
            private int Rounds = 5;
    
            [StrategyPublishParameter("CompletedRounds", 
                DefaultValue = 0, 
                Description = "Published By Server : Total Completed Rounds.")]
            private int CompletedRounds = 0;
    
            private IVObject _ivObject = new IVObject("IV", 
                                                      "Spread Scalping Instrument", 
                                                      true,
                                                      InstrumentType.Equity | InstrumentType.Futures | InstrumentType.Options, 
                                                      MarketDataType.All,
                                                      OrderEventType.All);
    
            private IVInfo _ivInfo = null;
    
            private IOrderCommand _entryOrderCommand = null;
            private IOrderCommand _exitOrderCommand = null;
    
            #region Override Methods
    
            protected override void OnInitialize()
            {
                _ivInfo = base.GetIVInfo(_ivObject);
                _entryOrderCommand = base.GetSmartOrderCommand("Entry", TimeInForce.GFD, _ivObject);
                _exitOrderCommand = base.GetSmartOrderCommand("Exit", TimeInForce.GFD, _ivObject);
    
                if (_entryOrderCommand == null ||
                    _exitOrderCommand == null)
                    throw new Exception("Strategy Initialization Failed. Reason : Smart Order is not Initialized.");
    
                TraceLogInfo("Strategy Initialize Successfully");
            }
    
            protected override bool OnStart(out string errorString)
            {
                errorString = string.Empty;
                return true;
            }
    
            protected override void OnStopping()
            {
                string errorString = string.Empty;
                if (!base.CancelAllOpenOrders(5, out errorString))
                {
                    TraceLogWarning("Order cancellation failed. Reason : " + errorString);
                }
    
                TraceLogInfo("Strategy Stopped.");
            }
    
            public override IVObject[] IVObjects
            {
                get
                {
                    List _ivObjectArray = new List();
                    _ivObjectArray.Add(_ivObject);
                    return _ivObjectArray.ToArray();
                }
            }
    
            protected override int MarketDataDelayNotifyTimeInSeconds
            {
                get { return 10; }
            }
    
            protected override bool ValidateStrategyParameter(string parameterName, object paramterValue, out string errorString)
            {
                errorString = string.Empty;
                bool retValue = false;
    
                switch (parameterName)
                {
                    case "BenchMarkSpread" :
                        retValue = paramterValue is double;
                        if (retValue == false)
                            errorString = parameterName + " value is invalid";
                        break;
                    case "OrderLotSize" :
                        retValue = paramterValue is int;
                        if (retValue == false)
                            errorString = parameterName + " value is invalid";
                        break;
                    case "Rounds" :
                        retValue = paramterValue is int;
                        if (retValue == false)
                            errorString = parameterName + " value is invalid";
                        break;
    
                    default:
                        retValue = base.ValidateStrategyParameter(parameterName, paramterValue, out errorString);
                        break;
                }
    
                return retValue;
            }
    
            protected override void OnMarketDataEvent(StrategyMarketDataEventArgs strategyMarketDataEventArgs)
            {
                try
                {
                    if (StrategyRunningMode == StrategyMode.Stopped)
                        return;
    
                    if (!OrderRoutingEnabled)
                        return;
    
                    if (base.IsStrategyStopInitiated)
                        return;
    
                    if (CompletedRounds >= Rounds)
                        return;
    
                    if (_entryOrderCommand == null ||
                       _exitOrderCommand == null)
                        return;
    
                    if (strategyMarketDataEventArgs.MarketDataContainerInfo.TouchLineInfo.BestBidPrice <= 0 ||
                        strategyMarketDataEventArgs.MarketDataContainerInfo.TouchLineInfo.BestAskPrice <= 0)
                        return;
    
                    OrderSide entrySide = GetSpreadScalpingeEntrySide(strategyMarketDataEventArgs.MarketDataContainerInfo);
    
                    double entryOrderPrice = 0;
                    if (_entryOrderCommand.CurrentOrder == null)
                    {
                        // Current order null indicates that no open order is active
                        // retrieve the best price in the entry side of the order book
                        entryOrderPrice = base.GetBestPrice(_ivInfo.MarketDataContainer, entrySide);
                    }
                    else
                    {
                        // retrieve the best price in the entry side of the order book, keeping self order state
                        entryOrderPrice = base.GetBestPrice(_ivInfo.MarketDataContainer, _entryOrderCommand.CurrentOrder);
                    }
    
                    // Set call will automatically send a new order or modify existing order with a new price.
                    _entryOrderCommand.Set(true, entrySide, OrderType.Limit, OrderQty, entryOrderPrice, 0);
    
    
                    if (_entryOrderCommand.TotalTradedQuantity > 0)
                    {
                        //Process Exit Logic
                        OrderSide exitSide = OrderSide.None;
                        if (_entryOrderCommand.CurrentOrder.OrderSide == OrderSide.Buy)
                            exitSide = OrderSide.Sell;
                        else
                            exitSide = OrderSide.Buy;
    
                        double exitOrderPrice = 0;
                        if (_exitOrderCommand.CurrentOrder == null)
                            exitOrderPrice = base.GetBestPrice(_ivInfo.MarketDataContainer, entrySide);
                        else
                            exitOrderPrice = base.GetBestPrice(_ivInfo.MarketDataContainer, _exitOrderCommand.CurrentOrder);
    
                        _exitOrderCommand.Set(true, exitSide, OrderType.Limit, _entryOrderCommand.TotalTradedQuantity, exitOrderPrice, 0);
                    }
    
                    if (_entryOrderCommand.TotalTradedQuantity > 0 &&
                       _entryOrderCommand.TotalTradedQuantity == _exitOrderCommand.TotalTradedQuantity)
                    {
                        CompletedRounds++;
                        base.TraceLogInfo("Transaction Completed.");
                        string errorString = string.Empty;
    
                        if (!_entryOrderCommand.Reset(out errorString))
                            base.TraceLogError("Smart Order Resetting Failed. Reason : " + errorString);
    
                        if (!_exitOrderCommand.Reset(out errorString))
                            base.TraceLogError("Smart Order Resetting Failed. Reason : " + errorString);
                    }
                }
                catch(Exception oEx)
                {
                    base.TraceLogError("Error occured. Message : " + oEx.Message);
                }
            }
    
            #endregion
    
    
            #region Private Methods
            private OrderSide GetSpreadScalpingeEntrySide(IMarketDataContainerInfo marketDataContainerInfo)
            {
    
                // Alpha to detect a direction of trade entry
                OrderSide spreadScalpingeEntryDecesion = OrderSide.None;
    
                double marketSpreadValue = marketDataContainerInfo.TouchLineInfo.BestAskPrice  -marketDataContainerInfo.TouchLineInfo.BestBidPrice;
    
                double buyerLTPGap = marketDataContainerInfo.TouchLineInfo.LastPrice - marketDataContainerInfo.TouchLineInfo.BestBidPrice;
                double sellerLTPGap = marketDataContainerInfo.TouchLineInfo.BestAskPrice - marketDataContainerInfo.TouchLineInfo.LastPrice;
    
                if (marketSpreadValue >= BenchMarkSpread)
                {
                    if (buyerLTPGap > sellerLTPGap)
                        spreadScalpingeEntryDecesion = OrderSide.Buy;
                    else if (buyerLTPGap < sellerLTPGap)
                        spreadScalpingeEntryDecesion = OrderSide.Sell;
                }
    
                return spreadScalpingeEntryDecesion;
            }
    
            protected override void OnDispose()
            {
                if (!IsInitialized)
                    return;
                TraceLogInfo("Strategy Dispossing.");
            }
        }    
    }
    

    Change the output path property value in project setting to redirect the output to {BlitzTraderInstallation Folder}/Strategies folder.
    Now all we need to do is build the project. You can also manually copy the required custom strategy DLL file and any of its dependedable DLL to {BlitzTraderInstallation Folder}/Strategies folder.
    The user defined Strategy implementation class must derive from QX.Blitz.Core.StrategyBase base class provided by the framework. By overriding methods of this StrategyBase class provided by the framework, you can customize the behaviour of your strategy. StrategyBase class provides a way to establish a bridge between your code and the native platform.