package com.activequant.trading;
import org.apache.log4j.Logger;
import com.activequant.domainmodel.TimeStamp;
import com.activequant.domainmodel.trade.event.OrderAcceptedEvent;
import com.activequant.domainmodel.trade.event.OrderCancelSubmittedEvent;
import com.activequant.domainmodel.trade.event.OrderCancellationRejectedEvent;
import com.activequant.domainmodel.trade.event.OrderCancelledEvent;
import com.activequant.domainmodel.trade.event.OrderEvent;
import com.activequant.domainmodel.trade.event.OrderFillEvent;
import com.activequant.domainmodel.trade.event.OrderRejectedEvent;
import com.activequant.domainmodel.trade.event.OrderReplacedEvent;
import com.activequant.domainmodel.trade.event.OrderSubmittedEvent;
import com.activequant.domainmodel.trade.event.OrderUpdateRejectedEvent;
import com.activequant.domainmodel.trade.event.OrderUpdateSubmittedEvent;
import com.activequant.domainmodel.trade.order.LimitOrder;
import com.activequant.domainmodel.trade.order.MarketOrder;
import com.activequant.domainmodel.trade.order.Order;
import com.activequant.domainmodel.trade.order.OrderSide;
import com.activequant.domainmodel.trade.order.SingleLegOrder;
import com.activequant.domainmodel.trade.order.StopOrder;
import com.activequant.interfaces.trading.IOrderTracker;
import com.activequant.interfaces.transport.IPublisher;
import com.activequant.interfaces.transport.IReceiver;
import com.activequant.interfaces.utils.IEventListener;
import com.activequant.interfaces.utils.IEventSource;
import com.activequant.messages.AQMessages;
import com.activequant.messages.AQMessages.BaseMessage;
import com.activequant.messages.AQMessages.OrderRejected;
import com.activequant.messages.Marshaller;
import com.activequant.messages.MessageFactory2;
import com.activequant.utils.UniqueTimeStampGenerator;
import com.activequant.utils.events.Event;
import com.google.protobuf.InvalidProtocolBufferException;
/**
* not thread safe. permit access from one thread only.
*
* @author GhostRider
*
*/
public class TransportOrderTracker implements IOrderTracker {
private SingleLegOrder orderContainer;
private SingleLegOrder pendingOrderContainer;
private SingleLegOrder nextPendingOrderContainer;
private Event<OrderEvent> event = new Event<OrderEvent>();
UniqueTimeStampGenerator utsg = new UniqueTimeStampGenerator();
private Logger log = Logger.getLogger(TransportOrderTracker.class);
private int seqCounter = 0;
private OrderEvent lastState;
private String internalOrderId = "";
private String originalOrderId = "";
//
private boolean cancellationPending = false;
//
private boolean workingState = false;
private boolean terminalState = false;
private boolean submitted = false;
private MessageFactory2 messageFactory;
private IPublisher transportPublisher;
private IReceiver transportReceiver;
private Marshaller marshaller = new Marshaller();
public TransportOrderTracker(IPublisher publisher, IReceiver receiver,
SingleLegOrder order) {
this.transportPublisher = publisher;
this.transportReceiver = receiver;
// register the byte[] handler.
receiver.getRawEvent().addEventListener(new IEventListener<byte[]>() {
@Override
public void eventFired(byte[] arg0) {
handle(arg0);
}
});
this.orderContainer = order;
order.setOpenQuantity(order.getQuantity());
messageFactory = new MessageFactory2();
internalOrderId = order.getOrderId();
originalOrderId = order.getOrderId();
//
}
@Override
public Order getOrder() {
return orderContainer;
}
/**
* Called from outside and inside this class. Routes an event within this
* virtual machine (not inter-machine communication)
*
* @param oe
*/
public void fireEvent(OrderEvent oe) {
lastState = oe;
log.info("Received an order event: " + oe);
if (oe instanceof OrderSubmittedEvent) {
submitted = true;
workingState = false;
} else if (oe instanceof OrderRejectedEvent) {
terminalState = true;
workingState = false;
} else if (oe instanceof OrderCancelledEvent) {
terminalState = true;
workingState = false;
} else if (oe instanceof OrderReplacedEvent
|| oe instanceof OrderAcceptedEvent
|| oe instanceof OrderCancellationRejectedEvent
|| oe instanceof OrderUpdateRejectedEvent) {
// ok, order is working again.
workingState = true;
if (oe instanceof OrderReplacedEvent)
this.orderUpdateSucceeded();
else if (oe instanceof OrderUpdateRejectedEvent)
this.orderUpdateFailed();
//
if (oe instanceof OrderCancellationRejectedEvent)
cancellationPending = false;
// first check if there is a scheduled cancellation ...
checkPendingCancellation();
// then check if there is a pending order update.
// we also check if we are still in a working state, as there could
// have been a cancellation
// indeed!!!
if (workingState)
checkPendingOrderUpdate();
} else if (oe instanceof OrderCancelSubmittedEvent) {
// we have to wait for a response now, so mark as nonworking.
workingState = false;
} else if (oe instanceof OrderUpdateSubmittedEvent) {
// we have to wait for a response now, so mark as nonworking.
workingState = false;
} else if (oe instanceof OrderFillEvent) {
//
fill((OrderFillEvent) oe);
}
event.fire(oe);
}
@Override
public String getVenueAssignedId() {
// not using a venue assigned ID, as TT is reusing our IDs.
return orderContainer.getOrderId();
}
@Override
public void submit() {
if (submitted) {
log.info("order has been submitted already.");
return;
}
if (terminalState) {
log.warn("Order is in terminal state, cannot resubmit.");
return;
}
Order o = orderContainer;
String tradInstId = orderContainer.getTradInstId();
BaseMessage bm = null;
if (o instanceof MarketOrder) {
MarketOrder mo = (MarketOrder) o;
bm = messageFactory.orderMktOrder(o.getOrderId(), tradInstId,
mo.getQuantity(), mo.getOrderSide(), 0);
} else if (o instanceof LimitOrder) {
log.info("Submitting limit order:" + o.toString());
LimitOrder lo = (LimitOrder) o;
bm = messageFactory.orderLimitOrder(o.getOrderId(), tradInstId,
lo.getQuantity(), lo.getLimitPrice(), lo.getOrderSide(), 0);
} else if (o instanceof StopOrder) {
StopOrder so = (StopOrder) o;
bm = messageFactory.orderStopOrder(o.getOrderId(), tradInstId,
so.getQuantity(), so.getStopPrice(), so.getOrderSide(), 0);
}
if (bm != null) {
try {
transportPublisher.send(bm.toByteArray());
} catch (Exception e) {
log.warn("Error while sending message: ", e);
}
}
OrderSubmittedEvent ose = new OrderSubmittedEvent();
ose.setTimeStamp(new TimeStamp());
//
fireEvent(ose);
}
//
// private void processOrderUpdateQueue(){
// if(nextPendingOrderContainer!=null){
//
// }
// }
@Override
/**
* Functionality is limited to updating limit order price and limit order size.
*
* If another order is pending, this order is scheduled for being executed AFTER the current
* order update is done.
*
* There is no queue of order updates, just ONE pending order update plus one next order update.
*
* Meaning, if you update a hundred times, but the first order update is not through yet while you update 100 times, 98 of those
* updates are dropped and only the last update will go through. (good for HFT)
*
*/
public void update(Order o) {
if (terminalState) {
log.info("Cannot update order as order is in terminal state.");
fireEvent(new OrderUpdateRejectedEvent());
return;
}
log.info("Updating order:" + orderContainer.toString() + " to "
+ o.toString());
if (!workingState) {
log.info("Cannot update order as order is not in a working state, setting it as pending.");
// mark for update.
nextPendingOrderContainer = (SingleLegOrder) o;
return;
}
String originalClOrdId = internalOrderId; // this.orderContainer.getOrderId();
String updateid = "UPDT:" + originalClOrdId + ":" + seqCounter;
if (originalClOrdId.startsWith("UPDT:")) {
updateid = "UPDT:" + originalClOrdId.split(":")[1] + ":"
+ seqCounter;
}
seqCounter++;
if (o.getClass().equals(orderContainer.getClass())) {
//
//
String tradInstId = orderContainer.getTradInstId();
BaseMessage bm = null;
if (o instanceof LimitOrder) {
LimitOrder lo = (LimitOrder) o;
bm = messageFactory.updateLimitOrder(updateid, originalClOrdId,
tradInstId, lo.getQuantity(), lo.getLimitPrice(),
lo.getOrderSide());
} else if (o instanceof MarketOrder) {
MarketOrder mo = (MarketOrder) o;
bm = messageFactory.updateMktOrder(updateid, originalClOrdId,
tradInstId, mo.getQuantity(), mo.getOrderSide());
} else if (o instanceof StopOrder) {
StopOrder so = (StopOrder) o;
bm = messageFactory.updateStopOrder(updateid, originalClOrdId,
tradInstId, so.getQuantity(), so.getStopPrice(),
so.getOrderSide());
}
if (bm != null) {
try {
log.info("Sending order update for " + o.toString());
pendingOrderContainer = (SingleLegOrder) o;
pendingOrderContainer.setOrderId(updateid);
transportPublisher.send(bm.toByteArray());
// finally notify the event listeners.
fireEvent(new OrderUpdateSubmittedEvent());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//
} else {
log.warn("Cannot update order with a different type.");
fireEvent(new OrderUpdateRejectedEvent());
}
}
private void fill(OrderFillEvent oe) {
if (oe.getLeftQuantity() == 0.0 && oe.getResend()==0) {
// fully done.
workingState = false;
terminalState = true;
} else {
// let's update the open quantity ...
SingleLegOrder slo = this.orderContainer;
slo.setOpenQuantity(oe.getLeftQuantity());
}
}
private void orderUpdateSucceeded() {
// making the new order the current order.
internalOrderId = this.pendingOrderContainer.getOrderId();
this.pendingOrderContainer.setOrderId(originalOrderId);
pendingOrderContainer.setOpenQuantity(pendingOrderContainer
.getQuantity()
- (orderContainer.getQuantity() - orderContainer
.getOpenQuantity()));
this.orderContainer = pendingOrderContainer;
pendingOrderContainer = null;
}
private void orderUpdateFailed() {
// making the new order the current order.
pendingOrderContainer = null;
}
private void checkPendingOrderUpdate() {
// check if there is a next pending order container.
if (nextPendingOrderContainer != null) {
update(nextPendingOrderContainer);
nextPendingOrderContainer = null;
}
}
/**
* checks if there is a cancellation pending and if so, it send it.
*/
private void checkPendingCancellation() {
if (cancellationPending) {
sendCancel();
}
}
/**
* Cancels an order on the event bus.
*
*/
@Override
public void cancel() {
if (terminalState) {
log.info("Cannot cancel order as order is in terminal state.");
fireEvent(new OrderCancellationRejectedEvent());
return;
} else if (!workingState) {
log.info("Cannot cancel order as order is not in a working state (but waiting for response), marking order as to be cancelled. ");
cancellationPending = true;
return;
} else {
sendCancel();
}
}
private void sendCancel() {
// ok, we are working out a pending cancellation.
cancellationPending = false;
//
log.info("Cancellation called for " + internalOrderId + ". "
+ lastState);
//
String reqId = "CNCL:" + originalOrderId + ":" + seqCounter;
seqCounter++;
String tradInstId = orderContainer.getTradInstId();
log.info("Sending cancel with cancel reqId:" + reqId);
BaseMessage bm = messageFactory.OrderCancelRequest(reqId,
internalOrderId, tradInstId, orderContainer.getOrderSide(),
orderContainer.getQuantity());
if (bm != null) {
try {
log.info("Sending cancellation: " + orderContainer.toString());
transportPublisher.send(bm.toByteArray());
fireEvent(new OrderCancelSubmittedEvent());
} catch (Exception e) {
// DIRTY.
log.warn("Exception!!", e);
fireEvent(new OrderCancellationRejectedEvent());
}
}
}
@Override
public IEventSource<OrderEvent> getOrderEventSource() {
return event;
}
public OrderEvent lastState() {
return lastState;
}
public SingleLegOrder getPendingOrder() {
return pendingOrderContainer;
}
/**
* Handles incoming raw byte messages. Rohes Fleisch in ihrer reinsten Form.
* Mit Terijaki Sauce.
*
* @param rawMessage
*/
private void handle(byte[] rawMessage) {
if (log.isDebugEnabled())
log.debug("Handling raw byte message. ");
//
AQMessages.BaseMessage bm;
try {
bm = marshaller.demarshall(rawMessage);
System.out.println(bm);
switch (bm.getType()) {
case SECURITY_STATUS: {
AQMessages.SecurityStatus os = ((AQMessages.SecurityStatus) bm
.getExtension(AQMessages.SecurityStatus.cmd));
// handle(os);
break;
}
case ORD_SUBMITTED:
log.info("Order submitted.");
AQMessages.OrderSubmitted os = ((AQMessages.OrderSubmitted) bm
.getExtension(AQMessages.OrderSubmitted.cmd));
handle(os);
break;
case ORD_ACCPTD:
log.info("Order accepted.");
AQMessages.OrderAccepted oa = ((AQMessages.OrderAccepted) bm
.getExtension(AQMessages.OrderAccepted.cmd));
handle(oa);
break;
case ORD_CANCELLED:
log.info("Order cancelled.");
AQMessages.OrderCancelled oc = ((AQMessages.OrderCancelled) bm
.getExtension(AQMessages.OrderCancelled.cmd));
handle(oc);
break;
case ORD_CNCL_REJ: {
log.info("Order cancellation rejected.");
AQMessages.OrderCancelReject ocr = ((AQMessages.OrderCancelReject) bm
.getExtension(AQMessages.OrderCancelReject.cmd));
handle(ocr);
break;
}
case ORD_UPD_REJECTED: {
log.info("Order update rejected.");
AQMessages.OrderUpdateRejected ocr = ((AQMessages.OrderUpdateRejected) bm
.getExtension(AQMessages.OrderUpdateRejected.cmd));
handle(ocr);
break;
}
case ORD_UPDATE_SUBMITTED: {
log.info("Order update submitted.");
AQMessages.OrderUpdateSubmitted ocr = ((AQMessages.OrderUpdateSubmitted) bm
.getExtension(AQMessages.OrderUpdateSubmitted.cmd));
handle(ocr);
break;
}
case ORD_UPDATED:
log.info("Order updated.");
AQMessages.OrderUpdated ou = ((AQMessages.OrderUpdated) bm
.getExtension(AQMessages.OrderUpdated.cmd));
handle(ou);
break;
case ORD_REJ:
log.info("Order rejected.");
AQMessages.OrderRejected or = ((AQMessages.OrderRejected) bm
.getExtension(AQMessages.OrderRejected.cmd));
handle(or);
break;
case EXECUTION_REPORT2:
AQMessages.ExecutionReport2 er = ((AQMessages.ExecutionReport2) bm
.getExtension(AQMessages.ExecutionReport2.cmd));
handle(er);
break;
}
} catch (InvalidProtocolBufferException e) {
log.warn("Could not demarshall message.");
}
}
// private void handle(AQMessages.SecurityStatus sa) {
// //
// MarketOpen mo = new MarketOpen();
// mo.setTdiId(sa.getTdiId());
// mo.setText(sa.getStatus());
// this.marketStateEvent.fire(mo);
// }
private void handle(AQMessages.OrderSubmitted oa) {
// should do something about UPD messages.
String ordId = oa.getClOrdId();
if (ordId.startsWith("UPDT")) {
ordId = ordId.split(":")[1];
}
// let's check if this order event is for us.
if (ordId.equals(this.originalOrderId)) {
OrderSubmittedEvent oae = new OrderSubmittedEvent();
oae.setTimeStamp(new TimeStamp());
oae.setRefOrderId(ordId);
oae.setRefOrder(this.getOrder());
fireEvent(oae);
}
}
private void handle(OrderRejected or) {
String orderId = or.getClOrdId();
if (orderId.startsWith("UPDT:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
if (orderId.equals(originalOrderId)) {
// ok, it's for us.
log.info("Order rejected: " + or.getClOrdId());
OrderRejectedEvent oae = new OrderRejectedEvent();
oae.setTimeStamp(new TimeStamp());
oae.setRefOrderId(orderId);
oae.setRefOrder(getOrder());
oae.setReason(or.getReason());
fireEvent(oae);
}
}
private void handle(AQMessages.OrderAccepted oa) {
String orderId = oa.getClOrdId();
if (orderId.equals(originalOrderId)) {
log.info("Order accepted: " + oa.getClOrdId());
OrderAcceptedEvent oae = new OrderAcceptedEvent();
oae.setTimeStamp(new TimeStamp());
oae.setRefOrderId(oa.getClOrdId());
oae.setRefOrder(getOrder());
fireEvent(oae);
}
}
private void handle(AQMessages.OrderUpdateRejected oa) {
String orderId = oa.getClOrdId();
if (orderId.equals(originalOrderId)) {
log.info("Order update rejected: " + oa.getClOrdId());
OrderUpdateRejectedEvent oae = new OrderUpdateRejectedEvent();
oae.setTimeStamp(new TimeStamp());
oae.setRefOrderId(oa.getClOrdId());
oae.setRefOrder(getOrder());
oae.setReason(oa.getReason());
fireEvent(oae);
}
}
/**
*
* Translates wire execution reports to internal OrderFillEvents.
*
* @param er
*/
private void handle(AQMessages.ExecutionReport2 er) {
String orderId = er.getClOrdId();
if (orderId.startsWith("UPDT:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
if (orderId.equals(originalOrderId)) {
log.info("Execution report: " + er.getClOrdId());
OrderFillEvent ofe = new OrderFillEvent();
Double cumQty = er.getQty();
Double avgPx = er.getPrice();
String side = er.getSide();
ofe.setSide(OrderSide.valueOf(side));
ofe.setTimeStamp(new TimeStamp());
ofe.setLeftQuantity(er.getQuantityLeft());
ofe.setFillPrice(er.getPrice());
ofe.setFillAmount(cumQty);
ofe.setOptionalInstId(er.getTdiId());
ofe.setRefOrderId(orderId);
ofe.setRefOrder(getOrder());
ofe.setExecId(er.getExecId());
ofe.setResend(er.getResend());
fireEvent(ofe);
}
}
private void handle(AQMessages.OrderCancelled oc) {
// have to extract the central order id.
String orderId = oc.getClOrdId();
if (orderId.startsWith("CNCL:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
//
if (orderId.equals(originalOrderId)) {
log.info("Cancel received for " + orderId);
//
OrderCancelledEvent oce = new OrderCancelledEvent();
oce.setTimeStamp(new TimeStamp());
oce.setRefOrderId(orderId);
oce.setRefOrder(getOrder());
fireEvent(oce);
} else {
log.info("Could not find order tracker " + oc.getClOrdId());
}
}
private void handle(AQMessages.OrderUpdated ou) {
log.info("Order updated: " + ou.getClOrdId());
String orderId = ou.getClOrdId();
if (orderId.startsWith("UPDT:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
if (orderId.equals(originalOrderId)) {
//
OrderReplacedEvent ore = new OrderReplacedEvent();
ore.setTimeStamp(new TimeStamp());
ore.setRefOrderId(orderId);
ore.setRefOrder(getPendingOrder());
fireEvent(ore);
}
}
private void handle(AQMessages.OrderCancelReject ocr) {
String orderId = ocr.getClOrdId();
if (orderId.startsWith("CNCL:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
if (orderId.equals(originalOrderId)) {
//
log.info("Order cancellation rejected of " + ocr.getClOrdId());
OrderCancellationRejectedEvent oce = new OrderCancellationRejectedEvent();
oce.setTimeStamp(new TimeStamp());
oce.setReason(ocr.getClxRejReason());
oce.setRefOrderId(orderId);
oce.setRefOrder(getOrder());
fireEvent(oce);
}
}
private void handle(AQMessages.OrderUpdateSubmitted ocr) {
String orderId = ocr.getClOrdId();
if (orderId.startsWith("UPDT:")) {
// alright, let's split it.
orderId = orderId.split(":")[1];
}
if (orderId.equals(originalOrderId)) {
//
OrderUpdateSubmittedEvent oce = new OrderUpdateSubmittedEvent();
oce.setTimeStamp(new TimeStamp());
oce.setRefOrderId(orderId);
oce.setRefOrder(getOrder());
fireEvent(oce);
}
}
public boolean isCancellationPending() {
return cancellationPending;
}
public boolean isInTerminalState() {
return terminalState;
}
}