package sample; import org.marketcetera.strategy.java.Strategy; import org.marketcetera.trade.*; import java.util.Random; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; import java.security.SecureRandom; import java.math.BigDecimal; /* $License$ */ /** * Given parameters indicating symbol and size, slices the total size into * smaller chunks and sends market orders off at random times. * * @author anshul@marketcetera.com * @version $Id: OrderSlicer.java 16154 2012-07-14 16:34:05Z colin $ * @since 2.0.0 */ public class OrderSlicer extends Strategy { /** * Executed when the strategy is started. * Use this method to set up data flows * and other initialization tasks. */ @Override public void onStart() { String symbol = getParameter("symbol"); String qty = getParameter("quantity"); if (symbol == null || qty == null) { String msg = "Please specify the 'symbol' and/or 'quantity' parameters (right-click on registered Strategy and go to Properties)"; error(msg); notifyHigh("Strategy missing parameters", msg); //throw an exception to prevent the strategy from starting. throw new IllegalArgumentException(msg); } int quantity = Integer.parseInt(qty); info("Partioning " + quantity + " " + symbol); List<Integer> partition; //if it's more than 100 shares, do partitioning in "round lots" if (quantity > 100) { // Figure out if we have a odd lot int oddQty = quantity % 100; quantity = quantity / 100; partition = generatePartition(quantity, 100); if (oddQty > 0) { partition.add(oddQty); } } else { partition = generatePartition(quantity, 1); } mNumPartitions = partition.size(); //Output the partitioning info("Partitions: " + partition.toString()); //Generate order objects from partition sizes for(int size: partition) { OrderSingle order = Factory.getInstance().createOrderSingle(); order.setOrderType(OrderType.Market); order.setQuantity(new BigDecimal(size)); order.setSide(Side.Buy); order.setInstrument(new Equity(symbol)); order.setTimeInForce(TimeInForce.Day); // request a callback for each order at a random time (up to 10 seconds) requestCallbackAfter(1000 * sRandom.nextInt(10), order); } } /** * Executed when the strategy receives a callback requested via * {@link #requestCallbackAt(java.util.Date, Object)} or * {@link #requestCallbackAfter(long, Object)}. All timer * callbacks come with the data supplied when requesting callback, * as an argument. * * @param inData the callback data */ @Override public void onCallback(Object inData) { send(inData); int sent = mNumSent.incrementAndGet(); info("sent order " + sent + "/" + mNumPartitions); } /** * Generate random partitions of the given quantity. Multiplies each * partition with the supplied multiple. * * @param inQuantity the quantity to be partitioned. * @param inMultiple the multiple to be applied to each partition. * * @return the list of partitions. */ private static List<Integer> generatePartition(int inQuantity, int inMultiple) { List<Integer> list = new ArrayList<Integer>(); while (inQuantity > 0) { int split = sRandom.nextInt(inQuantity) + 1; list.add(split * inMultiple); inQuantity -= split; } Collections.shuffle(list, sRandom); return list; } private volatile int mNumPartitions; private final AtomicInteger mNumSent = new AtomicInteger(0); private final static Random sRandom = new SecureRandom(); }