前回までで一応成行注文に最低限欲しい機能は付けられましたので、今回は指値・逆指値注文用の画面を作ってみたいと思います。
一般的に指値・逆指値注文はレートを指定して注文するケースが多いかと思うんですが、個人的には今のレートから○○pips↑や↓に設定したいことが多いので、今回はそんな感じにします。
多くの国内業者では指値・逆指値注文を出す時に値幅制限があって、今のレートから2~3pips離れてないと注文が通らないなんてことがあるんですが、NDDのJForexを採用している業者ではそーゆー値幅制限がない点が個人的に気に入ってます。
例によってGUIのパーツはEclipseとWindowBuilderでちゃちゃっと作ってこんな感じにしました。
新規注文について
基本的にやってることは成行注文と大差ありません。
新規注文時のメソッドはIEngine.submitOrderで、OrderCommand引数が
- 成行買い注文のときはOrderCommand.BUY
- 成行売り注文のときはOrderCommand.SELL
- 指値買い注文のときはOrderCommand.BUYLIMIT
- 指値売り注文のときはOrderCommand.SELLLIMIT
- 逆指値買い注文のときはOrderCommand.BUYSTOP
- 逆指値売り注文のときはOrderCommand.SELLSTOP
になるだけです。
あと、ストラテジーからの注文時のみ
- BUYLIMIT_BYBID
- SELLLIMIT_BYASK
- BUYSTOP_BYBID
- SELLSTOP_BYASK
という注文方式も選べるんですが、使いみちは…どうなんでしょw
保留中の注文をキャンセルする
public class CancelOpenedOrders implements Callable<CancelOpenedOrders> { public CancelOpenedOrders call() throws JFException { Instrument instrument = (Instrument)cmbInstrument.getSelectedItem(); for (IOrder o: engine.getOrders(instrument)) { if (o.getState() == IOrder.State.OPENED) { o.setRequestedAmount(0); } } return this; } }
指値・逆指値注文は指定したレートに到達するまで待機されています。
待機注文はいつでもキャンセルが可能で、注文数量を0にすることでキャンセルとなります。
全コード
package jforex; import java.awt.Color; import java.awt.EventQueue; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Vector; import java.util.concurrent.Callable; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JSpinner.NumberEditor; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.TitledBorder; import com.dukascopy.api.IAccount; import com.dukascopy.api.IBar; import com.dukascopy.api.IConsole; import com.dukascopy.api.IContext; import com.dukascopy.api.IEngine; import com.dukascopy.api.IEngine.OrderCommand; import com.dukascopy.api.IMessage; import com.dukascopy.api.IOrder; import com.dukascopy.api.IStrategy; import com.dukascopy.api.ITick; import com.dukascopy.api.Instrument; import com.dukascopy.api.JFException; import com.dukascopy.api.Period; public class ConditionalOrder01 extends JFrame implements IStrategy { private IEngine engine; private IConsole console; private IContext context; private JButton btnBuyStop; private JButton btnSellLimit; private JLabel lblAmount; private JLabel lblAskT; private JLabel lblBidT; private JLabel lblStopLossPips; private JLabel lblTakeProfitPips; private JPanel pnlAmount; private JPanel pnlOrder; private JPanel pnlTpSlPips; private JSpinner spnAmount; private JSpinner spnStopLossPips; private JSpinner spnTakeProfitPips; private JComboBox cmbInstrument; private JButton btnCancelAll; private JLabel lblSlippagePips; private JSpinner spnSlippagePips; private JLabel lblSellLimit; private JLabel lblBuyStop; private JLabel lblSellStop; private JButton btnSellStop; private JButton btnBuyLimit; private JLabel lblBuyLimit; private JSpinner spnEntryMarginePips; private JLabel lblBid; private JLabel lblAsk; public ConditionalOrder01() { addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { try { onStop(); } catch (JFException e1) { e1.printStackTrace(); } } }); setResizable(false); setTitle("Conditional Order"); setBounds(100, 100, 340, 360); setLocationRelativeTo(null); GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[] {200}; gridBagLayout.rowHeights = new int[] {40, 60, 60}; getContentPane().setLayout(gridBagLayout); pnlAmount = new JPanel(); GridBagConstraints gbc_pnlAmount = new GridBagConstraints(); gbc_pnlAmount.insets = new Insets(0, 0, 5, 0); gbc_pnlAmount.fill = GridBagConstraints.BOTH; gbc_pnlAmount.gridx = 0; gbc_pnlAmount.gridy = 0; getContentPane().add(pnlAmount, gbc_pnlAmount); GridBagLayout gbl_pnlAmount = new GridBagLayout(); gbl_pnlAmount.rowHeights = new int[] {30}; gbl_pnlAmount.columnWidths = new int[] {80, 60, 60}; gbl_pnlAmount.columnWeights = new double[]{0.0, 1.0, 0.0}; pnlAmount.setLayout(gbl_pnlAmount); cmbInstrument = new JComboBox(getInstruments().toArray()); GridBagConstraints gbc_cmbInstrument = new GridBagConstraints(); gbc_cmbInstrument.fill = GridBagConstraints.BOTH; gbc_cmbInstrument.gridx = 0; gbc_cmbInstrument.gridy = 0; pnlAmount.add(cmbInstrument, gbc_cmbInstrument); lblAmount = new JLabel("Amount:"); lblAmount.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblAmount = new GridBagConstraints(); gbc_lblAmount.anchor = GridBagConstraints.EAST; gbc_lblAmount.gridx = 1; gbc_lblAmount.gridy = 0; gbc_lblAmount.fill = GridBagConstraints.BOTH; pnlAmount.add(lblAmount, gbc_lblAmount); SpinnerNumberModel snm; NumberEditor ne; snm = new SpinnerNumberModel(new Double(1), new Double(1), new Double(2500), new Double(0.1)); spnAmount = new JSpinner(snm); ne = new NumberEditor(spnAmount, "0.0"); spnAmount.setEditor(ne); GridBagConstraints gbc_spnAmount = new GridBagConstraints(); gbc_spnAmount.fill = GridBagConstraints.BOTH; gbc_spnAmount.gridx = 2; gbc_spnAmount.gridy = 0; pnlAmount.add(spnAmount, gbc_spnAmount); pnlOrder = new JPanel(); GridBagConstraints gbc_pnlOrder = new GridBagConstraints(); gbc_pnlOrder.insets = new Insets(0, 0, 5, 0); gbc_pnlOrder.fill = GridBagConstraints.BOTH; gbc_pnlOrder.gridx = 0; gbc_pnlOrder.gridy = 1; getContentPane().add(pnlOrder, gbc_pnlOrder); GridBagLayout gbl_pnlOrder = new GridBagLayout(); gbl_pnlOrder.columnWidths = new int[] {30, 80, 50, 80, 30}; gbl_pnlOrder.rowHeights = new int[] {30, 30, 30, 30, 30}; pnlOrder.setLayout(gbl_pnlOrder); lblBidT = new JLabel("Bid(SELL)"); lblBidT.setForeground(Color.RED); lblBidT.setHorizontalAlignment(SwingConstants.CENTER); GridBagConstraints gbc_lblBidT = new GridBagConstraints(); gbc_lblBidT.fill = GridBagConstraints.BOTH; gbc_lblBidT.gridx = 1; gbc_lblBidT.gridy = 0; pnlOrder.add(lblBidT, gbc_lblBidT); lblAskT = new JLabel("Ask(BUY)"); lblAskT.setForeground(Color.BLUE); lblAskT.setHorizontalAlignment(SwingConstants.CENTER); GridBagConstraints gbc_lblAskT = new GridBagConstraints(); gbc_lblAskT.fill = GridBagConstraints.BOTH; gbc_lblAskT.gridx = 3; gbc_lblAskT.gridy = 0; pnlOrder.add(lblAskT, gbc_lblAskT); btnSellLimit = new JButton(); btnSellLimit.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { JButton source = (JButton)e.getSource(); double price = Double.parseDouble(source.getText()); context.executeTask(new SubmitOrder(OrderCommand.SELLLIMIT, price)); } }); lblSellLimit = new JLabel("Limit"); lblSellLimit.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblSellLimit = new GridBagConstraints(); gbc_lblSellLimit.fill = GridBagConstraints.BOTH; gbc_lblSellLimit.gridx = 0; gbc_lblSellLimit.gridy = 1; pnlOrder.add(lblSellLimit, gbc_lblSellLimit); GridBagConstraints gbc_btnSellLimit = new GridBagConstraints(); gbc_btnSellLimit.fill = GridBagConstraints.BOTH; gbc_btnSellLimit.gridx = 1; gbc_btnSellLimit.gridy = 1; pnlOrder.add(btnSellLimit, gbc_btnSellLimit); btnBuyStop = new JButton(); btnBuyStop.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { JButton source = (JButton)e.getSource(); double price = Double.parseDouble(source.getText()); context.executeTask(new SubmitOrder(OrderCommand.BUYSTOP, price)); } }); snm = new SpinnerNumberModel(new Double(1.0), new Double(0.0), null, new Double(0.1)); spnEntryMarginePips = new JSpinner(snm); ne = new NumberEditor(spnEntryMarginePips, "0.0"); spnEntryMarginePips.setEditor(ne); GridBagConstraints gbc_spnEntryMargine = new GridBagConstraints(); gbc_spnEntryMargine.fill = GridBagConstraints.BOTH; gbc_spnEntryMargine.gridheight = 3; gbc_spnEntryMargine.gridx = 2; gbc_spnEntryMargine.gridy = 1; pnlOrder.add(spnEntryMarginePips, gbc_spnEntryMargine); GridBagConstraints gbc_btnBuyStop = new GridBagConstraints(); gbc_btnBuyStop.fill = GridBagConstraints.BOTH; gbc_btnBuyStop.gridx = 3; gbc_btnBuyStop.gridy = 1; pnlOrder.add(btnBuyStop, gbc_btnBuyStop); btnCancelAll = new JButton(); btnCancelAll.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { context.executeTask(new CancelOpenedOrders()); } }); lblBuyStop = new JLabel("Stop"); GridBagConstraints gbc_lblBuyStop = new GridBagConstraints(); gbc_lblBuyStop.gridx = 4; gbc_lblBuyStop.gridy = 1; pnlOrder.add(lblBuyStop, gbc_lblBuyStop); lblBid = new JLabel(""); lblBid.setHorizontalAlignment(SwingConstants.CENTER); GridBagConstraints gbc_lblBid = new GridBagConstraints(); gbc_lblBid.fill = GridBagConstraints.BOTH; gbc_lblBid.gridx = 1; gbc_lblBid.gridy = 2; pnlOrder.add(lblBid, gbc_lblBid); lblAsk = new JLabel(""); lblAsk.setHorizontalAlignment(SwingConstants.CENTER); GridBagConstraints gbc_lblAsk = new GridBagConstraints(); gbc_lblAsk.fill = GridBagConstraints.BOTH; gbc_lblAsk.gridx = 3; gbc_lblAsk.gridy = 2; pnlOrder.add(lblAsk, gbc_lblAsk); lblSellStop = new JLabel("Stop"); lblSellStop.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblSellStop = new GridBagConstraints(); gbc_lblSellStop.fill = GridBagConstraints.BOTH; gbc_lblSellStop.gridx = 0; gbc_lblSellStop.gridy = 3; pnlOrder.add(lblSellStop, gbc_lblSellStop); btnSellStop = new JButton(); btnSellStop.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { JButton source = (JButton)e.getSource(); double price = Double.parseDouble(source.getText()); context.executeTask(new SubmitOrder(OrderCommand.SELLSTOP, price)); } }); GridBagConstraints gbc_btnSellStop = new GridBagConstraints(); gbc_btnSellStop.fill = GridBagConstraints.BOTH; gbc_btnSellStop.gridx = 1; gbc_btnSellStop.gridy = 3; pnlOrder.add(btnSellStop, gbc_btnSellStop); btnBuyLimit = new JButton(); btnBuyLimit.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { JButton source = (JButton)e.getSource(); double price = Double.parseDouble(source.getText()); context.executeTask(new SubmitOrder(OrderCommand.BUYLIMIT, price)); } }); GridBagConstraints gbc_btnBuyLimit = new GridBagConstraints(); gbc_btnBuyLimit.fill = GridBagConstraints.BOTH; gbc_btnBuyLimit.gridx = 3; gbc_btnBuyLimit.gridy = 3; pnlOrder.add(btnBuyLimit, gbc_btnBuyLimit); lblBuyLimit = new JLabel("Limit"); lblBuyLimit.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblBuyLimit = new GridBagConstraints(); gbc_lblBuyLimit.gridx = 4; gbc_lblBuyLimit.gridy = 3; pnlOrder.add(lblBuyLimit, gbc_lblBuyLimit); btnCancelAll.setText("Cancel All"); GridBagConstraints gbc_btnCancelAll = new GridBagConstraints(); gbc_btnCancelAll.gridwidth = 3; gbc_btnCancelAll.fill = GridBagConstraints.BOTH; gbc_btnCancelAll.gridx = 1; gbc_btnCancelAll.gridy = 4; pnlOrder.add(btnCancelAll, gbc_btnCancelAll); pnlTpSlPips = new JPanel(); TitledBorder titleBorder = new TitledBorder(UIManager.getBorder("TitledBorder.border")); titleBorder.setTitle("Entry Option"); titleBorder.setTitleJustification(TitledBorder.LEADING); titleBorder.setTitlePosition(TitledBorder.TOP); pnlTpSlPips.setBorder(titleBorder); GridBagConstraints gbc_pnlTpSlPips = new GridBagConstraints(); gbc_pnlTpSlPips.insets = new Insets(0, 0, 5, 0); gbc_pnlTpSlPips.fill = GridBagConstraints.BOTH; gbc_pnlTpSlPips.gridx = 0; gbc_pnlTpSlPips.gridy = 2; getContentPane().add(pnlTpSlPips, gbc_pnlTpSlPips); GridBagLayout gbl_pnlTpSlPips = new GridBagLayout(); gbl_pnlTpSlPips.columnWidths = new int[] {100, 100}; gbl_pnlTpSlPips.rowHeights = new int[] {30, 30, 30}; pnlTpSlPips.setLayout(gbl_pnlTpSlPips); lblSlippagePips = new JLabel("Slippage(Pips):"); lblSlippagePips.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblSlippagePips = new GridBagConstraints(); gbc_lblSlippagePips.fill = GridBagConstraints.BOTH; gbc_lblSlippagePips.gridx = 0; gbc_lblSlippagePips.gridy = 0; pnlTpSlPips.add(lblSlippagePips, gbc_lblSlippagePips); snm = new SpinnerNumberModel(new Double(0), new Double(0), null, new Double(0.1)); spnSlippagePips = new JSpinner(snm); ne = new NumberEditor(spnSlippagePips, "0.0"); spnSlippagePips.setEditor(ne); GridBagConstraints gbc_spnSlippagePips = new GridBagConstraints(); gbc_spnSlippagePips.fill = GridBagConstraints.BOTH; gbc_spnSlippagePips.gridx = 1; gbc_spnSlippagePips.gridy = 0; pnlTpSlPips.add(spnSlippagePips, gbc_spnSlippagePips); lblStopLossPips = new JLabel("StopLoss(Pips):"); lblStopLossPips.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblStopLossPips = new GridBagConstraints(); gbc_lblStopLossPips.fill = GridBagConstraints.BOTH; gbc_lblStopLossPips.gridx = 0; gbc_lblStopLossPips.gridy = 1; pnlTpSlPips.add(lblStopLossPips, gbc_lblStopLossPips); snm = new SpinnerNumberModel(new Double(0), new Double(0), null, new Double(0.1)); spnTakeProfitPips = new JSpinner(snm); ne = new NumberEditor(spnTakeProfitPips, "0.0"); spnTakeProfitPips.setEditor(ne); GridBagConstraints gbc_spnTakeProfitPips = new GridBagConstraints(); gbc_spnTakeProfitPips.fill = GridBagConstraints.BOTH; gbc_spnTakeProfitPips.gridx = 1; gbc_spnTakeProfitPips.gridy = 1; pnlTpSlPips.add(spnTakeProfitPips, gbc_spnTakeProfitPips); lblTakeProfitPips = new JLabel("TakeProfit(Pips):"); lblTakeProfitPips.setHorizontalAlignment(SwingConstants.RIGHT); GridBagConstraints gbc_lblTakeProfitPips = new GridBagConstraints(); gbc_lblTakeProfitPips.fill = GridBagConstraints.BOTH; gbc_lblTakeProfitPips.gridx = 0; gbc_lblTakeProfitPips.gridy = 2; pnlTpSlPips.add(lblTakeProfitPips, gbc_lblTakeProfitPips); snm = new SpinnerNumberModel(new Double(0), new Double(0), null, new Double(0.1)); spnStopLossPips = new JSpinner(snm); ne = new NumberEditor(spnStopLossPips, "0.0"); spnStopLossPips.setEditor(ne); GridBagConstraints gbc_spnStopLossPips = new GridBagConstraints(); gbc_spnStopLossPips.fill = GridBagConstraints.BOTH; gbc_spnStopLossPips.gridx = 1; gbc_spnStopLossPips.gridy = 2; pnlTpSlPips.add(spnStopLossPips, gbc_spnStopLossPips); } public void onStart(IContext context) throws JFException { this.engine = context.getEngine(); this.console = context.getConsole(); this.context = context; EventQueue.invokeLater(new Runnable() { public void run() { try { setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); console.getOut().println("Strategy Started."); } public void onAccount(IAccount account) throws JFException { } public void onMessage(IMessage message) throws JFException { if (message.getOrder() == null) return; if (!cmbInstrument.getSelectedItem().equals(message.getOrder().getInstrument())) return; console.getOut().println(message.toString()); } public void onStop() throws JFException { console.getOut().println("Strategy Stopped."); } public void onTick(final Instrument instrument, final ITick tick) throws JFException { if (!cmbInstrument.getSelectedItem().equals(instrument)) return; final String scaleFormat = "%." + String.valueOf(instrument.getPipScale() + 1) + "f"; final double entryMarginePrice = (Double)spnEntryMarginePips.getValue() * instrument.getPipValue(); SwingUtilities.invokeLater(new Runnable () { public void run() { lblBid.setText(String.format(scaleFormat, tick.getBid())); lblAsk.setText(String.format(scaleFormat, tick.getAsk())); btnSellLimit.setText(String.format(scaleFormat, tick.getBid() + entryMarginePrice)); btnSellStop.setText(String.format(scaleFormat, tick.getBid() - entryMarginePrice)); btnBuyLimit.setText(String.format(scaleFormat, tick.getAsk() - entryMarginePrice)); btnBuyStop.setText(String.format(scaleFormat, tick.getAsk() + entryMarginePrice)); } }); } public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { } //通貨ペア private final Vector<Instrument> getInstruments() { Vector<Instrument> instruments = new Vector<Instrument>(); instruments.add(Instrument.USDJPY); instruments.add(Instrument.EURUSD); instruments.add(Instrument.EURJPY); instruments.add(Instrument.GBPUSD); instruments.add(Instrument.GBPJPY); instruments.add(Instrument.AUDUSD); instruments.add(Instrument.AUDJPY); return instruments; } //新規注文 private class SubmitOrder implements Callable<IOrder> { private OrderCommand orderCommand; private double price; public SubmitOrder(OrderCommand orderCommand, double price) { this.orderCommand = orderCommand; this.price = price; } public IOrder call() throws Exception { Instrument instrument = (Instrument)cmbInstrument.getSelectedItem(); double amount = (Double)spnAmount.getValue() * 0.01D; double slippagePips = (Double)spnSlippagePips.getValue(); double stopLossPips = (Double)spnStopLossPips.getValue(); double takeProfitPips = (Double)spnTakeProfitPips.getValue(); Calendar c = Calendar.getInstance(); SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss"); String label = orderCommand.toString() + sdf.format(c.getTime()); double stopLossPrice = 0.0D; double takeProfitPrice = 0.0D; if (orderCommand.isLong()) { if (stopLossPips > 0.0D) stopLossPrice = roundToPippette(price - stopLossPips * instrument.getPipValue(), instrument); if (takeProfitPips > 0.0D) takeProfitPrice = roundToPippette(price + takeProfitPips * instrument.getPipValue(), instrument); } else { if (stopLossPips > 0.0D) stopLossPrice = roundToPippette(price + stopLossPips * instrument.getPipValue(), instrument); if (takeProfitPips > 0.0D) takeProfitPrice = price - roundToPippette(takeProfitPips * instrument.getPipValue(), instrument); } IOrder order = engine.submitOrder(label, instrument, orderCommand, amount, price, slippagePips, stopLossPrice, takeProfitPrice); return order; } private double roundToPippette(double amount, Instrument instrument) { return round(amount, instrument.getPipScale() + 1); } private double round(double amount, int decimalPlaces) { return (new BigDecimal(amount)).setScale(decimalPlaces, BigDecimal.ROUND_HALF_UP).doubleValue(); } } //注文キャンセル public class CancelOpenedOrders implements Callable<CancelOpenedOrders> { public CancelOpenedOrders call() throws JFException { Instrument instrument = (Instrument)cmbInstrument.getSelectedItem(); for (IOrder o: engine.getOrders(instrument)) { if (o.getState() == IOrder.State.OPENED) { o.setRequestedAmount(0); } } return this; } } }
指値・逆指値注文時の注意点
先にも書いたとおり、JForexでは指値・逆指値の注文に値幅制限がないようです。
ただ、そのかわり指値・逆指値の条件が満たされている時に注文を出すと成行として処理されるので注意が必要です。
例えば現在のドル円レートが100円だった場合、101円の指値買い注文や101円の逆指値売り注文は成行として即約定することになります。
今回の注文画面ではそーゆー事態になるケースはほとんどない(値幅を0にすれば起こる)ワケですが、念のためご留意下さいませ。
コメント