Develop Your System

This section is to discuss the key components in Cyan Spring framework and how to develop your system based on those components

Tutorial on strategy development

We will demonstrate how to develop a single-Instrument strategy based on Cyan Spring strategy framework in this page. For multi-instrument strategies, please refer to Dollar Neutral and Low High strategies implementation included in the software distribution.


Custom Strategy - Version One

Developing an aglorithmic trading strategy is never a trivial task. However, Cyan Spring Strategy Framework has made it a lot easier. In the coming example, we are going to develop a commonly used Single Instrument Strategy called Iceberg. You will need Java programming knowledge for this. Experience with Eclipse will help but not important.


The Requirements

Iceberg strategy is useful when you have a huge order and you do not want to show all the quantity in market. Iceberg strategy will control the the release of quantity to market bit by bit. The following are the business requirements:

  • Queue child order(s) at best bid price for buy or best ask price for sell, in display quantity.
  • If your child orders in market get partially filled or fully filled, send more quantity to market to maintain the display quantity
  • Child orders must stay within Limit Price specified by order if any

Writing The Codes

Assuming you have build the project successfully in Maven and your source code directory is c:\projects\algo, open the custom project, you will find 3 files:
	CustomStrategy1.java
	CustomStrategy1PriceAnalyzer.java
	CustomStrategy1QuantityAnalyzer.java
	

Open them up to take a look, they are pretty much empty. Next we are going to fill in some codes for CustomStrategy1PriceAnalyzer.java and CustomStrategy1QuantityAnalyzer.java.

For file: CustomStrategy1QuantityAnalyzer.java

	public class CustomStrategy1QuantityAnalyzer extends AbstractQuantityAnalyzer {

	@Override
	protected QuantityInstruction calculate(SingleInstrumentStrategy strategy) {
			ParentOrder order = strategy.getParentOrder();
			//retrieve display quantity
			Double disQty = order.get(Double.class, "Dis Qty");
			if(null == disQty)
				strategy.logError("Missing Dis Qty Parameter");
			
			//set passive quantity to display quantity
			QuantityInstruction qi = new QuantityInstruction();
			qi.setPassiveQty(disQty);
			return qi;
		}

	}
	

For file: CustomStrategy1PriceAnalyzer.java


	public class CustomStrategy1PriceAnalyzer extends AbstractPriceAnalyzer {

		@Override
		protected PriceInstruction calculate(QuantityInstruction qtyInstruction,
			SingleInstrumentStrategy strategy) {
			ParentOrder order = strategy.getParentOrder();
			Quote quote = strategy.getQuote();
			// setting the price to best bid for buy or best ask for sell
			double price = order.getSide().isBuy()?quote.getBid():quote.getAsk();
			PriceInstruction pi = new PriceInstruction();
			// taking the price and quantity we set and send to framework as
			// a PriceInstruction object
			pi.add(new PriceAllocation(order.getSymbol(), order.getSide(), price, qtyInstruction.getPassiveQty(), 
					ExchangeOrderType.LIMIT, strategy.getId()));
			return pi;
		}
	}
	
	

That's it! This is all you need to do. You don't get to see any order sending/receiving codes or any market data process codes because they are already taken care by the strategy framework.

XML configuration

After finishing writing the java codes, we will need to do some xml editing to tell the strategy framework how to load your strategy. we need to have a [class name].xml to put in /conf directory. In our case, the xml configuration file name is conf/CustomStrategy1.xml. You may load it up in your editor and change it to the following
	<bean id="customStrategy1" class="com.cyanspring.custom.strategy.CustomStrategy1" scope="prototype">
		<property name="strategyName" value="CUSTOM1"/>
		<property name="executionAnalyzer">
			<ref bean="defaultExecutionAnalyzer"/>
		</property>
		<property name="executionManager">
			<ref bean="defaultExecutionManager"/>
		</property>
		<property name="quantityAnalyzer">
			<bean class="com.cyanspring.custom.strategy.CustomStrategy1QuantityAnalyzer"></bean>	
		</property>
		<property name="priceAnalyzer">
			<bean class="com.cyanspring.custom.strategy.CustomStrategy1PriceAnalyzer"></bean>	
		</property>
		<property name="strategyFieldDefs">
	        <list>
            	<bean class="com.cyanspring.common.business.FieldDef">
            		<property name="name" value="Dis Qty"/>
            		<property name="type" value="java.lang.Double"/>
            		<property name="input" value="true"/>
            		<property name="amendable" value="true"/>
            		<property name="value" value="0"/>
				</bean>
	        </list>
		</property>
		
	</bean>
	
Some explanation on the strategyFieldDefs property. It takes a list of FieldDef objects and defines the strategy parameters:
  • name - name of the parameter
  • type - the type of the parameter
  • input - whether the parameter is compulsory on creating a new strategy
  • amendable - whether the parameter is amendable
  • value - the default value of it

Build/Deploy/Test

After finish coding, you may ran

mvn clean install

to build the custom project. When it is successfully built, take cyanspring-custom-1.53.jar from the target directory and copy it into your server/strategies directory. You should be able to enter a new single-instrument strategy with CUSTOM1 and test it with simulator.

Custom Strategy - Version Two

The vanilla Iceberg strategy that we developed in previous section is a relative passive strategy. It just sits there to wait for things happen. If market doesn't move towards us, it may become difficult to complete the order. In this section, we are going to enhance this strategy and make it a bit more aggressive.

The Requirements

We add the following in addition to version one requirements:

  • If display quantity doesnt get any fill during X(definable) seconds, strategy will send aggressive order to hit far touch
  • Aggressive order price is the best ask price for buy order or the best bid price for sell order
  • Aggressive order quantity is the minimum of display quantity and the best volum of far touch

Adding A Strategy Parameter

The the new feature of our strategy requires a new parameter. Adding a strategy parameter can be something tedious especially where there is a chain of handling across server and clients. The good news is that Cyan Spring strategy framework has already taken care of this. It is only a matter of changing a configuration file.

Please load up conf/CustomStrategy1.xml in your editor and add the new parameter "Agg interval" shown below

	<bean id="customStrategy1" class="com.cyanspring.custom.strategy.CustomStrategy1" scope="prototype">
		<property name="strategyName" value="CUSTOM1"/>
		<property name="executionAnalyzer">
			<ref bean="defaultExecutionAnalyzer"/>
		</property>
		<property name="executionManager">
			<ref bean="defaultExecutionManager"/>
		</property>
		<property name="quantityAnalyzer">
			<bean class="com.cyanspring.custom.strategy.CustomStrategy1QuantityAnalyzer"></bean>	
		</property>
		<property name="priceAnalyzer">
			<bean class="com.cyanspring.custom.strategy.CustomStrategy1PriceAnalyzer"></bean>	
		</property>
		<property name="strategyFieldDefs">
	        <list>
            	<bean class="com.cyanspring.common.business.FieldDef">
            		<property name="name" value="Dis Qty"/>
            		<property name="type" value="java.lang.Double"/>
            		<property name="input" value="true"/>
            		<property name="amendable" value="true"/>
            		<property name="value" value="0"/>
				</bean>
            	<bean class="com.cyanspring.common.business.FieldDef">
            		<property name="name" value="Agg interval"/>
            		<property name="type" value="java.lang.Long"/>
            		<property name="input" value="false"/>
            		<property name="amendable" value="true"/>
            		<property name="value" value="15"/>
				</bean>
	        </list>
		</property>
		
	</bean>
	

You may do a quick build and deploy on project custom to test it. Open up the order entering dialog, you should find a new parameter called "Agg interval" with default value of 15


Writing The Codes

There are a bit more handling codes for our new requirements but not really much. The strategy container already provides a timer mechanism, we only need to hook our logic in.

For file: CustomStrategy1.java

	public class CustomStrategy1 extends SingleInstrumentStrategy {
		private static final Logger log = LoggerFactory
				.getLogger(CustomStrategy1.class);
		
		
		@Override
		public void processAsyncTimerEvent(AsyncTimerEvent event) {
			Long interval = parentOrder.get(Long.class, "Agg interval");
			// only apply this logic when field "Agg interval" is set and not 0
			if(null != interval && interval > 0) {
				// the time when last fill came
				Date lastExecutionTime = this.getLastExecutionTime();
				long pass = Clock.getInstance().now().getTime() - lastExecutionTime.getTime();
				if(pass >= interval * 1000)
					// execute our logic now
					execute(ExecuteTiming.NOW);
			}
			
			super.processAsyncTimerEvent(event);
		}
	}
	

Some new codes are added for CustomStrategy1PriceAnalyzer.java and CustomStrategy1QuantityAnalyzer.java.

For file: CustomStrategy1QuantityAnalyzer.java

	public class CustomStrategy1QuantityAnalyzer extends AbstractQuantityAnalyzer {

		@Override
		protected QuantityInstruction calculate(SingleInstrumentStrategy strategy) {
			ParentOrder order = strategy.getParentOrder();
			// retrieve display quantity
			Double disQty = order.get(Double.class,	"Dis Qty");
			if (null == disQty)
				strategy.logError("Missing Dis Qty Parameter");

			// set passive quantity to display quantity
			QuantityInstruction qi = new QuantityInstruction();
			qi.setPassiveQty(disQty);

			// added for version two - work out aggressive quantity
			Long interval = order.get(Long.class, "Agg interval");
			// only apply this logic when field "Agg interval" is set and not 0
			if (null != interval && interval > 0) {
				// the time when last fill came
				Date lastExecutionTime = strategy.getLastExecutionTime();
				long pass = Clock.getInstance().now().getTime()
						- lastExecutionTime.getTime();
				if (pass >= interval * 1000)
					qi.setAggresiveQty(disQty);
			}

			return qi;
		}

	}
	

For file: CustomStrategy1PriceAnalyzer.java


	public class CustomStrategy1PriceAnalyzer extends AbstractPriceAnalyzer {

		@Override
		protected PriceInstruction calculate(QuantityInstruction qtyInstruction,
				SingleInstrumentStrategy strategy) {
			ParentOrder order = strategy.getParentOrder();
			Quote quote = strategy.getQuote();
			// setting the price to best bid for buy or best ask for sell
			double price = order.getSide().isBuy() ? quote.getBid() : quote
					.getAsk();
			PriceInstruction pi = new PriceInstruction(order.getSide());
			// taking the price and quantity we set and send to framework as
			// a PriceInstruction object
			pi.add(new PriceAllocation(order.getSymbol(), order.getSide(), price,
					qtyInstruction.getPassiveQty(), ExchangeOrderType.LIMIT));

			// added for version two - work out aggressive price for aggress quantity
			if (qtyInstruction.getAggresiveQty() > 0) {
				price = order.getSide().isBuy() ? quote.getAsk() : quote.getBid();
				double qty = order.getSide().isBuy() ? quote.getAskVol() : quote
						.getBidVol();
				// since we dont want to move market much, we only take out one
				// price level of
				// far touch
				qty = Math.min(qtyInstruction.getAggresiveQty(), qty);
				pi.add(new PriceAllocation(order.getSymbol(), order.getSide(),
						price, qty, ExchangeOrderType.LIMIT));
			}
			return pi;
		}

	}
	

Build/Deploy/Test

You may rebuild project custom with "mvn clean install" and deploy the jar to test the new functionality.

Summary

We hope the above sample programs give you some ideas of how to develop strategies by using Cyan Spring strategy framework. You may get help here if you hit any issues in completing this exercise. The following are the tips for some common problems/confusions:

  • Running several strategies together with the same stock that affects each other may cause confusion
  • You have a limit price specifed for you order which may prevent your strategy from doing things that you expect it to do
  • Since the demo version runs an embedded JMS broker(to save the hassle of running a seperate JMS broker), you will need to start server before starting CSTW.