package org.marketcetera.trade;
import java.math.BigDecimal;
import java.util.Map;
import org.marketcetera.core.instruments.InstrumentToMessage;
import org.marketcetera.quickfix.FIXMessageFactory;
import org.marketcetera.quickfix.FIXMessageUtil;
import org.marketcetera.util.except.I18NException;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.misc.ClassVersion;
import quickfix.DataDictionary;
import quickfix.Message;
import quickfix.field.*;
/**
* FIX conversion utilities.
*
* @author tlerios@marketcetera.com
* @since 1.0.0
* @version $Id: FIXConverter.java 16888 2014-04-22 18:32:36Z colin $
*/
/* $License$ */
@ClassVersion("$Id: FIXConverter.java 16888 2014-04-22 18:32:36Z colin $") //$NON-NLS-1$
public final class FIXConverter
{
// CLASS METHODS.
/**
* Adds the given instrument to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param instrument The instrument. It cannot be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param required True if the instrument is required.
* @param msg The QuickFIX/J message.
* @throws I18NException Thrown if the instrument is required but is
* not set.
*/
private static void addInstrument
(Instrument instrument,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
if(instrument==null) {
if (required) {
throw new I18NException(new I18NBoundMessage1P(Messages.NO_INSTRUMENT,instrument));
}
} else{
InstrumentToMessage<?> instrumentFunction=InstrumentToMessage.SELECTOR.
forInstrument(instrument);
if (!instrumentFunction.isSupported(fixDictionary,msgType)) {
throw new I18NException(Messages.UNSUPPORTED_INSTRUMENT);
}
instrumentFunction.set(instrument,fixDictionary,msgType,msg);
}
}
/**
* Adds the given price to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param price The price. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the price is required but is not set.
*
* @throws I18NException Thrown if the price is required but is
* not set.
*/
private static void addPrice
(BigDecimal price,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,Price.FIELD));
if (price==null) {
if (supported && required) {
throw new I18NException(Messages.NO_PRICE);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_PRICE);
}
msg.setField(new Price(price));
}
}
/**
* Adds the given quantity to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param quantity The quantity. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the quantity is required but is not set.
*
* @throws I18NException Thrown if the quantity is required but is
* not set.
*/
private static void addQuantity
(BigDecimal quantity,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,OrderQty.FIELD));
if (quantity==null) {
if (supported && required) {
throw new I18NException(Messages.NO_QUANTITY);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_QUANTITY);
}
msg.setField(new OrderQty(quantity));
}
}
/**
* Adds the given display quantity to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param display quantity The quantity. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the display quantity is required but is not set.
*
* @throws I18NException Thrown if the display quantity is required but is
* not set.
*/
private static void addDisplayQuantity
(BigDecimal displayQuantity,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,MaxFloor.FIELD));
if (displayQuantity==null) {
if (supported && required) {
throw new I18NException(Messages.NO_DISPLAY_QUANTITY);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_DISPLAY_QUANTITY);
}
msg.setField(new MaxFloor(displayQuantity));
}
}
/**
* Adds the given account to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param account The account. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the account is required but is not set.
*
* @throws I18NException Thrown if the account is required but is
* not set.
*/
private static void addAccount
(String account,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,Account.FIELD));
if (account==null) {
if (supported && required) {
throw new I18NException(Messages.NO_ACCOUNT);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_ACCOUNT);
}
msg.setField(new Account(account));
}
}
/**
* Adds the given text to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param text The text. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the account is required but is not set.
*
* @throws I18NException Thrown if the account is required but is
* not set.
*/
private static void addText
(String text,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,Text.FIELD));
if (text==null) {
if (supported && required) {
throw new I18NException(Messages.NO_TEXT);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_TEXT);
}
msg.setField(new Text(text));
}
}
/**
* Adds the given order ID to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param orderID The order ID. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the order ID is required but is not
* set.
*
* @throws I18NException Thrown if the order ID is required but is
* not set.
*/
private static void addOrderID
(OrderID orderID,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,ClOrdID.FIELD));
if (orderID==null) {
if (supported && required) {
throw new I18NException(Messages.NO_ORDER_ID);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_ORDER_ID);
}
msg.setField(new ClOrdID(orderID.getValue()));
}
}
/**
* Adds the given original order ID to the given QuickFIX/J
* message (of the given FIX dictionary).
*
* @param originalOrderID The original order ID. It may be null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the original order ID is required but
* is not set.
*
* @throws I18NException Thrown if the original order ID is
* required but is not set.
*/
private static void addOriginalOrderID
(OrderID originalOrderID,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,OrigClOrdID.FIELD));
if (originalOrderID==null) {
if (supported && required) {
throw new I18NException(Messages.NO_ORIGINAL_ORDER_ID);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_ORIGINAL_ORDER_ID);
}
msg.setField(new OrigClOrdID(originalOrderID.getValue()));
}
}
/**
* Adds the given broker order ID to the given QuickFIX/J
* message (of the given FIX dictionary).
*
* @param brokerOrderID The broker order ID. It may be
* null.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the broker order ID is required
* but is not set.
*
* @throws I18NException Thrown if the broker order ID is
* required but is not set.
*/
private static void addBrokerOrderID
(String brokerOrderID,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,quickfix.field.OrderID.FIELD));
if (brokerOrderID==null) {
if (supported && required) {
throw new I18NException(Messages.NO_BROKER_ORDER_ID);
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_BROKER_ORDER_ID);
}
msg.setField(new quickfix.field.OrderID(brokerOrderID));
}
}
/**
* Adds the given side to the given QuickFIX/J message (of the
* given FIX dictionary).
*
* @param side The side. It may be null or unknown.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the side is required but is not set.
*
* @throws I18NException Thrown if the side is required but is not
* set.
*/
private static void addSide
(Side side,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,quickfix.field.Side.FIELD));
if ((side==null) ||
(side==Side.Unknown)) {
if (supported && required) {
throw new I18NException
(new I18NBoundMessage1P(Messages.NO_SIDE,side));
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_SIDE);
}
msg.setField(new quickfix.field.Side(side.getFIXValue()));
}
}
/**
* Adds the given time-in-force to the given QuickFIX/J message
* (of the given FIX dictionary).
*
* @param timeInForce The time-in-force. It may be null or
* unknown.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the time-in-force is required but is
* not set.
*
* @throws I18NException Thrown if the time-in-force is required
* but is not set.
*/
private static void addTimeInForce
(TimeInForce timeInForce,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,quickfix.field.TimeInForce.FIELD));
if ((timeInForce==null) ||
(timeInForce==TimeInForce.Unknown)) {
if (supported && required) {
throw new I18NException
(new I18NBoundMessage1P
(Messages.NO_TIME_IN_FORCE,timeInForce));
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_TIME_IN_FORCE);
}
msg.setField(new quickfix.field.TimeInForce
(timeInForce.getFIXValue()));
}
}
/**
* Adds the given position effect to the given QuickFIX/J message
* (of the given FIX dictionary).
*
* @param positionEffect The position effect. It may be null or
* unknown.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the position effect is required but is
* not set.
*
* @throws I18NException Thrown if the position effect is required
* but is not set.
*/
private static void addPositionEffect
(PositionEffect positionEffect,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,quickfix.field.PositionEffect.FIELD));
if ((positionEffect==null) ||
(positionEffect==PositionEffect.Unknown)) {
if (supported && required) {
throw new I18NException
(new I18NBoundMessage1P
(Messages.NO_POSITION_EFFECT,positionEffect));
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_POSITION_EFFECT);
}
msg.setField(new quickfix.field.PositionEffect
(positionEffect.getFIXValue()));
}
}
/**
* Adds the given order capacity to the given QuickFIX/J message
* (of the given FIX dictionary).
*
* @param orderCapacity The order capacity. It may be null or
* unknown.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the order capacity is required but is
* not set.
*
* @throws I18NException Thrown if the order capacity is required
* but is not set.
*/
private static void addOrderCapacity
(OrderCapacity orderCapacity,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,quickfix.field.OrderCapacity.FIELD));
if ((orderCapacity==null) ||
(orderCapacity==OrderCapacity.Unknown)) {
if (supported && required) {
throw new I18NException
(new I18NBoundMessage1P
(Messages.NO_ORDER_CAPACITY,orderCapacity));
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_ORDER_CAPACITY);
}
msg.setField(new quickfix.field.OrderCapacity
(orderCapacity.getFIXValue()));
}
}
/**
* Adds the given order type to the given QuickFIX/J message (of
* the given FIX dictionary).
*
* @param orderType The order type. It may be null or unknown.
* @param fixDictionary The FIX dictionary.
* @param msgType The FIX message type
* @param msg The QuickFIX/J message.
* @param required True if the order type is required but is not
* set.
*
* @throws I18NException Thrown if the order type is required but
* is not set.
*/
private static void addOrderType
(OrderType orderType,
DataDictionary fixDictionary,
String msgType,
Message msg,
boolean required)
throws I18NException
{
boolean supported=
(fixDictionary.isMsgField(msgType,OrdType.FIELD));
if ((orderType==null) ||
(orderType==OrderType.Unknown)) {
if (supported && required) {
throw new I18NException
(new I18NBoundMessage1P(Messages.NO_ORDER_TYPE,orderType));
}
} else{
if (!supported) {
throw new I18NException(Messages.UNSUPPORTED_ORDER_TYPE);
}
msg.setField(new OrdType(orderType.getFIXValue()));
}
}
/**
* Adds the custom fields of the given order to the given
* QuickFIX/J message.
*
* @param o The order.
* @param msg The message.
*/
private static void addCustomFields(OrderBase o,
Message msg)
{
Map<String,String> fields=o.getCustomFields();
if (fields==null) {
return;
}
for(Map.Entry<String,String> entry : fields.entrySet()) {
msg.setString(Integer.parseInt(String.valueOf(entry.getKey())),
String.valueOf(entry.getValue()));
}
}
/**
* Returns the QuickFIX/J message form of the given single order,
* using the given message factory (alongside the associated data
* dictionary) to effect conversion.
*
* @param fixFactory The factory.
* @param fixDictionary The FIX dictionary.
* @param o The order.
*
* @return The QuickFIX/J message.
*
* @throws I18NException Thrown if conversion fails.
*/
public static Message toQMessage
(FIXMessageFactory fixFactory,
DataDictionary fixDictionary,
OrderSingle o)
throws I18NException
{
Message msg=fixFactory.newOrderEmpty();
String msgType=MsgType.ORDER_SINGLE;
addOrderID(o.getOrderID(),fixDictionary,msgType,msg,true);
addInstrument(o.getInstrument(),fixDictionary,msgType,msg,true);
addSide(o.getSide(),fixDictionary,msgType,msg,true);
addOrderType(o.getOrderType(),fixDictionary,msgType,msg,true);
addQuantity(o.getQuantity(),fixDictionary,msgType,msg,false);
addDisplayQuantity(o.getDisplayQuantity(),fixDictionary,msgType,msg,false);
addTimeInForce(o.getTimeInForce(),fixDictionary,msgType,msg,false);
addAccount(o.getAccount(),fixDictionary,msgType,msg,false);
addText(o.getText(),fixDictionary,msgType,msg,false);
addPositionEffect(o.getPositionEffect(),fixDictionary,msgType,msg,false);
addOrderCapacity(o.getOrderCapacity(),fixDictionary,msgType,msg,false);
if (o.getOrderType()==OrderType.Limit) {
addPrice(o.getPrice(),fixDictionary,msgType,msg,true);
}
addCustomFields(o, msg);
fixFactory.getMsgAugmentor().newOrderSingleAugment(msg);
return msg;
}
/**
* Returns the QuickFIX/J message form of the given order
* cancellation, using the given message factory (alongside the
* associated data dictionary) to effect conversion.
*
* @param fixFactory The factory.
* @param fixDictionary The FIX dictionary.
* @param o The order cancellation.
*
* @return The QuickFIX/J message.
*
* @throws I18NException Thrown if conversion fails.
*/
public static Message toQMessage
(FIXMessageFactory fixFactory,
DataDictionary fixDictionary,
OrderCancel o)
throws I18NException
{
Message msg=fixFactory.newCancelEmpty();
String msgType=MsgType.ORDER_CANCEL_REQUEST;
addOriginalOrderID(o.getOriginalOrderID(),fixDictionary,msgType,msg,true);
addOrderID(o.getOrderID(),fixDictionary,msgType,msg,true);
addInstrument(o.getInstrument(),fixDictionary,msgType,msg,true);
addSide(o.getSide(),fixDictionary,msgType,msg,true);
addQuantity(o.getQuantity(),fixDictionary,msgType,msg,false);
addBrokerOrderID(o.getBrokerOrderID(),fixDictionary,msgType,msg,false);
addAccount(o.getAccount(),fixDictionary,msgType,msg,false);
addText(o.getText(),fixDictionary,msgType,msg,false);
addCustomFields(o, msg);
fixFactory.getMsgAugmentor().cancelRequestAugment(msg);
return msg;
}
/**
* Returns the QuickFIX/J message form of the given order
* replacement, using the given message factory (alongside the
* associated data dictionary) to effect conversion.
*
* @param fixFactory The factory.
* @param fixDictionary The FIX dictionary.
* @param o The order replacement.
*
* @return The QuickFIX/J message.
*
* @throws I18NException Thrown if conversion fails.
*/
public static Message toQMessage
(FIXMessageFactory fixFactory,
DataDictionary fixDictionary,
OrderReplace o)
throws I18NException
{
Message msg=fixFactory.newCancelReplaceEmpty();
String msgType=MsgType.ORDER_CANCEL_REPLACE_REQUEST;
addOriginalOrderID(o.getOriginalOrderID(),fixDictionary,msgType,msg,true);
addOrderID(o.getOrderID(),fixDictionary,msgType,msg,true);
addInstrument(o.getInstrument(),fixDictionary,msgType,msg,true);
addSide(o.getSide(),fixDictionary,msgType,msg,true);
addOrderType(o.getOrderType(),fixDictionary,msgType,msg,true);
addQuantity(o.getQuantity(),fixDictionary,msgType,msg,false);
addDisplayQuantity(o.getDisplayQuantity(),fixDictionary,msgType,msg,false);
addAccount(o.getAccount(),fixDictionary,msgType,msg,false);
addText(o.getText(),fixDictionary,msgType,msg,false);
addPrice(o.getPrice(),fixDictionary,msgType,msg,false);
addTimeInForce(o.getTimeInForce(),fixDictionary,msgType,msg,false);
addPositionEffect(o.getPositionEffect(),fixDictionary,msgType,msg,false);
addOrderCapacity(o.getOrderCapacity(),fixDictionary,msgType,msg,false);
addBrokerOrderID(o.getBrokerOrderID(),fixDictionary,msgType,msg,false);
addCustomFields(o, msg);
fixFactory.getMsgAugmentor().cancelReplaceRequestAugment(msg);
return msg;
}
/**
* Returns the QuickFIX/J message form of the given order, using
* the given message factory (alongside the associated data
* dictionary) to effect conversion.
*
* @param fixFactory The factory.
* @param fixDictionary The FIX dictionary.
* @param o The order.
*
* @return The QuickFIX/J message.
*
* @throws I18NException Thrown if conversion fails.
*/
public static Message toQMessage
(FIXMessageFactory fixFactory,
DataDictionary fixDictionary,
Order o)
throws I18NException
{
if (o instanceof FIXOrder) {
return ((FIXOrder)o).getMessage();
}
if (o instanceof OrderSingle) {
return toQMessage(fixFactory,fixDictionary,(OrderSingle)o);
}
if (o instanceof OrderCancel) {
return toQMessage(fixFactory,fixDictionary,(OrderCancel)o);
}
if (o instanceof OrderReplace) {
return toQMessage(fixFactory,fixDictionary,(OrderReplace)o);
}
throw new I18NException
(new I18NBoundMessage1P(Messages.CANNOT_CONVERT,o));
}
/**
* Returns the FIX Agnostic message form of the given QuickFIX/J message.
*
* @param inMessage a <code>Message</code> value containing the QuickFIX/J message.
* @param inOriginator an <code>Originator</code> value containing the message originator
* @param inBrokerID a <code>BrokerID</code> value containing the ID of the broker which generated the QuickFIX/J message or <code>null</code>
* @param inHierarchy a <code>Hierarchy</code> value containing the hierarchy of the order
* @param inActorID a <code>UserID</code> value containing the ID of the actor user of this QuickFIX/J message or <code>null</code>
* @param inViewerID a <code>UserID</code> value containing the ID of the viewer user of this QuickFIX/J message or <code>null</code>
* @return a <code>TradeMessage</code> containing the FIX Agnostic message.
* @throws MessageCreationException if conversion fails
*/
public static TradeMessage fromQMessage(Message inMessage,
Originator inOriginator,
BrokerID inBrokerID,
Hierarchy inHierarchy,
UserID inActorID,
UserID inViewerID)
throws MessageCreationException
{
if(FIXMessageUtil.isExecutionReport(inMessage)) {
return Factory.getInstance().createExecutionReport(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
}
if(FIXMessageUtil.isCancelReject(inMessage)) {
return Factory.getInstance().createOrderCancelReject(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
}
return Factory.getInstance().createFIXResponse(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
}
/**
* Create a new FIXConverter instance.
*/
private FIXConverter() {}
}