package org.juxtapose.fxtradingsystem.ordermanager;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import org.juxtapose.fxtradingsystem.constants.FXDataConstants;
import org.juxtapose.fxtradingsystem.constants.FXProducerServiceConstants;
import org.juxtapose.fxtradingsystem.priceengine.PriceEngineDataConstants;
import org.juxtapose.streamline.producer.ISTMEntryKey;
import org.juxtapose.streamline.producer.STMEntryProducer;
import org.juxtapose.streamline.stm.ISTM;
import org.juxtapose.streamline.stm.STMTransaction;
import org.juxtapose.streamline.tools.DataConstants;
import org.juxtapose.streamline.util.ISTMEntry;
import org.juxtapose.streamline.util.ISTMEntryRequestSubscriber;
import org.juxtapose.streamline.util.Status;
import org.juxtapose.streamline.util.data.DataTypeArrayList;
import org.juxtapose.streamline.util.data.DataTypeBigDecimal;
import org.juxtapose.streamline.util.data.DataTypeBoolean;
import org.juxtapose.streamline.util.data.DataTypeString;
public class RFQLiquidityProducer extends STMEntryProducer implements ISTMEntryRequestSubscriber
{
final static long priceTag = 0;
final String ccy1;
final String ccy2;
final String nearPeriod;
final String farPeriod;
final BigDecimal amt;
ISTMEntryKey subscribeKey;
public RFQLiquidityProducer( ISTMEntryKey inKey, ISTM inSTM, String inCcy1, String inCcy2, String inNearPeriod, String inFarPeriod, BigDecimal inAmt )
{
super( inKey, inSTM );
ccy1 = inCcy1;
ccy2 = inCcy2;
nearPeriod = inNearPeriod;
farPeriod = inFarPeriod;
amt = inAmt;
}
@Override
protected void start()
{
stm.commit( new STMTransaction( entryKey, RFQLiquidityProducer.this, 1, 0, true )
{
@Override
public void execute()
{
putValue( FXDataConstants.FIELD_CCY1, new DataTypeString( ccy1 ) );
putValue( FXDataConstants.FIELD_CCY2, new DataTypeString( ccy2 ) );
if( nearPeriod != null )
putValue( FXDataConstants.FIELD_NEAR_SWAP, new DataTypeString( nearPeriod ));
if( farPeriod != null )
putValue( FXDataConstants.FIELD_FAR_SWAP, new DataTypeString( farPeriod ));
}
});
HashMap<String, String> query = new HashMap<String, String>();
query.put( FXDataConstants.FIELD_INSTRUMENT, FXDataConstants.STATE_INSTRUMENT_SPOT );
query.put( FXDataConstants.FIELD_CCY1, ccy1 );
query.put( FXDataConstants.FIELD_CCY2, ccy2 );
query.put( FXDataConstants.FIELD_TYPE, PriceEngineDataConstants.STATE_TYPE_LIQUIDITY );
query.put( FXDataConstants.FIELD_SOURCE, "*" );
stm.getDataKey( FXProducerServiceConstants.AGGREGATOR, RFQLiquidityProducer.this, priceTag, query );
}
@Override
public void deliverKey( final ISTMEntryKey inDataKey, Object inTag)
{
if( inTag.equals( priceTag ))
{
subscribeKey = inDataKey;
stm.subscribeToData( inDataKey, this );
}
}
@Override
public void queryNotAvailible(Object inTag)
{
setStatus( Status.NA );
stm.logError( "could not retrieve datakey from price engine" );
return;
}
public void updateData( ISTMEntryKey inKey, final ISTMEntry inData, boolean inFullUpdate )
{
if( inData.getStatus() == Status.ON_REQUEST )
return;
stm.commit( new STMTransaction( entryKey, RFQLiquidityProducer.this, inFullUpdate )
{
@Override
public void execute()
{
DataTypeArrayList bidSide = (DataTypeArrayList)inData.getValue( FXDataConstants.FIELD_BID );
DataTypeArrayList askSide = (DataTypeArrayList)inData.getValue( FXDataConstants.FIELD_ASK );
if( bidSide == null || askSide == null )
{
dispose();
return;
}
BigDecimal bid = getBestExecutablePrice( bidSide, amt, true );
BigDecimal ask = getBestExecutablePrice( askSide, amt, false );
if( bid == null || ask == null )
{
dispose();
return;
}
putValue( FXDataConstants.FIELD_BID, new DataTypeBigDecimal( bid ) );
putValue( FXDataConstants.FIELD_ASK, new DataTypeBigDecimal( ask ) );
putValue( DataConstants.FIELD_TIMESTAMP, inData.getValue( DataConstants.FIELD_TIMESTAMP ) );
DataTypeBoolean priced = (DataTypeBoolean)get( FXDataConstants.FIELD_FIRST_UPDATE );
if( priced == null )
{
putValue( FXDataConstants.FIELD_FIRST_UPDATE, new DataTypeBoolean(true) );
}
else if( priced.get() )
{
putValue( FXDataConstants.FIELD_FIRST_UPDATE, new DataTypeBoolean(false) );
}
if( getStatus() == Status.ON_REQUEST )
{
setStatus( Status.OK );
}
}
} );
}
//static aggergator methods
private static BigDecimal getBestExecutablePrice( DataTypeArrayList inEntries, BigDecimal inAmt, boolean inBid )
{
BigDecimal workPrice = BigDecimal.ZERO;
BigDecimal amtLeft = inAmt;
boolean done = false;
for( int i = 0; i < inEntries.get().size(); i++ )
{
DataTypeArrayList arrayList = (DataTypeArrayList)inEntries.get().get(i);
BigDecimal price = ((DataTypeBigDecimal)arrayList.get().get( 0 )).get();
BigDecimal size = ((DataTypeBigDecimal)arrayList.get().get( 1 )).get();
if( size.compareTo( amtLeft ) > 0 )
{
BigDecimal multiplicator = price.multiply( amtLeft );
workPrice = workPrice.add( multiplicator );
done = true;
break;
}
else
{
BigDecimal multiplicator = price.multiply( size );
workPrice = workPrice.add( multiplicator );
amtLeft = amtLeft.subtract( size );
}
}
if( !done )
return null;
RoundingMode rm = inBid ? RoundingMode.FLOOR : RoundingMode.CEILING;
workPrice = workPrice.divide( inAmt, 4, rm );
return workPrice;
}
protected void stop()
{
super.stop();
stm.unsubscribeToData( subscribeKey, this );
}
}