/* ===========================================================
* TradeManager : An application to trade strategies for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Project Info: org.trade
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Oracle, Inc.
* in the United States and other countries.]
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Original Author: Simon Allen;
* Contributor(s): -;
*
* Changes
* -------
*
*/
package org.trade.strategy;
import java.time.ZonedDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.trade.broker.BrokerModel;
import org.trade.core.util.CoreUtils;
import org.trade.core.util.TradingCalendar;
import org.trade.core.valuetype.Money;
import org.trade.dictionary.valuetype.Action;
import org.trade.dictionary.valuetype.OrderStatus;
import org.trade.dictionary.valuetype.Side;
import org.trade.dictionary.valuetype.TradestrategyStatus;
import org.trade.persistent.dao.Entrylimit;
import org.trade.persistent.dao.TradeOrder;
import org.trade.strategy.data.CandleSeries;
import org.trade.strategy.data.StrategyData;
import org.trade.strategy.data.candle.CandleItem;
/**
* @author Simon Allen
*
* @version $Revision: 1.0 $
*/
public class FiveMinGapBarStrategy extends AbstractStrategyRule {
/**
* 1/ Enter in the direction of the first 5min bar with a stop at the first
* 5min bars open. Use a STPLMT order with a range from the Entry Limit
* table.
*
* 2/ Bar must be within the Entry Limit table % Range Bar. i.e in this case
* 2%
*
* 3/ If the position is not filled by 10:30 cancel the order.
*
* 4/ Add 1c to the entry price and round over/under whole/half numbers in
* the direction of the trade, same for stop price.
*
* E.g. If first 5min bar is H=21.50 L=21.15 Open= 21.2 Close=21.40 then
* position order will be Buy STPLMT=21.51-21.55 (STPLMT range 0.04 from
* EntryLimit table and as we are at 21.5 we also buy over/under whole/half
* numbers), Quantity=Risk/(21.51-21.2) rounded to +/-100 shares (see
* EntryLimit table).
*
*/
private static final long serialVersionUID = -1373776942145894938L;
private final static Logger _log = LoggerFactory.getLogger(FiveMinGapBarStrategy.class);
private Integer openPositionOrderKey = null;
/**
* Default Constructor Note if you use class variables remember these will
* need to be initialized if the strategy is restarted i.e. if they are
* created on startup under a constraint you must find a way to populate
* that value if the strategy were to be restarted and the constraint is not
* met.
*
* @param brokerManagerModel
* BrokerModel
* @param strategyData
* StrategyData
* @param idTradestrategy
* Integer
*/
public FiveMinGapBarStrategy(BrokerModel brokerManagerModel, StrategyData strategyData, Integer idTradestrategy) {
super(brokerManagerModel, strategyData, idTradestrategy);
}
/**
* Method runStrategy.
*
* @param candleSeries
* CandleSeries
* @param newBar
* boolean
* @see org.trade.strategy.StrategyRule#runStrategy(CandleSeries, boolean)
*/
public void runStrategy(CandleSeries candleSeries, boolean newBar) {
try {
// Get the current candle
CandleItem currentCandleItem = this.getCurrentCandle();
// AbstractStrategyRule.logCandle(this,
// currentCandleItem.getCandle());
ZonedDateTime startPeriod = currentCandleItem.getPeriod().getStart();
/*
* Trade is open kill this Strategy as its job is done.
*/
if (this.isThereOpenPosition()) {
_log.info("Strategy complete open position filled symbol: " + getSymbol() + " startPeriod: "
+ startPeriod);
/*
* If the order is partial filled chaeck and if the risk goes
* beyond 1 risk unit cancel the openPositionOrder this will
* cause it to be marked as filled.
*/
if (OrderStatus.PARTIALFILLED.equals(this.getOpenPositionOrder().getStatus())) {
if (isRiskViolated(currentCandleItem.getClose(), this.getTradestrategy().getRiskAmount(),
this.getOpenPositionOrder().getQuantity(),
this.getOpenPositionOrder().getAverageFilledPrice())) {
this.cancelOrder(this.getOpenPositionOrder());
}
}
this.cancel();
return;
}
/*
* Open position order was cancelled kill this Strategy as its job
* is done.
*/
if (null != openPositionOrderKey && !this.getTradeOrder(openPositionOrderKey).isActive()) {
_log.info("Strategy complete open position cancelled symbol: " + getSymbol() + " startPeriod: "
+ startPeriod);
updateTradestrategyStatus(TradestrategyStatus.CANCELLED);
this.cancel();
return;
}
/*
* Is it the the 9:35 candle? and we have not created an open
* position trade.
*/
if (startPeriod.equals(this.getTradestrategy().getTradingday().getOpen()
.plusMinutes(this.getTradestrategy().getBarSize() / 60)) && newBar) {
/*
* Add the tails as a % of the body. 10% and vwap must be
* between O/C.
*/
CandleItem prevCandleItem = null;
if (getCurrentCandleCount() > 0) {
prevCandleItem = (CandleItem) candleSeries.getDataItem(getCurrentCandleCount() - 1);
// AbstractStrategyRule
// .logCandle(this, prevCandleItem.getCandle());
}
if (CoreUtils.isBetween(prevCandleItem.getOpen(), prevCandleItem.getClose(),
prevCandleItem.getVwap())) {
double barBodyPercent = (Math.abs(prevCandleItem.getOpen() - prevCandleItem.getClose())
/ Math.abs(prevCandleItem.getHigh() - prevCandleItem.getLow())) * 100;
if (barBodyPercent < 10) {
_log.info("Bar Body outside % range Symbol: " + getSymbol() + " Time: " + startPeriod);
updateTradestrategyStatus(TradestrategyStatus.NBB);
this.cancel();
return;
}
}
Side side = Side.newInstance(Side.SLD);
if (prevCandleItem.isSide(Side.BOT)) {
side = Side.newInstance(Side.BOT);
}
Money price = new Money(prevCandleItem.getHigh());
Money priceStop = new Money(prevCandleItem.getLow());
String action = Action.BUY;
if (Side.SLD.equals(side.getCode())) {
price = new Money(prevCandleItem.getLow());
priceStop = new Money(prevCandleItem.getHigh());
action = Action.SELL;
}
Money priceClose = new Money(prevCandleItem.getClose());
Entrylimit entrylimit = getEntryLimit().getValue(priceClose);
double highLowRange = Math.abs(prevCandleItem.getHigh() - prevCandleItem.getLow());
priceStop = new Money(prevCandleItem.getOpen());
// If the candle less than the entry limit %
if (((highLowRange) / prevCandleItem.getClose()) < entrylimit.getPercentOfPrice().doubleValue()) {
/*
* Check that the entry - stop is greater than 2* the STPLMT
* amount.
*/
// if (Math.abs(price.subtract(priceStop).doubleValue())
// > (entrylimit
// .getLimitAmount().doubleValue() * 2)) {
/*
* Create an open position.
*/
_log.info("We have a trade!! Symbol: " + getSymbol() + " Time: " + startPeriod);
TradeOrder tradeOrder = createRiskOpenPosition(action, price, priceStop, true, null, null, null,
null);
openPositionOrderKey = tradeOrder.getOrderKey();
// } else {
// _log.info("Rule 9:35 5min bar less than 2 * stop limits.
// Symbol: "
// + getSymbol() + " Time: " + startPeriod);
// updateTradestrategyStatus(TradestrategyStatus.NBB);
// // Kill this process we are done!
// this.cancel();
// }
} else {
_log.info("Rule 9:35 5min bar outside % limits. Symbol: " + getSymbol() + " Time: " + startPeriod);
updateTradestrategyStatus(TradestrategyStatus.PERCENT);
// Kill this process we are done!
this.cancel();
}
} else {
if (startPeriod.isBefore(this.getTradestrategy().getTradingday().getOpen().plusMinutes(120))
&& startPeriod.isAfter(this.getTradestrategy().getTradingday().getOpen().plusMinutes(5))) {
CandleItem firstCandle = this.getCandle(TradingCalendar.getDateAtTime(startPeriod,
this.getTradestrategy().getTradingday().getOpen()));
/*
* Check for 5 min H/L being broken in the opposite
* direction to the trade before position is opened.
*/
if (firstCandle.getSide()) {
if (currentCandleItem.getVwap() < firstCandle.getLow()) {
// updateTradestrategyStatus(TradestrategyStatus.FIVE_MIN_LOW_BROKEN);
// this.cancelAllOrders();
// // No trade we timed out
// _log.info("Rule 5min low broker Symbol: "
// + getSymbol() + " Time: " + startPeriod);
// this.cancel();
}
} else {
if (currentCandleItem.getVwap() > firstCandle.getHigh()) {
// updateTradestrategyStatus(TradestrategyStatus.FIVE_MIN_HIGH_BROKEN);
// this.cancelAllOrders();
// // No trade we timed out
// _log.info("Rule 5min high broker Symbol: "
// + getSymbol() + " Time: " + startPeriod);
// this.cancel();
}
}
}
}
if (!startPeriod.isBefore(this.getTradestrategy().getTradingday().getOpen().plusMinutes(120))) {
_log.info("Rule 11:30:00 bar, time out unfilled open position Symbol: " + getSymbol() + " Time: "
+ startPeriod);
if (!this.isThereOpenPosition()
&& !TradestrategyStatus.CANCELLED.equals(getTradestrategy().getStatus())) {
updateTradestrategyStatus(TradestrategyStatus.TO);
this.cancelAllOrders();
// No trade we timed out
_log.info("Rule 11:30:00 bar, time out unfilled open position Symbol: " + getSymbol() + " Time: "
+ startPeriod);
}
this.cancel();
}
} catch (StrategyRuleException ex) {
_log.error("Error runRule exception: " + ex.getMessage(), ex);
error(1, 10, "Error runRule exception: " + ex.getMessage());
}
}
}