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.
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: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.