Order

Order management is the main functionality of an initiator. An exchange must be capable of handling the following:

  • New order (35=D): This message is sent for a trading indication. This message can describe numerous types of orders, such as Limit, Fill or Kill, and Market order.
  • Cancel order (35=F): This message is sent to indicate that an order's been canceled.
  • Amend order (35=G): This message is sent to amend an order.

The onNewOrderSingle function is the function that handles the orders that are sent by the initiator. This function needs to get the principal order features:

  • Symbol (the ticker symbol)
  • Side (buy or sell)
  • Type (market, limit, stop, stop limit, and so on)
  • Quantity (the quantity to be traded)
  • Price (the price to be traded)
  • Client order ID (the unique identifier for an order)
  • Quote ID (the quote identifier to be traded)

An exchange checks whether the order ID already exists. If it does, a rejection message should be sent to indicate that it isn't possible to create a new order with the same order ID. If the order is correctly received by the exchange, an execution report message will be sent to the initiator, indicating that the exchange has received the order.

In the GitHub fixsim code, the author chose to reject randomly incoming orders. When we will talk about backtesting later in this book, we will mention the different options we can introduce to model the market's behavior. Introducing a random rejection is one way of mimicking the market's behavior. If there is no rejection, the exchange will fill the order by sending an execution report 35=8 with an order status indicating that it's been filled.

The onNewOrderSingle function (callback) is divided into two parts. The first part collects the information from the New Order (35=D) message. The second part creates a response for the initiator. This response will be an Execution Report 35=8 message.

The code will create quickfix objects (symbol, side, ordType, and so on) and get the value from the tag values by using the getField function. The author of this code chooses to accept an order, but only if this order has been previously quoted. This means that the order will be based on a price update that has been received by our trading system:

def onNewOrderSingle(self, message, beginString, sessionID):
symbol = quickfix.Symbol()
side = quickfix.Side()
ordType = quickfix.OrdType()
orderQty = quickfix.OrderQty()
price = quickfix.Price()
clOrdID = quickfix.ClOrdID()
quoteID = quickfix.QuoteID()
currency = quickfix.Currency()

message.getField(ordType)
if ordType.getValue() != quickfix.OrdType_PREVIOUSLY_QUOTED:
raise quickfix.IncorrectTagValue(ordType.getField())

message.getField(symbol)
message.getField(side)
message.getField(orderQty)
message.getField(price)
message.getField(clOrdID)
message.getField(quoteID)
message.getField(currency)

The following code will create the Execution Report (35=8) message. The first line of this code creates an object execution report representing this message. The line after that will create the required headers for this message:

    executionReport = quickfix.Message()
executionReport.getHeader().setField(beginString)
executionReport.getHeader().setField(quickfix.MsgType(quickfix.MsgType_ExecutionReport))
executionReport.setField(quickfix.OrderID(self.idGen.orderID()))
executionReport.setField(quickfix.ExecID(self.idGen.execID()))

The following code takes care of building the code so that it simulates rejections. It will reject the code by taking a reject_chance (a percentage) into account:

    try:
reject_chance = random.choice(range(1, 101))
if self.rejectRate > reject_chance:
raise FixSimError("Rejected by cruel destiny %s" % str((reject_chance, self.rejectRate)))

The following code will run some checks on the execution size and the price:

        execPrice = price.getValue()
execSize = orderQty.getValue()
if execSize > quote.size:
raise FixSimError("size to large for quote")

if abs(execPrice - quote.price) > 0.0000001:
raise FixSimError("Trade price not equal to quote")

The code will finish by populating the required fields of the Execution Report message:

        executionReport.setField(quickfix.SettlDate(self.getSettlementDate()))
executionReport.setField(quickfix.Currency(subscription.currency))
executionReport.setField(quickfix.OrdStatus(quickfix.OrdStatus_FILLED))
executionReport.setField(symbol)
executionReport.setField(side)
executionReport.setField(clOrdID)
executionReport.setField(quickfix.Price(price.getValue()))
executionReport.setField(quickfix.AvgPx(execPrice))
executionReport.setField(quickfix.LastPx(execPrice))
executionReport.setField(quickfix.LastShares(execSize))
executionReport.setField(quickfix.CumQty(execSize))
executionReport.setField(quickfix.OrderQty(execSize))
executionReport.setField(quickfix.ExecType(quickfix.ExecType_FILL))
executionReport.setField(quickfix.LeavesQty(0))

The following code will build the rejection message in case of an error. It is done in the same way as building the message to indicate that the order has been executed. We specify the Rejected value in the Order Status of the Execution Report message:

except Exception as e:
self.logger.exception("FixServer:Close order error")
executionReport.setField(quickfix.SettlDate(''))
executionReport.setField(currency)
executionReport.setField(quickfix.OrdStatus(quickfix.OrdStatus_REJECTED))
executionReport.setField(symbol)
executionReport.setField(side)
executionReport.setField(clOrdID)
executionReport.setField(quickfix.Price(0))
executionReport.setField(quickfix.AvgPx(0))
executionReport.setField(quickfix.LastPx(0))
executionReport.setField(quickfix.LastShares(0))
executionReport.setField(quickfix.CumQty(0))
executionReport.setField(quickfix.OrderQty(0))
executionReport.setField(quickfix.ExecType(quickfix.ExecType_REJECTED))
executionReport.setField(quickfix.LeavesQty(0))

Finally, we will send the message back to the initiator:

self.sendToTarget(executionReport, sessionID)

This concludes the part of the code that's specific to the acceptor. The role of the acceptor can be more rich than the bare minimum code we implement. The main role of the acceptor is to match orders between traders. If we were implementing an exchange, we would need to create a matching engine (to match orders that can be filled). In this simple example, we chose to fill our orders regardless of the state of the market. The main goal was just to build a simulation mimicking the behavior of the market by filling and rejecting orders.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.108.9