package org.marketcetera.trade;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.marketcetera.core.IDFactory;
import org.marketcetera.core.InMemoryIDFactory;
import org.marketcetera.core.NoMoreIDsException;
import org.marketcetera.event.HasFIXMessage;
import org.marketcetera.quickfix.FIXDataDictionary;
import org.marketcetera.quickfix.FIXMessageUtil;
import org.marketcetera.quickfix.SystemFIXMessageFactory;
import org.marketcetera.util.log.I18NBoundMessage1P;
import org.marketcetera.util.log.I18NBoundMessage2P;
import org.marketcetera.util.misc.ClassVersion;
import quickfix.Field;
import quickfix.FieldNotFound;
import quickfix.Message;
import quickfix.StringField;
import quickfix.field.BeginString;
/* $License$ */
/**
* Factory for creating various messages.
*
* @author anshul@marketcetera.com
* @version $Id: FactoryImpl.java 16888 2014-04-22 18:32:36Z colin $
* @since 1.0.0
*/
@ClassVersion("$Id: FactoryImpl.java 16888 2014-04-22 18:32:36Z colin $")
class FactoryImpl
extends Factory
{
@Override
public OrderSingle createOrderSingle() {
OrderSingleImpl order = new OrderSingleImpl();
assignOrderID(order);
return order;
}
@Override
public OrderSingleSuggestion createOrderSingleSuggestion() {
return new OrderSingleSuggestionImpl();
}
@Override
public OrderCancel createOrderCancel(ExecutionReport inLatestReport) {
OrderCancelImpl order = new OrderCancelImpl();
assignOrderID(order);
if (inLatestReport != null) {
order.setAccount(inLatestReport.getAccount());
order.setBrokerID(inLatestReport.getBrokerID());
order.setBrokerOrderID(inLatestReport.getBrokerOrderID());
order.setQuantity(inLatestReport.getOrderQuantity());
order.setSide(inLatestReport.getSide());
order.setInstrument(inLatestReport.getInstrument());
order.setText(inLatestReport.getText());
addCustomFieldsFromReport(inLatestReport, order, SystemFIXMessageFactory.EXECUTION_REPORT_FIELDS);
// set this manually after the customFields are copied, so that we take the OrigClOrdId from the report itself,
// not from the custom field of the report
order.setOriginalOrderID(inLatestReport.getOrderID());
}
return order;
}
@Override
public OrderReplace createOrderReplace(ExecutionReport inLatestReport) {
OrderReplaceImpl order = new OrderReplaceImpl();
assignOrderID(order);
if (inLatestReport != null) {
order.setAccount(inLatestReport.getAccount());
order.setBrokerID(inLatestReport.getBrokerID());
order.setBrokerOrderID(inLatestReport.getBrokerOrderID());
order.setOrderType(inLatestReport.getOrderType());
order.setPrice(inLatestReport.getPrice());
order.setQuantity(inLatestReport.getOrderQuantity());
order.setDisplayQuantity(inLatestReport.getOrderDisplayQuantity());
order.setSide(inLatestReport.getSide());
order.setInstrument(inLatestReport.getInstrument());
order.setTimeInForce(inLatestReport.getTimeInForce());
order.setOrderCapacity(inLatestReport.getOrderCapacity());
order.setPositionEffect(inLatestReport.getPositionEffect());
order.setText(inLatestReport.getText());
addCustomFieldsFromReport(inLatestReport, order, SystemFIXMessageFactory.CANCEL_REPLACE_EXCLUSION_FIELDS);
// set this manually after the customFields are copied, so that we take the OrigClOrdId from the report itself,
// not from the custom field of the report
order.setOriginalOrderID(inLatestReport.getOrderID());
}
return order;
}
/** Extracts the FIX message from the incoming ER, pulls the custom fields out of it, and sets it on the specified order */
protected void addCustomFieldsFromReport(ExecutionReport inLatestReport, RelatedOrder order, Set<Integer> inExcludeFields) {
if(inLatestReport instanceof HasFIXMessage) {
Message msg = ((HasFIXMessage) inLatestReport).getMessage();
try {
order.setCustomFields(getFieldMap(msg, inExcludeFields));
} catch (MessageCreationException e) {
// ignore: unable to insert custom fields
}
}
}
@Override
public FIXOrder createOrder(Message inMessage, BrokerID inBrokerID)
throws MessageCreationException {
assignOrderID(inMessage);
return new FIXOrderImpl(inMessage, inBrokerID);
}
@Override
public ExecutionReport createExecutionReport(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
UserID inActorID,
UserID inViewerID)
throws MessageCreationException {
if(inMessage == null) {
throw new NullPointerException();
}
if(inOriginator == null) {
throw new NullPointerException();
}
if(FIXMessageUtil.isExecutionReport(inMessage)) {
return new ExecutionReportImpl(inMessage,
inBrokerID, inOriginator, inActorID, inViewerID);
} else {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_EXECUTION_REPORT, inMessage.toString()));
}
}
/* (non-Javadoc)
* @see org.marketcetera.trade.Factory#createExecutionReport(quickfix.Message, org.marketcetera.trade.BrokerID, org.marketcetera.trade.Originator, org.marketcetera.trade.Hierarchy, org.marketcetera.trade.UserID, org.marketcetera.trade.UserID)
*/
@Override
public ExecutionReport createExecutionReport(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
Hierarchy inHierarchy,
UserID inActorID,
UserID inViewerID)
throws MessageCreationException
{
if(inMessage == null) {
throw new NullPointerException();
}
if(inOriginator == null) {
throw new NullPointerException();
}
if(inHierarchy == null) {
throw new NullPointerException();
}
if(FIXMessageUtil.isExecutionReport(inMessage)) {
return new ExecutionReportImpl(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
} else {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_EXECUTION_REPORT, inMessage.toString()));
}
}
/* (non-Javadoc)
* @see org.marketcetera.trade.Factory#createOrderCancelReject(quickfix.Message, org.marketcetera.trade.BrokerID, org.marketcetera.trade.Originator, org.marketcetera.trade.Hierarchy, org.marketcetera.trade.UserID, org.marketcetera.trade.UserID)
*/
@Override
public OrderCancelReject createOrderCancelReject(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
Hierarchy inHierarchy,
UserID inActorID,
UserID inViewerID)
throws MessageCreationException
{
if(inMessage == null) {
throw new NullPointerException();
}
if(inOriginator == null) {
throw new NullPointerException();
}
if(inHierarchy == null) {
throw new NullPointerException();
}
if(FIXMessageUtil.isCancelReject(inMessage)) {
return new OrderCancelRejectImpl(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
} else {
throw new MessageCreationException(new I18NBoundMessage1P(Messages.NOT_CANCEL_REJECT,
inMessage.toString()));
}
}
@Override
public OrderCancelReject createOrderCancelReject(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
UserID inActorID,
UserID inViewerID)
throws MessageCreationException
{
if(inMessage == null) {
throw new NullPointerException();
}
if(inOriginator == null) {
throw new NullPointerException();
}
if(FIXMessageUtil.isCancelReject(inMessage)) {
return new OrderCancelRejectImpl(
inMessage, inBrokerID, inOriginator, inActorID, inViewerID);
} else {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_CANCEL_REJECT, inMessage.toString()));
}
}
/* (non-Javadoc)
* @see org.marketcetera.trade.Factory#createFIXResponse(quickfix.Message, org.marketcetera.trade.BrokerID, org.marketcetera.trade.Originator, org.marketcetera.trade.Hierarchy, org.marketcetera.trade.UserID, org.marketcetera.trade.UserID)
*/
@Override
public FIXResponse createFIXResponse(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
Hierarchy inHierarchy,
UserID inActorID,
UserID inViewerID)
{
if(inMessage == null) {
throw new NullPointerException();
}
if(inOriginator == null) {
throw new NullPointerException();
}
if(inHierarchy == null) {
throw new NullPointerException();
}
return new FIXResponseImpl(inMessage,
inBrokerID,
inOriginator,
inHierarchy,
inActorID,
inViewerID);
}
@Override
public FIXResponse createFIXResponse(Message inMessage,
BrokerID inBrokerID,
Originator inOriginator,
UserID inActorID,
UserID inViewerID)
{
if (inMessage==null) {
throw new NullPointerException();
}
if (inOriginator==null) {
throw new NullPointerException();
}
return new FIXResponseImpl(inMessage, inBrokerID, inOriginator,
inActorID, inViewerID);
}
@Override
public OrderSingle createOrderSingle(Message inMessage,
BrokerID inBrokerID)
throws MessageCreationException {
checkSystemMessage(inMessage);
if(!FIXMessageUtil.isOrderSingle(inMessage)) {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_SINGLE_ORDER, inMessage.toString()));
}
OrderSingle order = createOrderSingle();
order.setAccount(FIXUtil.getAccount(inMessage));
order.setBrokerID(inBrokerID);
order.setCustomFields(getFieldMap(inMessage, SystemFIXMessageFactory.ORDER_SINGLE_FIELDS));
order.setOrderID(FIXUtil.getOrderID(inMessage));
order.setOrderType(FIXUtil.getOrderType(inMessage));
order.setPrice(FIXUtil.getPrice(inMessage));
order.setQuantity(FIXUtil.getOrderQuantity(inMessage));
order.setDisplayQuantity(FIXUtil.getOrderDisplayQuantity(inMessage));
order.setSide(FIXUtil.getSide(inMessage));
order.setInstrument(FIXUtil.getInstrument(inMessage));
order.setTimeInForce(FIXUtil.getTimeInForce(inMessage));
order.setOrderCapacity(FIXUtil.getOrderCapacity(inMessage));
order.setPositionEffect(FIXUtil.getPositionEffect(inMessage));
order.setText(FIXUtil.getText(inMessage));
assignOrderID(order);
return order;
}
@Override
public OrderCancel createOrderCancel(Message inMessage,
BrokerID inBrokerID)
throws MessageCreationException {
checkSystemMessage(inMessage);
if(!FIXMessageUtil.isCancelRequest(inMessage)) {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_CANCEL_ORDER, inMessage.toString()));
}
OrderCancel order = new OrderCancelImpl();
order.setAccount(FIXUtil.getAccount(inMessage));
order.setBrokerID(inBrokerID);
order.setBrokerOrderID(FIXUtil.getBrokerOrderID(inMessage));
order.setCustomFields(getFieldMap(inMessage, SystemFIXMessageFactory.ORDER_CANCEL_FIELDS));
order.setOrderID(FIXUtil.getOrderID(inMessage));
order.setOriginalOrderID(FIXUtil.getOriginalOrderID(inMessage));
order.setQuantity(FIXUtil.getOrderQuantity(inMessage));
order.setSide(FIXUtil.getSide(inMessage));
order.setInstrument(FIXUtil.getInstrument(inMessage));
order.setText(FIXUtil.getText(inMessage));
assignOrderID(order);
return order;
}
@Override
public OrderReplace createOrderReplace(
Message inMessage,
BrokerID inBrokerID)
throws MessageCreationException {
checkSystemMessage(inMessage);
if(!FIXMessageUtil.isCancelReplaceRequest(inMessage)) {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NOT_CANCEL_REPLACE_ORDER, inMessage.toString()));
}
OrderReplace order = new OrderReplaceImpl();
order.setAccount(FIXUtil.getAccount(inMessage));
order.setBrokerID(inBrokerID);
order.setBrokerOrderID(FIXUtil.getBrokerOrderID(inMessage));
order.setCustomFields(getFieldMap(inMessage, SystemFIXMessageFactory.ORDER_REPLACE_FIELDS));
order.setOrderID(FIXUtil.getOrderID(inMessage));
order.setOrderType(FIXUtil.getOrderType(inMessage));
order.setOriginalOrderID(FIXUtil.getOriginalOrderID(inMessage));
order.setPrice(FIXUtil.getPrice(inMessage));
order.setQuantity(FIXUtil.getOrderQuantity(inMessage));
order.setDisplayQuantity(FIXUtil.getOrderDisplayQuantity(inMessage));
order.setSide(FIXUtil.getSide(inMessage));
order.setInstrument(FIXUtil.getInstrument(inMessage));
order.setTimeInForce(FIXUtil.getTimeInForce(inMessage));
order.setOrderCapacity(FIXUtil.getOrderCapacity(inMessage));
order.setPositionEffect(FIXUtil.getPositionEffect(inMessage));
order.setText(FIXUtil.getText(inMessage));
assignOrderID(order);
return order;
}
@Override
public void setOrderIDFactory(IDFactory inIDFactory) {
if(inIDFactory == null) {
throw new NullPointerException();
}
mIDFactory = inIDFactory;
}
/**
* Assigns a unique order ID to the supplied order.
*
* @param inOrder the order that needs to be assigned a unique order ID.
*/
private void assignOrderID(OrderBase inOrder) {
inOrder.setOrderID(new OrderID(getNextOrderID()));
}
/**
* Assigns a unique order ID to the supplied order.
*
* @param inMessage the order message that needs to be
* assigned a unique order ID.
*/
private void assignOrderID(Message inMessage) {
FIXUtil.setOrderID(inMessage, getNextOrderID());
}
/**
* Fetches the next orderID value from the ID factory.
*
* @return the next orderID value.
*
* @throws IllegalArgumentException if the ID factory was not
* configured correctly.
*/
private String getNextOrderID() throws IllegalArgumentException {
try {
return mIDFactory.getNext();
} catch (NoMoreIDsException e) {
//Indicates that id factories are not correctly assembled
//to prevent failure. The factory should not throw exceptions.
//In case it's unable to generate IDs, it should rely on a
//local, in-memory ID generator that cannot fail
Messages.UNABLE_TO_GENERATE_IDS.error(this, e);
throw new IllegalArgumentException(
Messages.UNABLE_TO_GENERATE_IDS.getText(), e);
}
}
private volatile IDFactory mIDFactory = new InMemoryIDFactory(
System.currentTimeMillis());
/**
* Returns all the fields contained in the supplied message as a map.
* The map has the field tag number as the key and the field string
* value as the value.
* <p>
* The returned map excludes the set of fields supplied in
* <code>inExcludeFields</code>.
* <p>
* Byte Fields are currently not supported. If the supplied message
* includes any byte fields, an exception will be thrown.
*
* @param inMessage The message whose fields need to be converted into
* the map.
* @param inExcludeFields The message fields that should not be included
* in the returned map.
*
* @return the map containing fields of the supplied message.
*
* @throws MessageCreationException if there were errors.
*/
static Map<String,String> getFieldMap(Message inMessage,
Set<Integer> inExcludeFields)
throws MessageCreationException {
Map<String,String> fields = new HashMap<String, String>();
Iterator<Field<?>> iterator = inMessage.iterator();
while(iterator.hasNext()) {
Field<?> f = iterator.next();
if(inExcludeFields != null && inExcludeFields.contains(f.getTag())) {
continue;
}
//all fix fields except the one's that contain binary data
//are stored as string fields. All of them can be safely
//retrieved as string fields.
if(f instanceof StringField) {
fields.put(String.valueOf(f.getTag()),
((StringField)f).getObject());
}
//ignore fields that contain binary data.
}
return fields.isEmpty()
? null
: fields;
}
/**
* Verify if the supplied message is a system FIX message, ie. it's
* not a regular FIX message. And that it does not contain any groups.
*
* @param inMessage the message to be verified. Cannot be null.
*
* @throws MessageCreationException if the message fails any of the checks.
*/
private void checkSystemMessage(Message inMessage)
throws MessageCreationException {
if(inMessage == null) {
throw new NullPointerException();
}
try {
//Verify that the message is a System FIX Message, not a
//regular FIX Message.
String beginString = inMessage.getHeader().getField(
new BeginString()).getValue();
if(!FIXDataDictionary.FIX_SYSTEM_BEGIN_STRING.equals(beginString)) {
throw new MessageCreationException(new I18NBoundMessage1P(
Messages.NON_SYSTEM_FIX_MESSAGE,beginString));
}
} catch (FieldNotFound inFieldNotFound) {
throw new MessageCreationException(inFieldNotFound,
new I18NBoundMessage1P(
Messages.SYSTEM_FIX_MESSAGE_NO_BEGIN_STRING,
inMessage.toString()));
}
//Verify that the message does not contain any groups as
//we do not support messages with groups.
Iterator<Integer> iterator = inMessage.groupKeyIterator();
if(iterator.hasNext()) {
throw new MessageCreationException(new I18NBoundMessage2P(
Messages.MESSAGE_HAS_GROUPS, iterator.next(),
inMessage.toString()));
}
}
}