?> QuantXpress – Deliver high performance automated trading and connectivity solutions



Bid-Ask Spread Scalping Strategy

Scalping is a legitimate method of arbitrage of small price gaps created by the bid-ask spread. Traders enter into trades when a market spread difference is bigger than a specified benchmark which is set wider than the usual spread of Instrument. Traders make the spread profit means to buy at the Bid price and sell at the Ask price, in order to gain the bid/ask difference. The scalping opportunities are created by quick momentum of trades triggered by order flow.
We are demonstrating a sample spread scalping strategy developed using BlitzTrader API.

The Strategy Parameter detail:

Strategy Parameter Types Description
BenchMarkSpread double User defined Benchmark
OrderQty int User defined order quantity
TotalRounds int User defined maximum rounds. The single rounds constitutes the entry and exit of the trade with P/L
CompletedRounds int Maintains the completed rounds and strategy publish this information to trading terminal for visibility of completed rounds. The further round will not be executed when completed rounds equals total rounds.

The Instrument Variable

The strategy needs single instrument to scalp bid-ask spread. We can define an IV variable representing an instrument of types Equities, Futures and Options. During portfolio creation user map exact market to this IV.

Smart Order Command

The strategy uses two smart order commands for representing a entry and exit orders legs of trade cycle. Smart order gives flexible means for managing fast quoting, then individual order command.
The initial state of smart order command is in reset state and any call to set method on smart order will send a new order. Further call to set will modify the order with new price till order will not be again reset. Reset method call on SmartOrder object which holds an open order state will immediately cancel the open order.
Reset on SmartOrder command can also be achieved by calling a Set method with first argument to be set as false.

Strategy Execution Logic

Strategy starts quoting in an entry direction (Buy or Sell) based on a proprietary alpha logic which provides some indication to enter in the direction of the market. In this example we are using a very simple logic to sense the direction based on instrument last traded price difference from current Best Bid and Ask. The function GetSpreadScalpingeEntrySide encapsulates the decision for the entry direction. If the functions return no direction, the smart order command reacts automatically to cancel the existing bid.
The strategy evaluates the direction on every market change event and automatically manages the order side and keeps improving the bid based on new market conditions. The strategy also manages bidding by considering that it never becomes best of the self bid and if next best bid has gap more than instrument minimum tick size, then it adjust the bid to maintain at maximum 1 tick size gap with next bid price.
Once the entry order got filled, the exit order command automatically starts bidding in reverse direction.
When both entry-exit order generates trades, then strategy should not carry any open positions and we reset both the orders command to start a new trading cycle.
The IOrderCommand interface represents a smart order command and with set and reset method it automatically manages placing new order, modifying and cancelling order.
 
public void SendSetPositionCommand(int newPosition,
                                   int lastPosition,
                                   string terminalID, 
                                   string strategyName, 
                                   string marketSymbol, 
                                   string text)
        {
            if (ClientSocket != null && ClientSocket.Connected)
            {
                ST_ATECommand_SetPosition commandRequest = new ST_ATECommand_SetPosition();
                commandRequest.Header = new HeaderStruct();
                commandRequest.Header.MessageCode = (int)MessageType.AtExecutionSetPosition;
                commandRequest.Header.ATSystemType = 0;
                commandRequest.Header.SendingTime = ExchangeCommon.GetCurrentTotalSecondsFromBaseDate(ExchangeSegment.NONE);
                commandRequest.Header.MessageSize = _setPositionMessageSize;

                commandRequest.NewPosition = newPosition;
                commandRequest.LastPosition = lastPosition;
                commandRequest.TerminalID = terminalID;
                commandRequest.StrategyName = strategyName;
                commandRequest.MarketSymbol = marketSymbol;
                commandRequest.Text = text;

                byte[] bytes = QX.Common.Helper.MemoryHelper.StructureToByteArray(commandRequest);

                ClientSocket.Send(bytes, SocketFlags.None);

                OutboundMessageCount++;
            }
        }

        public void SendMarketOrderCommand(int orderSide,
                                           int quantity,
                                           string terminalID, 
                                           string strategyName, 
                                           string marketSymbol, 
                                           string text)
        {
            if (ClientSocket != null && ClientSocket.Connected)
            {
                ST_ATECommand_SendMarketOrder commandRequest = new ST_ATECommand_SendMarketOrder();
                commandRequest.Header = new HeaderStruct();
                commandRequest.Header.MessageCode = (int)MessageType.AtExecutionSendMarketOrder;
                commandRequest.Header.ATSystemType = 0;
                commandRequest.Header.SendingTime = ExchangeCommon.GetCurrentTotalSecondsFromBaseDate(ExchangeSegment.NONE);
                commandRequest.Header.MessageSize = _sendMarketOrderMessageSize;

                commandRequest.Side = orderSide;
                commandRequest.Quantity = quantity;
                commandRequest.TerminalID = terminalID;
                commandRequest.StrategyName = strategyName;
                commandRequest.MarketSymbol = marketSymbol;
                commandRequest.Text = text;

                byte[] bytes = QX.Common.Helper.MemoryHelper.StructureToByteArray(commandRequest);

                ClientSocket.Send(bytes, SocketFlags.None);

                OutboundMessageCount++;
            }
        }

        public void SendLimitOrderCommand(int orderSide,
                                          int quantity,
                                          double limitPrice, 
                                          string terminalID, 
                                          string strategyName, 
                                          string marketSymbol, 
                                          string text)
        {
            if (ClientSocket != null && ClientSocket.Connected)
            {
                ST_ATECommand_SendLimitOrderOrder commandRequest = new ST_ATECommand_SendLimitOrderOrder();
                commandRequest.Header = new HeaderStruct();
                commandRequest.Header.MessageCode = (int)MessageType.AtExecutionSendLimitOrder;
                commandRequest.Header.ATSystemType = 0;
                commandRequest.Header.SendingTime = ExchangeCommon.GetCurrentTotalSecondsFromBaseDate(ExchangeSegment.NONE);
                commandRequest.Header.MessageSize = _sendLimitOrderOrderMessageSize;

                commandRequest.Side = orderSide;
                commandRequest.LimitPrice = limitPrice;
                commandRequest.Quantity = quantity;
                commandRequest.TerminalID = terminalID;
                commandRequest.StrategyName = strategyName;
                commandRequest.MarketSymbol = marketSymbol;
                commandRequest.Text = text;

                byte[] bytes = QX.Common.Helper.MemoryHelper.StructureToByteArray(commandRequest);

                ClientSocket.Send(bytes, SocketFlags.None);

                OutboundMessageCount++;
            }
        }

        public void SendStopOrderCommand(int orderSide,
                                         int quantity, 
                                         double stopPrice,
                                         string terminalID, 
                                         string strategyName, 
                                         string marketSymbol, 
                                         string text)
        {
            if (ClientSocket != null && ClientSocket.Connected)
            {
                ST_ATECommand_SendStopOrderOrder commandRequest = new ST_ATECommand_SendStopOrderOrder();
                commandRequest.Header = new HeaderStruct();
                commandRequest.Header.MessageCode = (int)MessageType.AtExecutionSendStopOrder;
                commandRequest.Header.ATSystemType = 0;
                commandRequest.Header.SendingTime = ExchangeCommon.GetCurrentTotalSecondsFromBaseDate(ExchangeSegment.NONE);
                commandRequest.Header.MessageSize = _sendStopOrderOrderMessageSize;

                commandRequest.Side = orderSide;
                commandRequest.StopPrice = stopPrice;
                commandRequest.Quantity = quantity;
                commandRequest.TerminalID = terminalID;
                commandRequest.StrategyName = strategyName;
                commandRequest.MarketSymbol = marketSymbol;
                commandRequest.Text = text;

                byte[] bytes = QX.Common.Helper.MemoryHelper.StructureToByteArray(commandRequest);

                ClientSocket.Send(bytes, SocketFlags.None);

                OutboundMessageCount++;
            }
        }

        public void SendCancelAllOrderCommand(string terminalID, string strategyName, string marketSymbol, string text)
        {
            if (ClientSocket != null && ClientSocket.Connected)
            {
                ST_ATECommand_CancellAllOrder commandRequest = new ST_ATECommand_CancellAllOrder();
                commandRequest.Header = new HeaderStruct();
                commandRequest.Header.MessageCode = (int)MessageType.AtExecutionCancelAllOrder;
                commandRequest.Header.ATSystemType = 0;
                commandRequest.Header.SendingTime = ExchangeCommon.GetCurrentTotalSecondsFromBaseDate(ExchangeSegment.NONE);
                commandRequest.Header.MessageSize = _cancellAllOrderMessageSize;

                commandRequest.TerminalID = terminalID;
                commandRequest.StrategyName = strategyName;
                commandRequest.MarketSymbol = marketSymbol;
                commandRequest.Text = text;

                byte[] bytes = QX.Common.Helper.MemoryHelper.StructureToByteArray(commandRequest);

                ClientSocket.Send(bytes, SocketFlags.None);

                OutboundMessageCount++;
            }
        }