preloader
blog-post

ORDER FIX API ~ Execution Report

author image

This is the most important callback function to receive execution report message once the status of an order changes.

It can be triggered after an market order message is sent.

Usually, it’s a notification about:

  • A pending order is created.

ExecType: ExecType.PENDING_NEW ExecType: ExecType.NEW OrdStatus: OrdStatus.NEW OrdStatus: OrdStatus.PENDING_NEW

  • A pending order is canceled.

ExecType: ExecType.CANCELED ExecType: ExecType.PENDING_CANCEL OrdStatus: OrdStatus.CANCELED

  • An open trade position is filled.

ExecType: ExecType.TRADE ExecType: ExecType.FILL OrdStatus: OrdStatus.PARTIALLY_FILLED OrdStatus: OrdStatus.FILLED

  • A pending order is rejected.

ExecType: ExecType.REJECTED OrdStatus: OrdStatus.REJECTED

The message type (MsgType, Tag ID: 35) is “8”. Actually, this tag field is encapsulated, only useful when you check the log file.

Usually, we will get these tag fields from the message:

  • Sending Time (SendingTime, Tag ID: 52)
SendingTime sendingTime = new SendingTime();
((Message) executionReport).getHeader().getField(sendingTime);
LocalDateTime localDateTime = sendingTime.getValue();
long time = localDateTime.toEpochSecond(ZoneOffset.UTC);
  • Client Order ID (ClOrdID, Tag ID: 11)

This ID should be generated on our side(the client side) and transferred when we send an order.

ClOrdID clOrdID = new ClOrdID();
executionReport.get(clOrdID);
String clOrderId = clOrdID.getValue();
  • Order ID (OrderID, Tag ID: 37)

This ID will be generated on the sell side(the server side).

Not every liquidity provider supports this tag field, so we need to check whether it exists in the received message by calling isSetOrderID.

OrderID orderID = null;
String orderId = "";
if (executionReport.isSetOrderID()) {
  orderID = new OrderID();
  executionReport.get(orderID);
  orderId = orderID.getValue();
}
  • Execution ID (ExecID, Tag ID: 17)

This ID will be generated on the sell side(the server side).

Not every liquidity provider supports this tag field, so we need to check whether it exists in the received message by calling isSetExecID.

ExecID execID = null;
String execId = "";
if (executionReport.isSetExecID()) {
  execID = new ExecID();
  executionReport.get(execID);
  execId = execID.getValue();
}
  • Execution Type (ExecType, Tag ID: 150)

This is an enumeration variable to describe the status of the specific execution report(the specific trade position).

ExecType execType = new ExecType();
executionReport.get(execType);
String executionType = null;
if (execType.getValue() == ExecType.PENDING_NEW) {
  executionType = "PENDING_NEW";
} else if (execType.getValue() == ExecType.NEW) {
  executionType = "NEW";
} else if (execType.getValue() == ExecType.CANCELED) {
  executionType = "CANCELED";
} else if (execType.getValue() == ExecType.PENDING_CANCEL) {
  executionType = "PENDING_CANCEL";
} else if (execType.getValue() == ExecType.REJECTED) {
  executionType = "REJECTED";
} else if (execType.getValue() == ExecType.TRADE) {
  executionType = "TRADE";
} else if (execType.getValue() == ExecType.FILL) {
  executionType = "FILLED";
} else if (execType.getValue() == ExecType.ORDER_STATUS) {
  executionType = "ORDER_STATUS";
} else {
  executionType = "NOT_SUPPORTED";
}
  • Order Status (OrdStatus, Tag ID: 39)

This is an enumeration variable to describe the status of the specific order.

OrdStatus ordStatus = new OrdStatus();
executionReport.get(ordStatus);
String orderStatus = null;
if (ordStatus.getValue() == OrdStatus.NEW) {
  orderStatus = "NEW";
} else if (ordStatus.getValue() == OrdStatus.PARTIALLY_FILLED) {
  orderStatus = "PARTIALLY_FILLED";
} else if (ordStatus.getValue() == OrdStatus.FILLED) {
  orderStatus = "FILLED";
} else if (ordStatus.getValue() == OrdStatus.CANCELED) {
  orderStatus = "CANCELED";
} else if (ordStatus.getValue() == OrdStatus.PENDING_CANCEL) {
  orderStatus = "PENDING_CANCEL";
} else if (ordStatus.getValue() == OrdStatus.REJECTED) {
  orderStatus = "REJECTED";
} else if (ordStatus.getValue() == OrdStatus.PENDING_NEW) {
  orderStatus = "PENDING_NEW";
} else {
  orderStatus = "NOT_SUPPORTED";
}
  • Time In Force (TimeInForce, Tag ID: 59)

To simplify the process, we only send market orders. So, TimeInForce usually is set to FILL_OR_KILL.

IMMEDIATE_OR_CANCEL is for orders being partially filled, not used in our tutorials.

TimeInForce timeInForce = new TimeInForce();
String tmInForce = null;

if (executionReport.isSetTimeInForce()) {
  executionReport.get(timeInForce);

  if (timeInForce.getValue() == TimeInForce.IMMEDIATE_OR_CANCEL) {
    tmInForce = "IMMEDIATE_OR_CANCEL";
  } else if (timeInForce.getValue() == TimeInForce.FILL_OR_KILL) {
    tmInForce = "FILL_OR_KILL";
  } else {
    tmInForce = "";
  }
} else {
  tmInForce = "NOT_SUPPORTED";
}
  • Instrument Name (Symbol, Tag ID: 55)
quickfix.field.Symbol symbol = new quickfix.field.Symbol();
executionReport.get(symbol);
String symbolName = symbol.getValue();
  • Order Side (Side, Tag ID: 54)

To simplify the process, this variable describes that the order side is either Buy or Sell.

Side side = new Side();
executionReport.get(side);
String orderSide = null;
if (side.getValue() == Side.BUY) {
  orderSide = "BUY";
} else if (side.getValue() == Side.SELL) {
  orderSide = "SELL";
} else {
  orderSide = "NOT_SUPPORTED";
}
  • Average Price (AvgPx, Tag ID: 6)

This variable describes the average value of all the open prices to execute the specific order.

AvgPx avgPx = null;
double avgPrice = 0.0;
if (executionReport.isSetAvgPx()) {
  avgPx = new AvgPx();
  executionReport.get(avgPx);
  avgPrice = avgPx.getValue();
}
  • Order Quantity (OrderQty, Tag ID: 38)

This variable describes the quantity of the specific order(order volume).

OrderQty orderQty = null;
double ordQty = 0.0;
if (executionReport.isSetOrderQty()) {
  orderQty = new OrderQty();
  executionReport.get(orderQty);
  ordQty = orderQty.getValue();

  ordQty *= 100000;
}
  • Quantity Open for Further Execution (LeavesQty, Tag ID: 151)

This variable describes the quantity of the specific trade position open for further execution.

LeavesQty leavesQty = new LeavesQty();
executionReport.get(leavesQty);
double leavesQuantity = leavesQty.getValue();
leavesQuantity *= 100000;
  • Quantity Filled (CumQty, Tag ID: 14)

This variable describes the quantity of the specific trade position filled.

CumQty cumQty = new CumQty();
executionReport.get(cumQty);
double cumQuantity = cumQty.getValue();
cumQuantity *= 100000;
  • Read (Text, Tag ID: 58)
Text text = new Text();
String reason = "";
if (executionReport.isSet(text)) {
  executionReport.getField(text);
  reason = text.getValue();
}
  • Transaction Time (TransactTime, Tag ID: 60)
TransactTime transactTime = null;
LocalDateTime transactionTime = null;
long transactTm = (long) Math.floor(System.currentTimeMillis() / 1000.0);
if (executionReport.isSetTransactTime()) {
  transactTime = new TransactTime();
  executionReport.get(transactTime);
  transactionTime = transactTime.getValue();
  transactTm = transactionTime.toEpochSecond(ZoneOffset.UTC);
}

Relevant Articles

Fintechee Online FIX API Parser

The received messages will output to the “orderlog” folder.

If you want to parse them, please use Fintechee Online FIX Parser.

Fintechee FIX API Trading Platform Individual Version

If you want to trade via FIX API, please use Fintechee FIX API Trading Platform Individual Version(Paid Version).

If you have a Github / Youtube account, you can get a free license for the paid version(No Charge)!

If you have no Github / Youtube account, you can still use Fintechee FIX API Trading Platform Bridge Version(Free Forever)!

If you are working for financial institutions, you can choose Fintechee FIX API Trading Platform Institution Version(White Label License).

Github Repository

Please access our Github repository to get the latest source codes.

The Entire Source Codes to ExecutionReport

public void onMessage(ExecutionReport executionReport, SessionID sessionID) {
  try {
    SendingTime sendingTime = new SendingTime();
    ((Message) executionReport).getHeader().getField(sendingTime);
    LocalDateTime localDateTime = sendingTime.getValue();
    long time = localDateTime.toEpochSecond(ZoneOffset.UTC);

    ClOrdID clOrdID = new ClOrdID();
    executionReport.get(clOrdID);
    String clOrderId = clOrdID.getValue();

    OrderID orderID = null;
    String orderId = "";
    if (executionReport.isSetOrderID()) {
      orderID = new OrderID();
      executionReport.get(orderID);
      orderId = orderID.getValue();
    }

    ExecID execID = null;
    String execId = "";
    if (executionReport.isSetExecID()) {
      execID = new ExecID();
      executionReport.get(execID);
      execId = execID.getValue();
    }

    ExecType execType = new ExecType();
    executionReport.get(execType);
    String executionType = null;
    if (execType.getValue() == ExecType.PENDING_NEW) {
      executionType = "PENDING_NEW";
    } else if (execType.getValue() == ExecType.NEW) {
      executionType = "NEW";
    } else if (execType.getValue() == ExecType.CANCELED) {
      executionType = "CANCELED";
    } else if (execType.getValue() == ExecType.PENDING_CANCEL) {
      executionType = "PENDING_CANCEL";
    } else if (execType.getValue() == ExecType.REJECTED) {
      executionType = "REJECTED";
    } else if (execType.getValue() == ExecType.TRADE) {
      executionType = "TRADE";
    } else if (execType.getValue() == ExecType.FILL) {
      executionType = "FILLED";
    } else if (execType.getValue() == ExecType.ORDER_STATUS) {
      executionType = "ORDER_STATUS";
    } else {
      executionType = "NOT_SUPPORTED";
    }

    OrdStatus ordStatus = new OrdStatus();
    executionReport.get(ordStatus);
    String orderStatus = null;
    if (ordStatus.getValue() == OrdStatus.NEW) {
      orderStatus = "NEW";
    } else if (ordStatus.getValue() == OrdStatus.PARTIALLY_FILLED) {
      orderStatus = "PARTIALLY_FILLED";
    } else if (ordStatus.getValue() == OrdStatus.FILLED) {
      orderStatus = "FILLED";
    } else if (ordStatus.getValue() == OrdStatus.CANCELED) {
      orderStatus = "CANCELED";
    } else if (ordStatus.getValue() == OrdStatus.PENDING_CANCEL) {
      orderStatus = "PENDING_CANCEL";
    } else if (ordStatus.getValue() == OrdStatus.REJECTED) {
      orderStatus = "REJECTED";
    } else if (ordStatus.getValue() == OrdStatus.PENDING_NEW) {
      orderStatus = "PENDING_NEW";
    } else {
      orderStatus = "NOT_SUPPORTED";
    }

    if (executionType.equals("REJECTED")
      || orderStatus.equals("REJECTED")) {

      System.out.println(time + " clOrderId: " + clOrderId + ", orderId: " + orderId + ", execId: " + execId + ", rejected by the LP");
    } else {				
      TimeInForce timeInForce = new TimeInForce();
      String tmInForce = null;

      if (executionReport.isSetTimeInForce()) {
        executionReport.get(timeInForce);

        if (timeInForce.getValue() == TimeInForce.IMMEDIATE_OR_CANCEL) {
          tmInForce = "IMMEDIATE_OR_CANCEL";
        } else if (timeInForce.getValue() == TimeInForce.FILL_OR_KILL) {
          tmInForce = "FILL_OR_KILL";
        } else {
          tmInForce = "";
        }
      } else {
        tmInForce = "NOT_SUPPORTED";
      }

      quickfix.field.Symbol symbol = new quickfix.field.Symbol();
      executionReport.get(symbol);
      String symbolName = symbol.getValue();

      Side side = new Side();
      executionReport.get(side);
      String orderSide = null;
      if (side.getValue() == Side.BUY) {
        orderSide = "BUY";
      } else if (side.getValue() == Side.SELL) {
        orderSide = "SELL";
      } else {
        orderSide = "NOT_SUPPORTED";
      }

      AvgPx avgPx = null;
      double avgPrice = 0.0;
      if (executionReport.isSetAvgPx()) {
        avgPx = new AvgPx();
        executionReport.get(avgPx);
        avgPrice = avgPx.getValue();
      }

      OrderQty orderQty = null;
      double ordQty = 0.0;
      if (executionReport.isSetOrderQty()) {
        orderQty = new OrderQty();
        executionReport.get(orderQty);
        ordQty = orderQty.getValue();

        ordQty *= 100000;
      }

      LeavesQty leavesQty = new LeavesQty();
      executionReport.get(leavesQty);
      double leavesQuantity = leavesQty.getValue();
      leavesQuantity *= 100000;

      CumQty cumQty = new CumQty();
      executionReport.get(cumQty);
      double cumQuantity = cumQty.getValue();
      cumQuantity *= 100000;

      Text text = new Text();
      String reason = "";
      if (executionReport.isSet(text)) {
        executionReport.getField(text);
        reason = text.getValue();
      }

      TransactTime transactTime = null;
      LocalDateTime transactionTime = null;
      long transactTm = (long) Math.floor(System.currentTimeMillis() / 1000.0);
      if (executionReport.isSetTransactTime()) {
        transactTime = new TransactTime();
        executionReport.get(transactTime);
        transactionTime = transactTime.getValue();
        transactTm = transactionTime.toEpochSecond(ZoneOffset.UTC);
      }

      System.out.println("A trade position is opened!");
      System.out.println("SendingTime: " + time);
      System.out.println("ClOrdID: " + clOrderId);
      System.out.println("OrderID: " + orderId);
      System.out.println("ExecID: " + execId);
      System.out.println("ExecType: " + executionType);
      System.out.println("OrdStatus: " + orderStatus);
      System.out.println("TimeInForce: " + tmInForce);
      System.out.println("Symbol: " + symbolName);
      System.out.println("Side: " + orderSide);
      System.out.println("AvgPx: " + avgPrice);
      System.out.println("OrderQty: " + ordQty);
      System.out.println("LeavesQty: " + leavesQuantity);
      System.out.println("CumQty: " + cumQuantity);
      System.out.println("Text: " + reason);
      System.out.println("TransactTime: " + transactTm);
    }
  } catch (Exception e) {
    e.printStackTrace();
    logger.error(e.getMessage());
  }
}

Recent Articles

blog-post

FIX API Starter Application Class

This is the FIX API Starter Application Class. It includes: A method to be called to start FIX API Data session A method …

Paid Consulting Service

We offer professional FIX API consulting services, including self-service options for establishing a broker business. There are no additional fees, and all resources can be utilized without any associated costs.

Book One
*