JForexで注文画面を自作する(スプレッド表示と新規注文時のオプション)

FX

前回までで成行注文と全決済が出来るようになったので、今回はもう少し使い勝手を良くしたいと思います。

例によってGUIのパーツはEclipseとWindowBuilderでちゃちゃっと作ってこんな感じに。

スプレッド表示

public void onTick(final Instrument instrument, final ITick tick) throws JFException {
    if (!cmbInstrument.getSelectedItem().equals(instrument)) return;</p>

    final String scaleFormat = "%." + String.valueOf(instrument.getPipScale() + 1) + "f";

    SwingUtilities.invokeLater(new Runnable () {
        public void run() {
            btnBid.setText(String.format(scaleFormat, tick.getBid()));
            btnAsk.setText(String.format(scaleFormat, tick.getAsk()));

            double spread = (tick.getAsk() - tick.getBid()) / instrument.getPipValue();
            lblSpread.setText(String.format("%.1f", spread));
        }
    });
}

前回つけるのを忘れてましたw

やることはボタンへのレート表示と大差ないので説明は省略します。

エントリーオプション

新規注文はIEngine.submitOrderメソッドで行うわけですが、スリッページを設定しない場合、デフォルトで5pipsのスリッページが設定されてしまいます。

なので、任意に設定できるようにします。

また、新規注文時に指値と逆指値も設定できた方が便利ですので、コチラも同時に実装します。

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 &gt; 0.0D)
                stopLossPrice = roundToPippette(price - stopLossPips * instrument.getPipValue(), instrument);
            if (takeProfitPips &gt; 0.0D)
                takeProfitPrice = roundToPippette(price + takeProfitPips * instrument.getPipValue(), instrument);
        } else {
            if (stopLossPips &gt; 0.0D)
                stopLossPrice = roundToPippette(price + stopLossPips * instrument.getPipValue(), instrument);
            if (takeProfitPips &gt; 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();
    }
}

スリッページが0の場合はデフォルトの5pipsではなくスリッページなしでの注文となりますので弾かれやすくなります。

指値・逆指値が0の場合は設定しないのと同じことになります。

あと、指値・逆指値を設定する際、pipsからレートに換算し直すところで誤差が生じる可能性があり、一応丸めるようにしています。

スポンサーリンク

全コード

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.ArrayList;
import java.util.Calendar;
import java.util.List;
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 SimpleOrder02 extends JFrame implements IStrategy {
    private IEngine engine;
    private IConsole console;
    private IContext context;
    private JButton btnAsk;
    private JButton btnBid;
    private JLabel lblAmount;
    private JLabel lblAsk;
    private JLabel lblBid;
    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 btnCloseAll;
    private JLabel lblSlippagePips;
    private JSpinner spnSlippagePips;
    private JLabel lblSpread;

    public SimpleOrder02() {
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                try {
                    onStop();
                } catch (JFException e1) {
                    e1.printStackTrace();
                }
            }
        });
        setResizable(false);
        setTitle("SimpleOrder");
        setBounds(100, 100, 280, 320);
        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[] {80, 40, 80};
        gbl_pnlOrder.rowHeights = new int[] {30, 30, 30};
        pnlOrder.setLayout(gbl_pnlOrder);

        lblBid = new JLabel("Bid(SELL)");
        lblBid.setForeground(Color.RED);
        lblBid.setHorizontalAlignment(SwingConstants.CENTER);
        GridBagConstraints gbc_lblBid = new GridBagConstraints();
        gbc_lblBid.fill = GridBagConstraints.BOTH;
        gbc_lblBid.gridx = 0;
        gbc_lblBid.gridy = 0;
        pnlOrder.add(lblBid, gbc_lblBid);

        lblAsk = new JLabel("Ask(BUY)");
        lblAsk.setForeground(Color.BLUE);
        lblAsk.setHorizontalAlignment(SwingConstants.CENTER);
        GridBagConstraints gbc_lblAsk = new GridBagConstraints();
        gbc_lblAsk.fill = GridBagConstraints.BOTH;
        gbc_lblAsk.gridx = 2;
        gbc_lblAsk.gridy = 0;
        pnlOrder.add(lblAsk, gbc_lblAsk);

        btnBid = new JButton();
        btnBid.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.SELL, price));
            }
        });
        GridBagConstraints gbc_btnBid = new GridBagConstraints();
        gbc_btnBid.fill = GridBagConstraints.BOTH;
        gbc_btnBid.gridx = 0;
        gbc_btnBid.gridy = 1;
        pnlOrder.add(btnBid, gbc_btnBid);

        btnAsk = new JButton();
        btnAsk.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.BUY, price));
            }
        });

        lblSpread = new JLabel();
        lblSpread.setHorizontalAlignment(SwingConstants.CENTER);
        GridBagConstraints gbc_lblSpread = new GridBagConstraints();
        gbc_lblSpread.fill = GridBagConstraints.BOTH;
        gbc_lblSpread.gridx = 1;
        gbc_lblSpread.gridy = 1;
        pnlOrder.add(lblSpread, gbc_lblSpread);
        GridBagConstraints gbc_btnAsk = new GridBagConstraints();
        gbc_btnAsk.fill = GridBagConstraints.BOTH;
        gbc_btnAsk.gridx = 2;
        gbc_btnAsk.gridy = 1;
        pnlOrder.add(btnAsk, gbc_btnAsk);

        btnCloseAll = new JButton();
        btnCloseAll.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                context.executeTask(new CloseOrders());
            }
        });
        btnCloseAll.setText("Close All");
        GridBagConstraints gbc_btnCloseAll = new GridBagConstraints();
        gbc_btnCloseAll.gridwidth = 3;
        gbc_btnCloseAll.fill = GridBagConstraints.BOTH;
        gbc_btnCloseAll.gridx = 0;
        gbc_btnCloseAll.gridy = 2;
        pnlOrder.add(btnCloseAll, gbc_btnCloseAll);

        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";

        SwingUtilities.invokeLater(new Runnable () {
            public void run() {
                btnBid.setText(String.format(scaleFormat, tick.getBid()));
                btnAsk.setText(String.format(scaleFormat, tick.getAsk()));

                double spread = (tick.getAsk() - tick.getBid()) / instrument.getPipValue();
                lblSpread.setText(String.format("%.1f", spread));
            }
        });
    }

    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();
        }
    }
    //全決済注文
    private class CloseOrders implements Callable<CloseOrders> {
        public CloseOrders call() throws JFException {
            Instrument instrument = (Instrument)cmbInstrument.getSelectedItem();

            List<IOrder> orders = new ArrayList<IOrder>();
            for (IOrder o: engine.getOrders()) {
                if (o.getState().equals(IOrder.State.FILLED) && o.getInstrument().equals(instrument)) {
                    orders.add(o);
                }
            }
            engine.closeOrders(orders);

            return this;
        }
    }
}

コメント

タイトルとURLをコピーしました