diff --git a/src/main/java/ats/orders/ClientExtensions.java b/src/main/java/ats/orders/ClientExtensions.java new file mode 100644 index 0000000..a3d4232 --- /dev/null +++ b/src/main/java/ats/orders/ClientExtensions.java @@ -0,0 +1,26 @@ +package ats.orders; + +/** + * A ClientExtensions object allows a client to attach a clientID, tag + * and comment to Orders and Trades in their Account. Do not set, + * modify, or delete this field if your account is associated with + * MT4. + * + * @see OANDA API docs + */ +public class ClientExtensions { + /** + * The Client ID of the Order/Trade + */ + public String id; + + /** + * A tag associated with the Order/Trade + */ + public String tag; + + /** + * A comment associated with the Order/Trade + */ + public String comment; +} diff --git a/src/main/java/ats/orders/LimitOrderRequest.java b/src/main/java/ats/orders/LimitOrderRequest.java new file mode 100644 index 0000000..4812ce8 --- /dev/null +++ b/src/main/java/ats/orders/LimitOrderRequest.java @@ -0,0 +1,248 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new limit order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class LimitOrderRequest extends OrderRequest { + + /** + * Instrument to open the order on. Example: "EUR_USD" + */ + public String instrument; + + /** + * The quantity requested to be filled by the Order. A + * posititive number of units results in a long Order, and a + * negative number of units results in a short Order. + */ + public int units; + + /** + * The price threshold specified for the Limit Order. The Limit + * Order will only be filled by a market price that is equal to or + * better than this price. + */ + public String price; + + /** + * The time-in-force requested for the Order. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the Limit Order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of how Positions in the Account are modified when + * the Order is filled. + */ + public OrderPositionFill positionFill; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a String top Loss Order for a long + * trade valid values are “DEFAULT” and “BID”, and for short + * trades “DEFAULT” and “ASK” are valid. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + /** + * TakeProfitDetails specifies the details of a Take Profit Order + * to be created on behalf of a client. This may happen when an + * Order is filled that opens a Trade requiring a Take Profit, or + * when a Trade’s dependent Take Profit Order is modified directly + * through the Trade. + */ + public TakeProfitDetails takeProfitOnFill; + + /** + * StopLossDetails specifies the details of a Stop Loss Order to + * be created on behalf of a client. This may happen when an Order + * is filled that opens a Trade requiring a Stop Loss, or when a + * Trade’s dependent Stop Loss Order is modified directly through + * the Trade. + */ + public StopLossDetails stopLossOnFill; + + /** + * TrailingStopLossDetails specifies the details of a Trailing + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a + * Trailing Stop Loss, or when a Trade’s dependent Trailing Stop + * Loss Order is modified directly through the Trade. + */ + public TrailingStopLossDetails trailingStopLossOnFill; + + /** + * Client Extensions to add to the Trade created when the Order is + * filled (if such a Trade is created). Do not set, modify, or + * delete tradeClientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions tradeClientExtensions; + + + + /** + * Create a new limit order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the Limit + * Order. The Limit Order will only be filled by a market price + * that is equal to or better than this price. + */ + public LimitOrderRequest(String instrument, int units, String price) + { + this(instrument, units, price, null, null, null, + null, null, null, null, null, null); + } + + /** + * Create a new limit order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the Limit + * Order. The Limit Order will only be filled by a market price + * that is equal to or better than this price. + * + * @param timeInForce The time-in-force requested for the Limit + * Order. The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Limit Order will be cancelled + * on if timeInForce is GTD. + * + * @param positionFill Specification of how Positions in the Account + * are modified when the Order is filled. The default is + * OrderPositionFill.DEFAULT. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + * + * @param takeProfitOnFill TakeProfitDetails specifies the details of + * a Take Profit Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Take + * Profit, or when a Trade’s dependent Take Profit Order is modified + * directly through the Trade. + * + * @param stopLossOnFill StopLossDetails specifies the details of a + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Stop + * Loss, or when a Trade’s dependent Stop Loss Order is modified + * directly through the Trade. + * + * @param trailingStopLossOnFill TrailingStopLossDetails specifies the + * details of a Trailing Stop Loss Order to be created on behalf of a + * client. This may happen when an Order is filled that opens a Trade + * requiring a Trailing Stop Loss, or when a Trade’s dependent + * Trailing Stop Loss Order is modified directly through the Trade. + * + * @param tradeClientExtensions Client Extensions to add to the Trade + * created when the Order is filled (if such a Trade is created). Do + * not set, modify, or delete tradeClientExtensions if your account is + * associated with MT4. + */ + public LimitOrderRequest(String instrument, + int units, + String price, + TimeInForce timeInForce, + DateTime gtdTime, + OrderPositionFill positionFill, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions, + TakeProfitDetails takeProfitOnFill, + StopLossDetails stopLossOnFill, + TrailingStopLossDetails trailingStopLossOnFill, + ClientExtensions tradeClientExtensions) + { + super(OrderType.LIMIT); + + this.instrument = instrument; + this.units = units; + this.price = price; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.positionFill = positionFill; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + this.takeProfitOnFill = takeProfitOnFill; + this.stopLossOnFill = stopLossOnFill; + this.trailingStopLossOnFill = trailingStopLossOnFill; + this.tradeClientExtensions = tradeClientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.positionFill == null) + this.positionFill = OrderPositionFill.DEFAULT; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/MarketIfTouchedOrderRequest.java b/src/main/java/ats/orders/MarketIfTouchedOrderRequest.java new file mode 100644 index 0000000..9d1f020 --- /dev/null +++ b/src/main/java/ats/orders/MarketIfTouchedOrderRequest.java @@ -0,0 +1,272 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new market-if-touched order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class MarketIfTouchedOrderRequest extends OrderRequest { + + /** + * Instrument to open the order on. Example: "EUR_USD" + */ + public String instrument; + + /** + * The quantity requested to be filled by the order. A posititive + * number of units results in a long Order, and a negative number + * of units results in a short Order. + */ + public int units; + + /** + * The price threshold specified for the MarketIfTouched + * Order. The MarketIfTouched Order will only be filled by a + * market price that crosses this price from the direction of the + * market price at the time when the Order was created (the + * initialMarketPrice). Depending on the value of the Order’s + * price and initialMarketPrice, the MarketIfTouchedOrder will + * behave like a Limit or a Stop Order. + */ + public String price; + + /** + * The worst market price that may be used to fill this + * MarketIfTouched Order. + */ + public String priceBound; + + /** + * The time-in-force requested for the order. The default is + * TimeInForce.GTC. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of how Positions in the Account are modified when + * the Order is filled. + */ + public OrderPositionFill positionFill; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a String top Loss Order for a long + * trade valid values are “DEFAULT” and “BID”, and for short + * trades “DEFAULT” and “ASK” are valid. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + /** + * TakeProfitDetails specifies the details of a Take Profit Order + * to be created on behalf of a client. This may happen when an + * Order is filled that opens a Trade requiring a Take Profit, or + * when a Trade’s dependent Take Profit Order is modified directly + * through the Trade. + */ + public TakeProfitDetails takeProfitOnFill; + + /** + * StopLossDetails specifies the details of a Stop Loss Order to + * be created on behalf of a client. This may happen when an Order + * is filled that opens a Trade requiring a Stop Loss, or when a + * Trade’s dependent Stop Loss Order is modified directly through + * the Trade. + */ + public StopLossDetails stopLossOnFill; + + /** + * TrailingStopLossDetails specifies the details of a Trailing + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a + * Trailing Stop Loss, or when a Trade’s dependent Trailing Stop + * Loss Order is modified directly through the Trade. + */ + public TrailingStopLossDetails trailingStopLossOnFill; + + /** + * Client Extensions to add to the Trade created when the Order is + * filled (if such a Trade is created). Do not set, modify, or + * delete tradeClientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions tradeClientExtensions; + + + + /** + * Create a new stop order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the + * MarketIfTouched Order. The MarketIfTouched Order will only be + * filled by a market price that crosses this price from the + * direction of the market price at the time when the Order was + * created (the initialMarketPrice). Depending on the value of the + * Order’s price and initialMarketPrice, the MarketIfTouchedOrder + * will behave like a Limit or a Stop Order. + */ + public MarketIfTouchedOrderRequest(String instrument, int units, String price) + { + this(instrument, units, price, null, null, null, + null, null, null, null, null, null, null); + } + + /** + * Create a new stop order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the + * MarketIfTouched Order. The MarketIfTouched Order will only be + * filled by a market price that crosses this price from the + * direction of the market price at the time when the Order was + * created (the initialMarketPrice). Depending on the value of the + * Order’s price and initialMarketPrice, the MarketIfTouchedOrder + * will behave like a Limit or a Stop Order. + * + * @param priceBound The worst market price that may be used to + * fill this MarketIfTouched Order. + * + * @param timeInForce The time-in-force requested for the Order. + * The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Order will be cancelled on if + * timeInForce is GTD. + * + * @param positionFill Specification of how Positions in the Account + * are modified when the Order is filled. The default is + * OrderPositionFill.DEFAULT. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + * + * @param takeProfitOnFill TakeProfitDetails specifies the details of + * a Take Profit Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Take + * Profit, or when a Trade’s dependent Take Profit Order is modified + * directly through the Trade. + * + * @param stopLossOnFill StopLossDetails specifies the details of a + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Stop + * Loss, or when a Trade’s dependent Stop Loss Order is modified + * directly through the Trade. + * + * @param trailingStopLossOnFill TrailingStopLossDetails specifies the + * details of a Trailing Stop Loss Order to be created on behalf of a + * client. This may happen when an Order is filled that opens a Trade + * requiring a Trailing Stop Loss, or when a Trade’s dependent + * Trailing Stop Loss Order is modified directly through the Trade. + * + * @param tradeClientExtensions Client Extensions to add to the Trade + * created when the Order is filled (if such a Trade is created). Do + * not set, modify, or delete tradeClientExtensions if your account is + * associated with MT4. + */ + public MarketIfTouchedOrderRequest(String instrument, + int units, + String price, + String priceBound, + TimeInForce timeInForce, + DateTime gtdTime, + OrderPositionFill positionFill, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions, + TakeProfitDetails takeProfitOnFill, + StopLossDetails stopLossOnFill, + TrailingStopLossDetails trailingStopLossOnFill, + ClientExtensions tradeClientExtensions) + { + super(OrderType.MARKET_IF_TOUCHED); + + this.instrument = instrument; + this.units = units; + this.price = price; + this.priceBound = priceBound; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.positionFill = positionFill; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + this.takeProfitOnFill = takeProfitOnFill; + this.stopLossOnFill = stopLossOnFill; + this.trailingStopLossOnFill = trailingStopLossOnFill; + this.tradeClientExtensions = tradeClientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.positionFill == null) + this.positionFill = OrderPositionFill.DEFAULT; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/MarketOrderRequest.java b/src/main/java/ats/orders/MarketOrderRequest.java new file mode 100644 index 0000000..daff38a --- /dev/null +++ b/src/main/java/ats/orders/MarketOrderRequest.java @@ -0,0 +1,177 @@ +package ats.orders; + +/** + * Create a new market order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class MarketOrderRequest extends OrderRequest { + + /** + * Instrument to open the order on. Example: "EUR_USD" + */ + public String instrument; + + /** + * The quantity requested to be filled by the Market Order. A + * posititive number of units results in a long Order, and a + * negative number of units results in a short Order. + */ + public int units; + + /** + * The time-in-force requested for the Market Order. Restricted to + * FOK or IOC for a MarketOrder. + */ + public TimeInForce timeInForce; + + /** + * The worst price that the client is willing to have the Market + * Order filled at. + */ + public String priceBound; + + /** + * Specification of how Positions in the Account are modified when + * the Order is filled. + */ + public OrderPositionFill positionFill; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + /** + * TakeProfitDetails specifies the details of a Take Profit Order + * to be created on behalf of a client. This may happen when an + * Order is filled that opens a Trade requiring a Take Profit, or + * when a Trade’s dependent Take Profit Order is modified directly + * through the Trade. + */ + public TakeProfitDetails takeProfitOnFill; + + /** + * StopLossDetails specifies the details of a Stop Loss Order to + * be created on behalf of a client. This may happen when an Order + * is filled that opens a Trade requiring a Stop Loss, or when a + * Trade’s dependent Stop Loss Order is modified directly through + * the Trade. + */ + public StopLossDetails stopLossOnFill; + + /** + * TrailingStopLossDetails specifies the details of a Trailing + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a + * Trailing Stop Loss, or when a Trade’s dependent Trailing Stop + * Loss Order is modified directly through the Trade. + */ + public TrailingStopLossDetails trailingStopLossOnFill; + + /** + * Client Extensions to add to the Trade created when the Order is + * filled (if such a Trade is created). Do not set, modify, or + * delete tradeClientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions tradeClientExtensions; + + + /** + * Create a new market order request to send to the OANDA API. + * Use empty or default values for any unspecified properties. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the Market + * Order. A posititive number of units results in a long Order, and a + * negative number of units results in a short Order. + */ + public MarketOrderRequest(String instrument, int units) + { + this(instrument, units, null, null, null, null, null, null, null, null); + } + + /** + * Create a new market order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the Market + * Order. A posititive number of units results in a long Order, and a + * negative number of units results in a short Order. + * + * @param timeInForce The time-in-force requested for the Market + * Order. Restricted to FOK or IOC for a MarketOrder. The default is + * TimeInForce.FOK. + * + * @param priceBound The worst price that the client is willing to + * have the Market Order filled at. + * + * @param positionFill Specification of how Positions in the Account + * are modified when the Order is filled. The default is + * OrderPositionFill.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + * + * @param takeProfitOnFill TakeProfitDetails specifies the details of + * a Take Profit Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Take + * Profit, or when a Trade’s dependent Take Profit Order is modified + * directly through the Trade. + * + * @param stopLossOnFill StopLossDetails specifies the details of a + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Stop + * Loss, or when a Trade’s dependent Stop Loss Order is modified + * directly through the Trade. + * + * @param trailingStopLossOnFill TrailingStopLossDetails specifies the + * details of a Trailing Stop Loss Order to be created on behalf of a + * client. This may happen when an Order is filled that opens a Trade + * requiring a Trailing Stop Loss, or when a Trade’s dependent + * Trailing Stop Loss Order is modified directly through the Trade. + * + * @param tradeClientExtensions Client Extensions to add to the Trade + * created when the Order is filled (if such a Trade is created). Do + * not set, modify, or delete tradeClientExtensions if your account is + * associated with MT4. + */ + public MarketOrderRequest(String instrument, + int units, + TimeInForce timeInForce, + String priceBound, + OrderPositionFill positionFill, + ClientExtensions clientExtensions, + TakeProfitDetails takeProfitOnFill, + StopLossDetails stopLossOnFill, + TrailingStopLossDetails trailingStopLossOnFill, + ClientExtensions tradeClientExtensions) + { + super(OrderType.MARKET); + + this.instrument = instrument; + this.units = units; + this.timeInForce = timeInForce; + this.priceBound = priceBound; + this.positionFill = positionFill; + this.clientExtensions = clientExtensions; + this.takeProfitOnFill = takeProfitOnFill; + this.stopLossOnFill = stopLossOnFill; + this.trailingStopLossOnFill = trailingStopLossOnFill; + this.tradeClientExtensions = tradeClientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.FOK; + + if (this.positionFill == null) + this.positionFill = OrderPositionFill.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/OrderPositionFill.java b/src/main/java/ats/orders/OrderPositionFill.java new file mode 100644 index 0000000..b2b64bd --- /dev/null +++ b/src/main/java/ats/orders/OrderPositionFill.java @@ -0,0 +1,33 @@ +package ats.orders; + +/** + * Specification of how Positions in the Account are modified when the + * Order is filled. + * + * @see OANDA API docs + */ +public enum OrderPositionFill { + /** + * When the Order is filled, only allow Positions to be opened + * or extended. + */ + OPEN_ONLY, + + /** + * When the Order is filled, always fully reduce an existing + * Position before opening a new Position. + */ + REDUCE_FIRST, + + /** + * When the Order is filled, only reduce an existing Position. + */ + REDUCE_ONLY, + + /** + * When the Order is filled, use REDUCE_FIRST behaviour for + * non-client hedging Accounts, and OPEN_ONLY behaviour for + * client hedging Accounts. + */ + DEFAULT +}; diff --git a/src/main/java/ats/orders/OrderRequest.java b/src/main/java/ats/orders/OrderRequest.java new file mode 100644 index 0000000..dbc6ee1 --- /dev/null +++ b/src/main/java/ats/orders/OrderRequest.java @@ -0,0 +1,66 @@ +package ats.orders; + +import java.util.HashMap; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaModule; + +/** + * Base class for order requests to send to the OANDA API. + */ +public class OrderRequest { + /** + * The type of the Order to Create. + */ + public OrderType type; + + + /** + * Create an order. Subclasses only will call this. Not for user + * code. + */ + public OrderRequest(OrderType orderType) + { + this.type = orderType; + } + + /** + * Return all properties of the request. Subclasses should + * override to add their own properties to the parent class'. + */ + // public Map getOrderParams() { + // Map params = new HashMap<>(); + + // params.put("type", orderType.toString()); + + // return params; + // } + + /** + * Create a JSON representation of the order suitable for sending + * to the OANDA API. + * + * @see OANDA API docs + */ + public String toJSON() throws JsonProcessingException { + Map order = new HashMap<>(); + order.put("order", this); + return getJSONMapper().writeValueAsString(order); + } + + /** + * Return a new JSON object mapper suitable for serializing orders + * for the OANDA api. + */ + public static ObjectMapper getJSONMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new JodaModule()); + mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + mapper.setSerializationInclusion(Include.NON_NULL); + return mapper; + } +} diff --git a/src/main/java/ats/orders/OrderTriggerCondition.java b/src/main/java/ats/orders/OrderTriggerCondition.java new file mode 100644 index 0000000..7d5f2f7 --- /dev/null +++ b/src/main/java/ats/orders/OrderTriggerCondition.java @@ -0,0 +1,59 @@ +package ats.orders; + +/** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This allows + * Orders to be triggered based on the bid, ask, mid, default (ask for + * buy, bid for sell) or inverse (ask for sell, bid for buy) price + * depending on the desired behaviour. Orders are always filled using + * their default price component. + * + * This feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see it + * reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s trigger + * condition is set to the default value when indicating the distance + * from an Order’s trigger price, and will always provide the default + * trigger condition when creating or modifying an Order. + * + * A special restriction applies when creating a guaranteed Stop Loss + * Order. In this case the TriggerCondition value must either be + * “DEFAULT”, or the “natural” trigger side “DEFAULT” results in. So + * for a Stop Loss Order for a long trade valid values are “DEFAULT” + * and “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * + * @see OANDA API docs + */ +public enum OrderTriggerCondition { + + /** + * Trigger an Order the “natural” way: compare its price to the + * ask for long Orders and bid for short Orders. + */ + DEFAULT, + + /** + * Trigger an Order the opposite of the “natural” way: compare its + * price the bid for long Orders and ask for short Orders. + */ + INVERSE, + + /** + * Trigger an Order by comparing its price to the bid regardless + * of whether it is long or short. + */ + BID, + + /** + * Trigger an Order by comparing its price to the ask regardless + * of whether it is long or short. + */ + ASK, + + /** + * Trigger an Order by comparing its price to the midpoint + * regardless of whether it is long or short. + */ + MID +} diff --git a/src/main/java/ats/orders/OrderType.java b/src/main/java/ats/orders/OrderType.java new file mode 100644 index 0000000..00cf278 --- /dev/null +++ b/src/main/java/ats/orders/OrderType.java @@ -0,0 +1,17 @@ +package ats.orders; + +/** + * Order type. + * + * @see OANDA API docs + */ +public enum OrderType { + MARKET, + LIMIT, + STOP, + MARKET_IF_TOUCHED, + TAKE_PROFIT, + STOP_LOSS, + TRAILING_STOP_LOSS, + FIXED_PRICE +}; diff --git a/src/main/java/ats/orders/StopLossDetails.java b/src/main/java/ats/orders/StopLossDetails.java new file mode 100644 index 0000000..1ef57d2 --- /dev/null +++ b/src/main/java/ats/orders/StopLossDetails.java @@ -0,0 +1,57 @@ +package ats.orders; + +import java.math.BigDecimal; + +import org.joda.time.DateTime; + +/** + * StopLossDetails specifies the details of a Stop Loss Order to be + * created on behalf of a client. This may happen when an Order is + * filled that opens a Trade requiring a Stop Loss, or when a Trade’s + * dependent Stop Loss Order is modified directly through the Trade. + * + * @see OANDA API docs + */ +public class StopLossDetails { + + /** + * The price that the Stop Loss Order will be triggered + * at. Only one of the price and distance fields may be + * specified. + */ + public String price; + + /** + * Specifies the distance (in price units) from the Trade’s + * open price to use as the Stop Loss Order price. Only one of + * the distance and price fields may be specified. + */ + public BigDecimal distance; + + /** + * The time in force for the created Stop Loss Order. This may + * only be GTC, GTD or GFD. The default is GTC. + */ + public TimeInForce timeInForce = TimeInForce.GTC; + + /** + * The date when the Stop Loss Order will be cancelled on if + * timeInForce is GTD. + */ + public DateTime gtdTime; + + /** + * The Client Extensions to add to the Stop Loss Order when + * created. + */ + public ClientExtensions clientExtensions; + + /** + * Flag indicating that the price for the Stop Loss Order is + * guaranteed. The default value depends on the + * GuaranteedStopLossOrderMode of the account, if it is + * REQUIRED, the default will be true, for DISABLED or ENABLED + * the default is false. + */ + public boolean guaranteed; +} diff --git a/src/main/java/ats/orders/StopLossOrderRequest.java b/src/main/java/ats/orders/StopLossOrderRequest.java new file mode 100644 index 0000000..c95253f --- /dev/null +++ b/src/main/java/ats/orders/StopLossOrderRequest.java @@ -0,0 +1,185 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new stop loss order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class StopLossOrderRequest extends OrderRequest { + + /** + * The ID of the Trade to close when the price threshold is + * breached. + */ + public String tradeID; + + /** + * The client ID of the Trade to be closed when the price + * threshold is breached. + */ + public String clientTradeID; + + /** + * The price threshold specified for the Stop Loss Order. If the + * guaranteed flag is false, the associated Trade will be closed + * by a market price that is equal to or worse than this + * threshold. If the flag is true the associated Trade will be + * closed at this price. + */ + public String price; + + /** + * Specifies the distance (in price units) from the Account’s + * current price to use as the StopLoss Order price. If the Trade + * is short the Instrument’s bid price is used, and for long + * Trades the ask is used. + */ + public String distance; + + /** + * The time-in-force requested for the StopLoss Order. Restricted + * to “GTC”, “GFD” and “GTD” for StopLoss Orders. The default + * is TimeInForce.GTC. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a Stop Loss Order for a long trade + * valid values are “DEFAULT” and “BID”, and for short trades + * “DEFAULT” and “ASK” are valid. The default is + * OrderTriggerCondition.DEFAULT. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + + + /** + * Create a new stop loss order request to send to the OANDA API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param price The price threshold specified for the TakeProfit + * Order. The associated Trade will be closed by a market price + * that is equal to or better than this threshold. + */ + public StopLossOrderRequest(String tradeID, String clientTradeID, String price) + { + this(tradeID, clientTradeID, price, null, null, null, null, null); + } + + /** + * Create a new stop loss order request to send to the OANDA API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param price The price threshold specified for the TakeProfit + * Order. The associated Trade will be closed by a market price + * that is equal to or better than this threshold. + * + * @param distance Specifies the distance (in price units) from + * the Account’s current price to use as the Stop Loss Order + * price. If the Trade is short the Instrument’s bid price is + * used, and for long Trades the ask is used. + * + * @param timeInForce The time-in-force requested for the Order. + * Restricted to “GTC”, “GFD” and “GTD” for StopLoss Orders. + * The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Order will be cancelled on if + * timeInForce is GTD. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + */ + public StopLossOrderRequest(String tradeID, + String clientTradeID, + String price, + String distance, + TimeInForce timeInForce, + DateTime gtdTime, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions) + { + super(OrderType.STOP_LOSS); + + this.tradeID = tradeID; + this.clientTradeID = clientTradeID; + this.price = price; + this.distance = distance; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/StopOrderRequest.java b/src/main/java/ats/orders/StopOrderRequest.java new file mode 100644 index 0000000..ac0aea4 --- /dev/null +++ b/src/main/java/ats/orders/StopOrderRequest.java @@ -0,0 +1,264 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new stop order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class StopOrderRequest extends OrderRequest { + + /** + * Instrument to open the order on. Example: "EUR_USD" + */ + public String instrument; + + /** + * The quantity requested to be filled by the order. A + * posititive number of units results in a long Order, and a + * negative number of units results in a short Order. + */ + public int units; + + /** + * The price threshold specified for the order. The order will + * only be filled by a market price that is equal to or worse than + * this price. + */ + public String price; + + /** + * The worst market price that may be used to fill this Stop + * Order. If the market gaps and crosses through both the price + * and the priceBound, the Stop Order will be cancelled instead of + * being filled. + */ + public String priceBound; + + /** + * The time-in-force requested for the order. The default is + * TimeInForce.GTC. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of how Positions in the Account are modified when + * the Order is filled. + */ + public OrderPositionFill positionFill; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a String top Loss Order for a long + * trade valid values are “DEFAULT” and “BID”, and for short + * trades “DEFAULT” and “ASK” are valid. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + /** + * TakeProfitDetails specifies the details of a Take Profit Order + * to be created on behalf of a client. This may happen when an + * Order is filled that opens a Trade requiring a Take Profit, or + * when a Trade’s dependent Take Profit Order is modified directly + * through the Trade. + */ + public TakeProfitDetails takeProfitOnFill; + + /** + * StopLossDetails specifies the details of a Stop Loss Order to + * be created on behalf of a client. This may happen when an Order + * is filled that opens a Trade requiring a Stop Loss, or when a + * Trade’s dependent Stop Loss Order is modified directly through + * the Trade. + */ + public StopLossDetails stopLossOnFill; + + /** + * TrailingStopLossDetails specifies the details of a Trailing + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a + * Trailing Stop Loss, or when a Trade’s dependent Trailing Stop + * Loss Order is modified directly through the Trade. + */ + public TrailingStopLossDetails trailingStopLossOnFill; + + /** + * Client Extensions to add to the Trade created when the Order is + * filled (if such a Trade is created). Do not set, modify, or + * delete tradeClientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions tradeClientExtensions; + + + + /** + * Create a new stop order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the order. The + * order will only be filled by a market price that is equal to or + * worse than this price. + */ + public StopOrderRequest(String instrument, int units, String price) + { + this(instrument, units, price, null, null, null, + null, null, null, null, null, null, null); + } + + /** + * Create a new stop order request to send to the OANDA API. + * + * @param instrument Instrument to open the order on. + * Example: "EUR_USD" + * + * @param units The quantity requested to be filled by the + * Order. A posititive number of units results in a long Order, + * and a negative number of units results in a short Order. + * + * @param price The price threshold specified for the Order. The + * Order will only be filled by a market price that is equal to or + * worse than this price. + * + * @param priceBound The worst market price that may be used to + * fill this Stop Order. If the market gaps and crosses through + * both the price and the priceBound, the Stop Order will be + * cancelled instead of being filled. + * + * @param timeInForce The time-in-force requested for the Order. + * The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Order will be cancelled on if + * timeInForce is GTD. + * + * @param positionFill Specification of how Positions in the Account + * are modified when the Order is filled. The default is + * OrderPositionFill.DEFAULT. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + * + * @param takeProfitOnFill TakeProfitDetails specifies the details of + * a Take Profit Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Take + * Profit, or when a Trade’s dependent Take Profit Order is modified + * directly through the Trade. + * + * @param stopLossOnFill StopLossDetails specifies the details of a + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a Stop + * Loss, or when a Trade’s dependent Stop Loss Order is modified + * directly through the Trade. + * + * @param trailingStopLossOnFill TrailingStopLossDetails specifies the + * details of a Trailing Stop Loss Order to be created on behalf of a + * client. This may happen when an Order is filled that opens a Trade + * requiring a Trailing Stop Loss, or when a Trade’s dependent + * Trailing Stop Loss Order is modified directly through the Trade. + * + * @param tradeClientExtensions Client Extensions to add to the Trade + * created when the Order is filled (if such a Trade is created). Do + * not set, modify, or delete tradeClientExtensions if your account is + * associated with MT4. + */ + public StopOrderRequest(String instrument, + int units, + String price, + String priceBound, + TimeInForce timeInForce, + DateTime gtdTime, + OrderPositionFill positionFill, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions, + TakeProfitDetails takeProfitOnFill, + StopLossDetails stopLossOnFill, + TrailingStopLossDetails trailingStopLossOnFill, + ClientExtensions tradeClientExtensions) + { + super(OrderType.STOP); + + this.instrument = instrument; + this.units = units; + this.price = price; + this.priceBound = priceBound; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.positionFill = positionFill; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + this.takeProfitOnFill = takeProfitOnFill; + this.stopLossOnFill = stopLossOnFill; + this.trailingStopLossOnFill = trailingStopLossOnFill; + this.tradeClientExtensions = tradeClientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.positionFill == null) + this.positionFill = OrderPositionFill.DEFAULT; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/TakeProfitDetails.java b/src/main/java/ats/orders/TakeProfitDetails.java new file mode 100644 index 0000000..3cf2708 --- /dev/null +++ b/src/main/java/ats/orders/TakeProfitDetails.java @@ -0,0 +1,40 @@ +package ats.orders; + +import org.joda.time.DateTime; + +/** + * TakeProfitDetails specifies the details of a Take Profit Order to + * be created on behalf of a client. This may happen when an Order is + * filled that opens a Trade requiring a Take Profit, or when a + * Trade’s dependent Take Profit Order is modified directly through + * the Trade. + * + * @see OANDA API docs + */ +public class TakeProfitDetails { + + /** + * The price that the Take Profit Order will be triggered + * at. Only one of the price and distance fields may be + * specified. + */ + public String price; + + /** + * The time in force for the created Take Profit Order. This + * may only be GTC, GTD or GFD. Default is GTC. + */ + public TimeInForce timeInForce = TimeInForce.GTC; + + /** + * The date when the Take Profit Order will be cancelled on if + * timeInForce is GTD. + */ + public DateTime gtdTime; + + /** + * The Client Extensions to add to the Take Profit Order when + * created. + */ + public ClientExtensions clientExtensions; +} diff --git a/src/main/java/ats/orders/TakeProfitOrderRequest.java b/src/main/java/ats/orders/TakeProfitOrderRequest.java new file mode 100644 index 0000000..d93cdb9 --- /dev/null +++ b/src/main/java/ats/orders/TakeProfitOrderRequest.java @@ -0,0 +1,170 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new market-if-touched order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class TakeProfitOrderRequest extends OrderRequest { + + /** + * The ID of the Trade to close when the price threshold is + * breached. + */ + public String tradeID; + + /** + * The client ID of the Trade to be closed when the price + * threshold is breached. + */ + public String clientTradeID; + + /** + * The price threshold specified for the TakeProfit Order. The + * associated Trade will be closed by a market price that is equal + * to or better than this threshold. + */ + public String price; + + /** + * The time-in-force requested for the TakeProfit + * Order. Restricted to “GTC”, “GFD” and “GTD” for TakeProfit + * Orders. The default is TimeInForce.GTC. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a Stop Loss Order for a long trade + * valid values are “DEFAULT” and “BID”, and for short trades + * “DEFAULT” and “ASK” are valid. The default is + * OrderTriggerCondition.DEFAULT. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + + + /** + * Create a new take profit order request to send to the OANDA + * API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param price The price threshold specified for the TakeProfit + * Order. The associated Trade will be closed by a market price + * that is equal to or better than this threshold. + */ + public TakeProfitOrderRequest(String tradeID, String clientTradeID, String price) + { + this(tradeID, clientTradeID, price, null, null, null, null); + } + + /** + * Create a new take profit order request to send to the OANDA + * API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param price The price threshold specified for the TakeProfit + * Order. The associated Trade will be closed by a market price + * that is equal to or better than this threshold. + * + * @param timeInForce The time-in-force requested for the Order. + * Restricted to “GTC”, “GFD” and “GTD” for TakeProfit Orders. + * The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Order will be cancelled on if + * timeInForce is GTD. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + */ + public TakeProfitOrderRequest(String tradeID, + String clientTradeID, + String price, + TimeInForce timeInForce, + DateTime gtdTime, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions) + { + super(OrderType.TAKE_PROFIT); + + this.tradeID = tradeID; + this.clientTradeID = clientTradeID; + this.price = price; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/main/java/ats/orders/TimeInForce.java b/src/main/java/ats/orders/TimeInForce.java new file mode 100644 index 0000000..935dcf6 --- /dev/null +++ b/src/main/java/ats/orders/TimeInForce.java @@ -0,0 +1,35 @@ +package ats.orders; + +/** + * The time-in-force of an Order. TimeInForce describes how long + * an Order should remain pending before being automatically + * cancelled by the execution system. + * + * @see OANDA API docs + */ +public enum TimeInForce { + /** + * The Order is “Good unTil Cancelled” + */ + GTC, + + /** + * The Order is “Good unTil Date” and will be cancelled at the provided time + */ + GTD, + + /** + * The Order is “Good For Day” and will be cancelled at 5pm New York time + */ + GFD, + + /** + * The Order must be immediately “Filled Or Killed” + */ + FOK, + + /** + * The Order must be “Immediatedly paritally filled Or Cancelled” + */ + IOC +}; diff --git a/src/main/java/ats/orders/TrailingStopLossDetails.java b/src/main/java/ats/orders/TrailingStopLossDetails.java new file mode 100644 index 0000000..97f2421 --- /dev/null +++ b/src/main/java/ats/orders/TrailingStopLossDetails.java @@ -0,0 +1,41 @@ +package ats.orders; + +import java.math.BigDecimal; + +import org.joda.time.DateTime; + +/** + * TrailingStopLossDetails specifies the details of a Trailing + * Stop Loss Order to be created on behalf of a client. This may + * happen when an Order is filled that opens a Trade requiring a + * Trailing Stop Loss, or when a Trade’s dependent Trailing Stop + * Loss Order is modified directly through the Trade. + * + * @see OANDA API docs + */ +public class TrailingStopLossDetails { + /** + * The distance (in price units) from the Trade’s fill price + * that the Trailing Stop Loss Order will be triggered at. + */ + public BigDecimal distance; + + /** + * The time in force for the created Trailing Stop Loss + * Order. This may only be GTC, GTD or GFD. The default is + * GTC. + */ + public TimeInForce timeInForce = TimeInForce.GTC; + + /** + * The date when the Trailing Stop Loss Order will be + * cancelled on if timeInForce is GTD. + */ + public DateTime gtdTime; + + /** + * The Client Extensions to add to the Trailing Stop Loss Order when + * created. + */ + public ClientExtensions clientExtensions; +} diff --git a/src/main/java/ats/orders/TrailingStopLossOrderRequest.java b/src/main/java/ats/orders/TrailingStopLossOrderRequest.java new file mode 100644 index 0000000..0bbaf13 --- /dev/null +++ b/src/main/java/ats/orders/TrailingStopLossOrderRequest.java @@ -0,0 +1,166 @@ +package ats.orders; + +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; + +/** + * Create a new trailing stop loss order request to send to the OANDA API. + * + * @see OANDA API docs + */ +public class TrailingStopLossOrderRequest extends OrderRequest { + + /** + * The ID of the Trade to close when the price threshold is + * breached. + */ + public String tradeID; + + /** + * The client ID of the Trade to be closed when the price + * threshold is breached. + */ + public String clientTradeID; + + /** + * The price distance (in price units) specified for the + * TrailingStopLoss Order. + */ + public String distance; + + /** + * The time-in-force requested for the TrailingStopLoss + * Order. Restricted to “GTC”, “GFD” and “GTD” for + * TrailingStopLoss Orders. The default is TimeInForce.GTC. + */ + public TimeInForce timeInForce; + + /** + * The date/time when the order will be cancelled if its + * timeInForce is “GTD”. + */ + public DateTime gtdTime; + + /** + * Specification of which price component should be used when + * determining if an Order should be triggered and filled. This + * allows Orders to be triggered based on the bid, ask, mid, + * default (ask for buy, bid for sell) or inverse (ask for sell, + * bid for buy) price depending on the desired behaviour. Orders + * are always filled using their default price component. This + * feature is only provided through the REST API. Clients who + * choose to specify a non-default trigger condition will not see + * it reflected in any of OANDA’s proprietary or partner trading + * platforms, their transaction history or their account + * statements. OANDA platforms always assume that an Order’s + * trigger condition is set to the default value when indicating + * the distance from an Order’s trigger price, and will always + * provide the default trigger condition when creating or + * modifying an Order. A special restriction applies when creating + * a guaranteed Stop Loss Order. In this case the TriggerCondition + * value must either be “DEFAULT”, or the “natural” trigger side + * “DEFAULT” results in. So for a Stop Loss Order for a long trade + * valid values are “DEFAULT” and “BID”, and for short trades + * “DEFAULT” and “ASK” are valid. The default is + * OrderTriggerCondition.DEFAULT. + */ + public OrderTriggerCondition triggerCondition; + + /** + * The client extensions to add to the Order. Do not set, modify, + * or delete clientExtensions if your account is associated with + * MT4. + */ + public ClientExtensions clientExtensions; + + + + /** + * Create a new stop loss order request to send to the OANDA API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param price The price threshold specified for the TakeProfit + * Order. The associated Trade will be closed by a market price + * that is equal to or better than this threshold. + */ + public TrailingStopLossOrderRequest(String tradeID, String clientTradeID, String distance) + { + this(tradeID, clientTradeID, distance, null, null, null, null); + } + + /** + * Create a new stop loss order request to send to the OANDA API. + * + * @param tradeID The ID of the Trade to close when the price + * threshold is breached. + * + * @param clientTradeID The client ID of the Trade to be closed + * when the price threshold is breached. + * + * @param distance The price distance (in price units) specified + * for the TrailingStopLoss Order. + * + * @param timeInForce The time-in-force requested for the Order. + * Restricted to “GTC”, “GFD” and “GTD” for TrailingStopLoss Orders. + * The default is TimeInForce.GTC. + * + * @param gtdTime The date when the Order will be cancelled on if + * timeInForce is GTD. + * + * @param triggerCondition Specification of which price component + * should be used when determining if an Order should be triggered + * and filled. This allows Orders to be triggered based on the + * bid, ask, mid, default (ask for buy, bid for sell) or inverse + * (ask for sell, bid for buy) price depending on the desired + * behaviour. Orders are always filled using their default price + * component. This feature is only provided through the REST + * API. Clients who choose to specify a non-default trigger + * condition will not see it reflected in any of OANDA’s + * proprietary or partner trading platforms, their transaction + * history or their account statements. OANDA platforms always + * assume that an Order’s trigger condition is set to the default + * value when indicating the distance from an Order’s trigger + * price, and will always provide the default trigger condition + * when creating or modifying an Order. A special restriction + * applies when creating a guaranteed Stop Loss Order. In this + * case the TriggerCondition value must either be “DEFAULT”, or + * the “natural” trigger side “DEFAULT” results in. So for a Stop + * Loss Order for a long trade valid values are “DEFAULT” and + * “BID”, and for short trades “DEFAULT” and “ASK” are valid. + * The default is OrderTriggerCondition.DEFAULT. + * + * @param clientExtensions The client extensions to add to the + * Order. Do not set, modify, or delete clientExtensions if your + * account is associated with MT4. + */ + public TrailingStopLossOrderRequest(String tradeID, + String clientTradeID, + String distance, + TimeInForce timeInForce, + DateTime gtdTime, + OrderTriggerCondition triggerCondition, + ClientExtensions clientExtensions) + { + super(OrderType.TRAILING_STOP_LOSS); + + this.tradeID = tradeID; + this.clientTradeID = clientTradeID; + this.distance = distance; + this.timeInForce = timeInForce; + this.gtdTime = gtdTime; + this.triggerCondition = triggerCondition; + this.clientExtensions = clientExtensions; + + if (this.timeInForce == null) + this.timeInForce = TimeInForce.GTC; + + if (this.triggerCondition == null) + this.triggerCondition = OrderTriggerCondition.DEFAULT; + } +} diff --git a/src/test/java/ats/orders/MarketOrderRequestTest.java b/src/test/java/ats/orders/MarketOrderRequestTest.java new file mode 100644 index 0000000..4b6e6f5 --- /dev/null +++ b/src/test/java/ats/orders/MarketOrderRequestTest.java @@ -0,0 +1,64 @@ +package ats.orders; + +import java.math.BigDecimal; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.joda.time.DateTime; +import org.json.JSONException; +import org.junit.Test; + +/** + * MarketOrderRequestTest tests JSON format serialization of + * MarketOrderRequest objects. + */ +public class MarketOrderRequestTest extends OrderSerializationTestBase { + + + /** + * Test the simplest form, including default values. + */ + @Test + public void testToJSONSmall() + throws JsonProcessingException, JSONException + { + MarketOrderRequest obj = new MarketOrderRequest("EUR_USD", 34); + + String expected = "{\"type\":\"MARKET\",\"instrument\":\"EUR_USD\",\"units\":34,\"timeInForce\":\"FOK\",\"positionFill\":\"DEFAULT\"}"; + + doTest(obj, expected); + } + + /** + * Test a fully filled out object. + */ + @Test + public void testToJSONLarge() + throws JsonProcessingException, JSONException + { + TakeProfitDetails tpd = new TakeProfitDetails(); + tpd.price = "66.2"; + tpd.timeInForce = TimeInForce.GTD; + tpd.gtdTime = new DateTime("2010-10-10T14:15:16.000Z"); + + StopLossDetails sld = new StopLossDetails(); + sld.distance = new BigDecimal("22.7"); + sld.timeInForce = TimeInForce.IOC; + sld.gtdTime = new DateTime("2012-12-12T14:15:16.000Z"); + sld.guaranteed = true; + + TrailingStopLossDetails tsld = new TrailingStopLossDetails(); + tsld.distance = new BigDecimal("33.6"); + tsld.timeInForce = TimeInForce.IOC; + tsld.gtdTime = new DateTime("2013-3-13T14:15:16.000Z"); + + MarketOrderRequest obj = + new MarketOrderRequest("EUR_USD", 34, TimeInForce.IOC, + "99.44", OrderPositionFill.REDUCE_ONLY, + null, tpd, sld, tsld, null); + + String expected = "{\"stopLossOnFill\":{\"distance\":22.7,\"timeInForce\":\"IOC\",\"gtdTime\":\"2012-12-12T14:15:16.000Z\",\"guaranteed\":true},\"takeProfitOnFill\":{\"price\":\"66.2\",\"timeInForce\":\"GTD\",\"gtdTime\":\"2010-10-10T14:15:16.000Z\"},\"instrument\":\"EUR_USD\",\"units\":34,\"priceBound\":\"99.44\",\"type\":\"MARKET\",\"timeInForce\":\"IOC\",\"positionFill\":\"REDUCE_ONLY\",\"trailingStopLossOnFill\":{\"distance\":33.6,\"timeInForce\":\"IOC\",\"gtdTime\":\"2013-03-13T14:15:16.000Z\"}}"; + + doTest(obj, expected); + } +} diff --git a/src/test/java/ats/orders/OrderSerializationTestBase.java b/src/test/java/ats/orders/OrderSerializationTestBase.java new file mode 100644 index 0000000..8a80b37 --- /dev/null +++ b/src/test/java/ats/orders/OrderSerializationTestBase.java @@ -0,0 +1,42 @@ +package ats.orders; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.json.JSONException; +import org.skyscreamer.jsonassert.JSONAssert; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * OrderSerializationTestBase is a base class for testing JSON format + * serialization of order types for sending to the OANDA api. + */ +public class OrderSerializationTestBase { + private static final Logger log = LoggerFactory.getLogger(OrderSerializationTestBase.class); + + /** + * Test an object against its expected json representation. + */ + protected void doTest(Object obj, String expected) + throws JsonProcessingException, JSONException + { + doTest(obj, expected, false); + } + + /** + * Test an object against its expected json representation. + * + * @param verbose Log a little extra if true. + */ + protected void doTest(Object obj, String expected, boolean verbose) + throws JsonProcessingException, JSONException + { + ObjectMapper mapper = OrderRequest.getJSONMapper(); + String actual = mapper.writeValueAsString(obj); + + if (verbose) log.warn(actual); + + JSONAssert.assertEquals(expected, actual, true); + } +} diff --git a/src/test/java/ats/orders/StopLossDetailsTest.java b/src/test/java/ats/orders/StopLossDetailsTest.java new file mode 100644 index 0000000..72737c0 --- /dev/null +++ b/src/test/java/ats/orders/StopLossDetailsTest.java @@ -0,0 +1,53 @@ +package ats.orders; + +import java.math.BigDecimal; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.joda.time.DateTime; +import org.json.JSONException; +import org.junit.Test; + +/** + * StopLossDetailsTest tests JSON format serialization of + * StopLossDetails objects. + */ +public class StopLossDetailsTest extends OrderSerializationTestBase { + + + /** + * Test the simplest form, including default values. + */ + @Test + public void testToJSONSmall() + throws JsonProcessingException, JSONException + { + StopLossDetails obj = new StopLossDetails(); + obj.price = "12.34"; + + String expected = "{\"price\":\"12.34\",\"timeInForce\":\"GTC\",\"guaranteed\":false}"; + + doTest(obj, expected); + } + + /** + * Test a fully filled out object. + */ + @Test + public void testToJSONLarge() + throws JsonProcessingException, JSONException + { + StopLossDetails obj = new StopLossDetails(); + obj.distance = new BigDecimal("12.34"); + obj.timeInForce = TimeInForce.GFD; + obj.gtdTime = new DateTime("2000-09-17T18:02:52.957Z"); + + obj.clientExtensions = new ClientExtensions(); + obj.clientExtensions.id = "my id"; + obj.clientExtensions.tag = "my tag"; + obj.clientExtensions.comment = "my comment"; + + String expected = "{\"distance\":12.34,\"timeInForce\":\"GFD\",\"gtdTime\":\"2000-09-17T18:02:52.957Z\",\"clientExtensions\":{\"id\":\"my id\",\"tag\":\"my tag\",\"comment\":\"my comment\"},\"guaranteed\":false}"; + doTest(obj, expected); + } +} diff --git a/src/test/java/ats/orders/TakeProfitDetailsTest.java b/src/test/java/ats/orders/TakeProfitDetailsTest.java new file mode 100644 index 0000000..8a529a7 --- /dev/null +++ b/src/test/java/ats/orders/TakeProfitDetailsTest.java @@ -0,0 +1,46 @@ +package ats.orders; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.joda.time.DateTime; +import org.json.JSONException; +import org.junit.Test; + +/** + * TakeProfitDetailsTest tests JSON format serialization of + * TakeProfitDetails objects. + */ +public class TakeProfitDetailsTest extends OrderSerializationTestBase { + + + /** + * Test the simplest form, including default values. + */ + @Test + public void testToJSONSmall() throws JsonProcessingException, JSONException { + TakeProfitDetails obj = new TakeProfitDetails(); + obj.price = "12.34"; + + String expected = "{\"price\":\"12.34\",\"timeInForce\":\"GTC\"}"; + doTest(obj, expected); + } + + /** + * Test a fully filled out object. + */ + @Test + public void testToJSONLarge() throws JsonProcessingException, JSONException { + TakeProfitDetails obj = new TakeProfitDetails(); + obj.price = "12.34"; + obj.timeInForce = TimeInForce.GFD; + obj.gtdTime = new DateTime("2000-09-17T18:02:52.957Z"); + + obj.clientExtensions = new ClientExtensions(); + obj.clientExtensions.id = "my id"; + obj.clientExtensions.tag = "my tag"; + obj.clientExtensions.comment = "my comment"; + + String expected = "{\"price\":\"12.34\",\"timeInForce\":\"GFD\",\"gtdTime\":\"2000-09-17T18:02:52.957Z\",\"clientExtensions\":{\"id\":\"my id\",\"tag\":\"my tag\",\"comment\":\"my comment\"}}"; + doTest(obj, expected); + } +} diff --git a/src/test/java/ats/orders/TakeProfitOrderRequestTest.java b/src/test/java/ats/orders/TakeProfitOrderRequestTest.java new file mode 100644 index 0000000..fc4e8b2 --- /dev/null +++ b/src/test/java/ats/orders/TakeProfitOrderRequestTest.java @@ -0,0 +1,54 @@ +package ats.orders; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.oanda.v20.order.OrderTriggerCondition; + +import org.joda.time.DateTime; +import org.json.JSONException; +import org.junit.Test; + +/** + * TakeProfitOrderRequestTest tests JSON format serialization of + * TakeProfitOrderRequest objects. + */ +public class TakeProfitOrderRequestTest extends OrderSerializationTestBase { + + + /** + * Test the simplest form, including default values. + */ + @Test + public void testToJSONSmall() + throws JsonProcessingException, JSONException + { + TakeProfitOrderRequest obj = new TakeProfitOrderRequest("12", "23", "45.67"); + + String expected = "{\"type\":\"TAKE_PROFIT\",\"tradeID\":\"12\",\"clientTradeID\":\"23\",\"price\":\"45.67\",\"timeInForce\":\"GTC\",\"triggerCondition\":\"DEFAULT\"}"; + + doTest(obj, expected); + } + + /** + * Test a fully filled out object. + */ + @Test + public void testToJSONLarge() + throws JsonProcessingException, JSONException + { + ClientExtensions clientExtensions = new ClientExtensions(); + clientExtensions.id = "my id"; + clientExtensions.tag = "my tag"; + clientExtensions.comment = "my comment"; + + TakeProfitOrderRequest obj = new TakeProfitOrderRequest("12", "23", "45.67", + TimeInForce.IOC, + new DateTime("2000-09-17T18:02:52.957Z"), + OrderTriggerCondition.MID, + clientExtensions); + + + String expected = "{\"type\":\"TAKE_PROFIT\",\"tradeID\":\"12\",\"clientTradeID\":\"23\",\"price\":\"45.67\",\"timeInForce\":\"IOC\",\"gtdTime\":\"2000-09-17T18:02:52.957Z\",\"triggerCondition\":\"MID\",\"clientExtensions\":{\"id\":\"my id\",\"tag\":\"my tag\",\"comment\":\"my comment\"}}"; + + doTest(obj, expected); + } +} diff --git a/src/test/java/ats/orders/TrailingStopLossDetailsTest.java b/src/test/java/ats/orders/TrailingStopLossDetailsTest.java new file mode 100644 index 0000000..194cbaa --- /dev/null +++ b/src/test/java/ats/orders/TrailingStopLossDetailsTest.java @@ -0,0 +1,53 @@ +package ats.orders; + +import java.math.BigDecimal; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import org.joda.time.DateTime; +import org.json.JSONException; +import org.junit.Test; + +/** + * TrailingStopLossDetailsTest tests JSON format serialization of + * TrailingStopLossDetails objects. + */ +public class TrailingStopLossDetailsTest extends OrderSerializationTestBase { + + + /** + * Test the simplest form, including default values. + */ + @Test + public void testToJSONSmall() + throws JsonProcessingException, JSONException + { + TrailingStopLossDetails obj = new TrailingStopLossDetails(); + obj.distance = new BigDecimal("12.34"); + + String expected = "{\"distance\":12.34,\"timeInForce\":\"GTC\"}"; + + doTest(obj, expected); + } + + /** + * Test a fully filled out object. + */ + @Test + public void testToJSONLarge() + throws JsonProcessingException, JSONException + { + TrailingStopLossDetails obj = new TrailingStopLossDetails(); + obj.distance = new BigDecimal("12.34"); + obj.timeInForce = TimeInForce.GFD; + obj.gtdTime = new DateTime("2000-09-17T18:02:52.957Z"); + + obj.clientExtensions = new ClientExtensions(); + obj.clientExtensions.id = "my id"; + obj.clientExtensions.tag = "my tag"; + obj.clientExtensions.comment = "my comment"; + + String expected = "{\"distance\":12.34,\"timeInForce\":\"GFD\",\"gtdTime\":\"2000-09-17T18:02:52.957Z\",\"clientExtensions\":{\"id\":\"my id\",\"tag\":\"my tag\",\"comment\":\"my comment\"}}"; + doTest(obj, expected); + } +}