package org.juxtapose.fxtradingsystem.ordermanager; import java.math.BigDecimal; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.juxtapose.fxtradingsystem.constants.FXDataConstants; import org.juxtapose.fxtradingsystem.constants.FXProducerServiceConstants; import org.juxtapose.streamline.producer.ISTMEntryKey; import org.juxtapose.streamline.producer.ISTMEntryProducer; import org.juxtapose.streamline.producer.ISTMEntryProducerService; import org.juxtapose.streamline.producer.executor.Executable; import org.juxtapose.streamline.producer.executor.IExecutor; import org.juxtapose.streamline.stm.osgi.DataProducerService; import org.juxtapose.streamline.tools.DataConstants; import org.juxtapose.streamline.tools.KeyConstants; import static org.juxtapose.streamline.tools.STMUtil.*; import org.juxtapose.streamline.util.ISTMEntryRequestSubscriber; import org.juxtapose.streamline.util.ISTMEntry; import org.juxtapose.streamline.util.Status; import org.juxtapose.streamline.util.data.DataType; import org.juxtapose.streamline.util.data.DataTypeBigDecimal; import org.juxtapose.streamline.util.data.DataTypeBoolean; import org.juxtapose.streamline.util.data.DataTypeRef; import org.juxtapose.streamline.util.data.DataTypeString; import org.juxtapose.streamline.util.producerservices.ProducerServiceConstants; import org.juxtapose.streamline.util.subscriber.DataSequencer; import org.juxtapose.streamline.util.subscriber.ISequencedDataSubscriber; /** * @author Pontus J�rgne * Feb 26, 2012 * Copyright (c) Pontus J�rgne. All rights reserved */ public class OrderManager extends DataProducerService implements IOrderManager, ISTMEntryProducerService, ISequencedDataSubscriber { volatile String priceKey = null; AtomicLong sequenceId = new AtomicLong(-1); final long spotPriceQueryTag = 1; ClientConnector connector; ConcurrentHashMap<Long, RFQContext> idToRFQProducer = new ConcurrentHashMap<Long, RFQContext>(512); boolean priceFromLiquidity = false; @Override public ISTMEntryProducer getDataProducer(ISTMEntryKey inDataKey) { if( FXDataConstants.STATE_TYPE_RFQ.equals( inDataKey.getType() )) { String val = inDataKey.getValue( FXDataConstants.FIELD_ID ); Long id = Long.parseLong( val ); RFQContext ctx = idToRFQProducer.get( id ); if( ctx == null ) { stm.logError("Could not find rfq producer for key "+inDataKey); return null; } return ctx.producer; } return null; } @Override public void updateData( ISTMEntryKey inKey, ISTMEntry inData, boolean inFullUpdate ) { if( inKey.equals( KeyConstants.PRODUCER_SERVICE_KEY )) { DataType<?> dataValue = inData.getValue( FXProducerServiceConstants.PRICE_ENGINE ); if( dataValue != null ) { System.out.println( "Price engine is registered with status: "+dataValue); if( dataValue.get() == Status.OK.toString() ) { connector = new ClientConnector( this ); } } else { System.out.println( "Price engine is not registered"); } } } @Override public String getServiceId() { return FXProducerServiceConstants.ORDER_MANAGER; } @Override public void dataUpdated(DataSequencer inSequencer) { ISTMEntry data = inSequencer.get(); processLiquidityData( data, inSequencer.getDataKey() ); } private void processData( ISTMEntry inData, ISTMEntryKey inKey) { Status status = inData.getStatus(); if( status == Status.OK ) { String idStr = inKey.getValue( FXDataConstants.FIELD_ID ); final Long id = Long.parseLong( idStr ); DataTypeRef priceRef = (DataTypeRef)inData.getValue( FXDataConstants.FIELD_PRICE ); final DataTypeBigDecimal bid = (DataTypeBigDecimal)priceRef.getReferenceData().getValue( FXDataConstants.FIELD_BID ); final DataTypeBigDecimal ask = (DataTypeBigDecimal)priceRef.getReferenceData().getValue( FXDataConstants.FIELD_ASK ); final DataTypeBigDecimal spread = (DataTypeBigDecimal)priceRef.getReferenceData().getValue( FXDataConstants.FIELD_SPREAD ); final DataTypeString ccy1 = (DataTypeString)inData.getValue( FXDataConstants.FIELD_CCY1 ); final DataTypeString ccy2 = (DataTypeString)inData.getValue( FXDataConstants.FIELD_CCY2 ); Long tou = (Long)priceRef.getReferenceData().getValue( DataConstants.FIELD_TIMESTAMP ).get(); final long sequence = inData.getSequenceID(); BigDecimal validateSpread = ask.get().subtract( bid.get() ); boolean valid = validateSpread.equals( spread.get() ); long now = System.nanoTime(); final long updateProcessingTime = now-tou; final Long firstTakeTime; DataTypeBoolean firstTake = (DataTypeBoolean)inData.getValue( FXDataConstants.FIELD_FIRST_UPDATE ); if( firstTake != null && firstTake.get() ) { RFQContext context = idToRFQProducer.get( id ); if( context != null ) { firstTakeTime = now - context.startTime; } else firstTakeTime = null; } else firstTakeTime = null; if( ! valid ) { System.err.println( "Price is not valid : "+validateSpread+" != "+spread.get() ); } else { // stm.execute( new Runnable(){ // // @Override // public void run() // { // if( firstTakeTime != null ) // System.out.println( "RoundTrip: "+firstTakeTime); // System.out.println( "Price is "+bid.get().toPlainString()+" / "+ask.get().toPlainString()+" sequence "+sequence+" updatetime: "+updateProcessingTime+" id: "+id ); // } // // }, IExecutor.LOW ); } // RFQMessage message = new RFQMessage( RFQMessage.TYPE_PRICING, ccy1.get(), ccy2.get(), id, bid.get().doubleValue(), ask.get().doubleValue(), firstTakeTime, updateProcessingTime, sequence ); // long start = System.nanoTime(); // connector.updateRFQ( message ); // long end = System.nanoTime(); // // System.err.println("Time it took for price update was: "+(end-start)+" nano"); } else { // /long sequence = inData.getSequenceID(); // System.out.println( "PriceStatus is "+status+" "+sequence+inData.getDataMap() ); } } private void processLiquidityData( ISTMEntry inData, ISTMEntryKey inKey ) { Status status = inData.getStatus(); if( status == Status.OK ) { String idStr = inKey.getValue( FXDataConstants.FIELD_ID ); final Long id = Long.parseLong( idStr ); final DataTypeBigDecimal bid = (DataTypeBigDecimal)inData.getValue( FXDataConstants.FIELD_BID ); final DataTypeBigDecimal ask = (DataTypeBigDecimal)inData.getValue( FXDataConstants.FIELD_ASK ); final DataTypeString ccy1 = (DataTypeString)inData.getValue( FXDataConstants.FIELD_CCY1 ); final DataTypeString ccy2 = (DataTypeString)inData.getValue( FXDataConstants.FIELD_CCY2 ); Long tou = (Long)inData.getValue( DataConstants.FIELD_TIMESTAMP ).get(); final long sequence = inData.getSequenceID(); long now = System.nanoTime(); final long updateProcessingTime = now-tou; final Long firstTakeTime; DataTypeBoolean firstTake = (DataTypeBoolean)inData.getValue( FXDataConstants.FIELD_FIRST_UPDATE ); if( firstTake != null && firstTake.get() ) { RFQContext context = idToRFQProducer.get( id ); if( context != null ) { firstTakeTime = now - context.startTime; } else firstTakeTime = null; } else firstTakeTime = null; RFQMessage message = new RFQMessage( RFQMessage.TYPE_PRICING, ccy1.get(), ccy2.get(), id, bid.get().doubleValue(), ask.get().doubleValue(), firstTakeTime, updateProcessingTime, sequence, BigDecimal.ONE ); connector.updateRFQ( message ); } } /** * @param inMessage */ public void sendRFQ( final RFQMessage inMessage ) { stm.execute( new Executable(){ @Override public void run() { long rfqID = inMessage.tag; String id = Long.toString( rfqID ); ISTMEntryKey key; RFQLiquidityProducer producer; if( inMessage.orderType.equals( FXDataConstants.STATE_INSTRUMENT_SPOT )) { key = createEntryKey( getServiceId(), FXDataConstants.STATE_TYPE_RFQ, new String[]{FXDataConstants.FIELD_ID, FXDataConstants.FIELD_CCY1, FXDataConstants.FIELD_CCY2}, new String[]{id, inMessage.ccy1, inMessage.ccy2 } ); producer = new RFQLiquidityProducer( key, stm, inMessage.ccy1, inMessage.ccy2, null, null, inMessage.amt ); } else if( inMessage.orderType.equals( FXDataConstants.STATE_INSTRUMENT_FWD )) { key = createEntryKey( getServiceId(), FXDataConstants.STATE_TYPE_RFQ, new String[]{FXDataConstants.FIELD_ID, FXDataConstants.FIELD_CCY1, FXDataConstants.FIELD_CCY2, FXDataConstants.FIELD_NEAR_SWAP}, new String[]{id, inMessage.ccy1, inMessage.ccy2, inMessage.nearDate } ); producer = new RFQLiquidityProducer( key, stm, inMessage.ccy1, inMessage.ccy2, inMessage.nearDate, null, inMessage.amt ); } else if( inMessage.orderType.equals( FXDataConstants.STATE_INSTRUMENT_SWAP )) { key = createEntryKey( getServiceId(), FXDataConstants.STATE_TYPE_RFQ, new String[]{FXDataConstants.FIELD_ID, FXDataConstants.FIELD_CCY1, FXDataConstants.FIELD_CCY2, FXDataConstants.FIELD_NEAR_SWAP, FXDataConstants.FIELD_FAR_SWAP}, new String[]{id, inMessage.ccy1, inMessage.ccy2, inMessage.nearDate, inMessage.farDate } ); producer = new RFQLiquidityProducer( key, stm, inMessage.ccy1, inMessage.ccy2, inMessage.nearDate, inMessage.farDate, inMessage.amt ); } else { stm.logError( "Invalid RFQ order type "+inMessage.orderType ); return; } DataSequencer seq = new DataSequencer( OrderManager.this, stm, key, IExecutor.HIGH ); RFQContext ctx = new RFQContext( seq, producer, System.nanoTime() ); idToRFQProducer.put( rfqID, ctx ); seq.start(); } @Override public void setHash(int inHash) { // TODO Auto-generated method stub } @Override public int getHash() { // TODO Auto-generated method stub return 0; } }, IExecutor.HIGH ); } /** * @param inMessage */ public void sendDR( final RFQMessage inMessage ) { stm.execute( new Executable(){ @Override public void run() { RFQContext ctx = idToRFQProducer.remove( inMessage.tag ); if( ctx != null ) { ctx.sequencer.stop(); } } }, IExecutor.HIGH ); } @Override public void getDataKey(ISTMEntryRequestSubscriber inSubscriber, Object inTag, Map<String, String> inQuery) { inSubscriber.queryNotAvailible( inTag ); } }