package jforex; import java.awt.*; import java.awt.event.*; import java.math.*; import java.text.*; import java.util.*; import java.util.concurrent.*; import javax.swing.*; import com.dukascopy.api.*; import com.dukascopy.api.drawings.*; import com.dukascopy.api.feed.*; import com.dukascopy.api.feed.util.*; import com.dukascopy.api.indicators.OutputParameterInfo.DrawingStyle; import com.dukascopy.api.IEngine.OrderCommand; import com.dukascopy.api.IIndicators.AppliedPrice; import com.dukascopy.api.IIndicators.MaType; /* @author: Life with FX */ public class GarnetGlow implements IStrategy { private final String sID = "GGL"; private final String rID = "00"; private String uID; private IEngine engine; private IConsole console; private IHistory history; private IContext context; private IIndicators indicators; private IUserInterface userInterface; private IChart chart = null; private IChartObjectFactory factory = null; private LfxMarketInfo mkInfo; private LfxChartUtil chartUtil; private LfxTradingMode tradingMode; private LfxOrderUtil orderUtil; public void onStart(IContext context) throws JFException { // Instrument targetInstrument = Instrument.EURUSD; this.uID = UUID.randomUUID().toString().toUpperCase().substring(0, 6); // this.engine = context.getEngine(); this.console = context.getConsole(); this.history = context.getHistory(); this.context = context; this.indicators = context.getIndicators(); this.userInterface = context.getUserInterface(); // TODO::ゆくゆくはHashMap化して、多通貨対応とする this.context.setSubscribedInstruments(Collections.singleton(targetInstrument)); this.mkInfo = new LfxMarketInfo(context, targetInstrument); this.mkInfo.init(); // 実運用(REAL or DEMOモード)なら新規チャートを開く。バックテストモードならテスターのチャートを利用する。 IChart targetChart = null; if (this.engine.getType() == IEngine.Type.TEST) { targetChart = this.context.getChart(targetInstrument); } else { IFeedDescriptor feedDescriptor = new TimePeriodAggregationFeedDescriptor(targetInstrument, Period.FIVE_MINS, OfferSide.BID, Filter.ALL_FLATS); targetChart = this.context.openChart(feedDescriptor); } //TODO:: テンプレートセット(これも後で綺麗にしよう) /* targetChart.add(this.indicators.getIndicator("FRACTALLINES"), new Object[]{12}, new Color[]{Color.ORANGE, Color.ORANGE}, new DrawingStyle[]{DrawingStyle.LINE, DrawingStyle.LINE}, new int[]{2,2}); targetChart.add(this.indicators.getIndicator("EMA"), new Object[]{20}, new Color[]{Color.PINK.darker()}, new DrawingStyle[]{DrawingStyle.LINE}, new int[]{1}); */ // プログラムからの描写は無理。先行スパンBだけ欲しい。現状、必ずクラウドが表示され、ずれの指定の仕方もわからない。後のせで行きましょう。 /* targetChart.add(this.indicators.getIndicator("ICHIMOKU"), new Object[]{9, 26, 52}, new Color[]{null, null, null, null, Color.BLUE.brighter(), null}, new DrawingStyle[]{DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.LINE, DrawingStyle.NONE}, new int[]{1, 1, 1, 1, 1, 1}); */ this.chartUtil = new LfxChartUtil(context, targetChart, targetInstrument); this.tradingMode = new LfxTradingMode(this.mkInfo, targetInstrument); this.orderUtil = new LfxOrderUtil(this.engine, this.console, this.history, this.sID, this.rID, this.uID, targetInstrument); // TODO::後でもうちょっと綺麗にしよう if (targetChart != null) { ICustomWidgetChartObject widget = targetChart.getChartObjectFactory().createChartWidget(); widget.setText(" Strategy::GarnetGlow -- devel"); widget.setFillOpacity(0.2f); widget.setColor(Color.RED); JPanel widgetPanel = widget.getContentPanel(); JLabel jlEngineType = new JLabel(" -- ACCOUNT TYPE is " + this.engine.getType() + " -- "); JLabel jlOpenOrder = new JLabel(" -- open position -- "); jlOpenOrder.setAlignmentX(Component.CENTER_ALIGNMENT); JButton jbOpenSell = new JButton("SELL"); jbOpenSell.setForeground(Color.RED.darker()); jbOpenSell.setAlignmentX(Component.CENTER_ALIGNMENT); jbOpenSell.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { orderUtil.submitOrder(IEngine.OrderCommand.SELL); } catch (JFException el) { el.printStackTrace(); } } }); JButton jbOpenBuy = new JButton("BUY"); jbOpenBuy.setForeground(Color.BLUE); jbOpenBuy.setAlignmentX(Component.CENTER_ALIGNMENT); jbOpenBuy.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { orderUtil.submitOrder(IEngine.OrderCommand.BUY); } catch (JFException el) { el.printStackTrace(); } } }); JLabel jlCloseOrder = new JLabel(" -- close position -- "); JButton jbCloseAll = new JButton("CLOSE"); jbCloseAll.setAlignmentX(Component.CENTER_ALIGNMENT); jbCloseAll.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { orderUtil.closePositions(); } catch (JFException el) { el.printStackTrace(); } } }); widgetPanel.add(Box.createRigidArea(new Dimension(0,10))); widgetPanel.add(jlEngineType); widgetPanel.add(Box.createRigidArea(new Dimension(0,40))); widgetPanel.add(jlOpenOrder); widgetPanel.add(Box.createRigidArea(new Dimension(80,10))); widgetPanel.add(jbOpenSell); widgetPanel.add(Box.createRigidArea(new Dimension(10,10))); widgetPanel.add(jbOpenBuy); widgetPanel.add(Box.createRigidArea(new Dimension(0,60))); widgetPanel.add(jlCloseOrder); widgetPanel.add(Box.createRigidArea(new Dimension(100,10))); widgetPanel.add(jbCloseAll); widgetPanel.setSize(new Dimension(230, 200)); targetChart.add(widget); } } public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { // skip day off if (askBar.getVolume() < 0.5) { return; } if (instrument != Instrument.EURUSD) { return; } this.mkInfo.updateMarketInfo(instrument, period, askBar, bidBar); if (period == Period.FIVE_MINS) { LfxTradingMode.TradingState s = this.tradingMode.getTradingState(); this.tradingMode.analyzeForTradingState(bidBar.getTime()); //this.console.getOut().println(tradingMode.getTradingState()); // MEMO::プルバックの検出。T3の変化の方向 // Awesomeのプルバックの検出(連続数個とか) // TODO::チャートオブジェクト側に移動してしまおう。後で。。。 if (s != this.tradingMode.getTradingState()) { if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.SET_UP) { this.chartUtil.drawTradingSetUpOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend()); } else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.CALM_DOWN) { this.chartUtil.drawTradingCalmDownOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend()); } else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.RE_SET_UP) { this.chartUtil.drawTradingReSetUpOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend()); } else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.SET_DOWN) { this.chartUtil.drawTradingSetDownOnChart(instrument, period, bidBar); } } } } /******************************************************************************** * NOT USE ********************************************************************************/ public void onMessage(IMessage message) throws JFException { } public void onAccount(IAccount account) throws JFException { } public void onStop() throws JFException { } public void onTick(Instrument instrument, ITick tick) throws JFException { } } class LfxTradingSupervisor { // 状態遷移について // // 1:プルバックを検出する // 2:エントリ可能ポイントに突入後に、ここだというポイントを描写する。 // 損切りポイントも描写する // ただし、想定外に不利になった場合は、リエントリポイントを再計算する // 3:利益確定ポイント(目標ポイントを描写する) // プルバック検出 // 5分足間隔で稼働するロジック private void detectPullBack() throws JFException { // method1 // T3(4)とAwesome,stochfを利用してプルバックを検出する // TODO::method2 // 長大ロウソク後のレンジを検出する(超強くエントリ実施時) // プルバックを検出したら、どこまで戻るかを算出する // チャートに目標ゾーンを描写する // Ticketを発行して、他のものと区別する // 動作モードをエントリポイントまで辛抱するモードへ変更 } } class LfxMarketInfo { public static final int ICHIMOKU_TENKAN = 0; public static final int ICHIMOKU_KIJYUN = 1; public static final int ICHIMOKU_CHIKOU = 2; public static final int ICHIMOKU_SENKOU_A = 3; public static final int ICHIMOKU_SENKOU_B = 4; public static final int ICHIMOKU_CLOUD_A = 5; public static final int ICHIMOKU_CLOUD_B = 6; public static final int MAMA_MAMA = 0; public static final int MAMA_FAMA = 1; public static final int FRACTALLINES_HIGH = 0; public static final int FRACTALLINES_LOW = 1; public static final int AWESOME_ZERO = 0; public static final int AWESOME_POSITIVE = 1; public static final int AWESOME_NEGATIVE = 2; public static final int STOCHF_FAST_K = 0; public static final int STOCHF_FAST_D = 1; public static final int KELTNER_UP = 0; public static final int KELTNER_MID = 1; public static final int KELTNER_LOW = 2; private IEngine engine; private IConsole console; private IHistory history; private IContext context; private IIndicators indicators; private Instrument managingInstrument; private boolean isInitialized; private final int QUEUE_SIZE = 80; private LinkedBlockingDeque q5mEma; private LinkedBlockingDeque q5mIchimokuSenkouB; private LinkedBlockingDeque q5mMamaFama; private LinkedBlockingDeque q5mFractalLineHigh; private LinkedBlockingDeque q5mFractalLineLow; private LinkedBlockingDeque q5mRci; private LinkedBlockingDeque q5mAwesome; private LinkedBlockingDeque q5mStochFK; private LinkedBlockingDeque q5mStochFD; private LinkedBlockingDeque q5mAtrS; private LinkedBlockingDeque q5mAtrL; private LinkedBlockingDeque q5mWaddahAttar; private LinkedBlockingDeque q5mKeltnerMid; private LinkedBlockingDeque q5mVolumeWAP; private LinkedBlockingDeque q5mT3; private LinkedBlockingDeque q5mIBarBid; private LinkedBlockingDeque q5mIBarAsk; private LinkedBlockingDeque q1hKeltnerUp; private LinkedBlockingDeque q1hKeltnerMid; private LinkedBlockingDeque q1hKeltnerLow; private LinkedBlockingDeque q1hMaPrice; public LfxMarketInfo(IContext context, Instrument instrument) { this.engine = context.getEngine(); this.console = context.getConsole(); this.history = context.getHistory(); this.context = context; this.indicators = context.getIndicators(); this.managingInstrument = instrument; this.q5mEma = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mIchimokuSenkouB = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mMamaFama = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mFractalLineHigh = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mFractalLineLow = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mRci = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mAwesome = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mStochFK = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mStochFD = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mAtrS = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mAtrL = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mWaddahAttar = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mKeltnerMid = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mVolumeWAP = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mT3 = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mIBarBid = new LinkedBlockingDeque(QUEUE_SIZE); this.q5mIBarAsk = new LinkedBlockingDeque(QUEUE_SIZE); this.q1hKeltnerUp = new LinkedBlockingDeque(QUEUE_SIZE); this.q1hKeltnerMid = new LinkedBlockingDeque(QUEUE_SIZE); this.q1hKeltnerLow = new LinkedBlockingDeque(QUEUE_SIZE); this.q1hMaPrice = new LinkedBlockingDeque(QUEUE_SIZE); } public boolean isInitialized() { return this.isInitialized; } public int getQueueSize() { return QUEUE_SIZE; } public void init() throws JFException { int i; IBar firstBidbar5M = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.BID, 1); IBar firstAskbar5M = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.ASK, 1); IBar firstBidbar15M = this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.BID, 1); IBar firstAskbar15M = this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.ASK, 1); IBar firstBidbar1H = this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.BID, 1); IBar firstAskbar1H = this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.ASK, 1); for (i = QUEUE_SIZE; i > 0; i--) { updateMarketInfo(this.managingInstrument, Period.FIVE_MINS, firstAskbar5M, firstBidbar5M, i); updateMarketInfo(this.managingInstrument, Period.FIFTEEN_MINS, firstAskbar15M, firstBidbar15M, i); updateMarketInfo(this.managingInstrument, Period.ONE_HOUR, firstAskbar1H, firstBidbar1H, i); } this.isInitialized = true; } private boolean updateMarketInfo(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException { if (period == Period.FIVE_MINS) { updateMarketInfo5Mins(instrument, period, askBar, bidBar, shift); } else if (period == Period.FIFTEEN_MINS) { updateMarketInfo15Mins(instrument, period, askBar, bidBar, shift); } else if (period == Period.ONE_HOUR) { updateMarketInfo1Hour(instrument, period, askBar, bidBar, shift); } else { return false; } return true; } public boolean updateMarketInfo(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { if (instrument != this.managingInstrument) { this.console.getOut().println("wrong instrument!!"); return false; } if (period == Period.FIVE_MINS) { updateMarketInfo5Mins(instrument, period, askBar, bidBar); } else if (period == Period.FIFTEEN_MINS) { updateMarketInfo15Mins(instrument, period, askBar, bidBar); } else if (period == Period.ONE_HOUR) { updateMarketInfo1Hour(instrument, period, askBar, bidBar); } else { return false; } return true; } private boolean updateMarketInfo5Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { return updateMarketInfo5Mins(instrument, period, askBar, bidBar, 1); } private boolean updateMarketInfo5Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException { // INDICATOR-01 double ema = this.indicators.ema(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 20, shift); // INDICATOR-02 double[] ichimoku = this.indicators.ichimoku(instrument, period, OfferSide.BID, 9, 26, 52, shift); // INDICATOR-03 double[] mama = this.indicators.mama(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 0.3, 0.05, shift); // INDICATOR-04 double[] fractal = this.indicators.fractalLines(instrument, period, OfferSide.BID, 12, shift); // INDICATOR-05 double rci = this.indicators.rci(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 36, shift); // INDICATOR-06 double[] awesome = this.indicators.awesome(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 4, IIndicators.MaType.T3, 21, IIndicators.MaType.T3, shift); // INDICATOR-07 double[] stochF = this.indicators.stochF(instrument, period, OfferSide.BID, 12, 4, IIndicators.MaType.EMA, shift); // INDICATOR-08 double atrS = this.indicators.atr(instrument, period, OfferSide.BID, 12, shift); // INDICATOR-09 double atrL = this.indicators.atr(instrument, period, OfferSide.BID, 24, shift); // INDICATOR-10 double waddahAttar = this.indicators.waddahAttar(instrument, period, OfferSide.BID, shift); // INDICATOR-11 double keltner[] = this.indicators.keltner(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 10, shift); // INDICATOR-12 double volumeWAP = this.indicators.volumeWAP(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.WEIGHTED_CLOSE, 4, shift); // INDICATOR-13 double t3 = this.indicators.t3(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 20, 0.8, shift); // if (!this.q5mEma.offer(ema)) { this.q5mEma.poll(); this.q5mEma.offer(ema); } if (!this.q5mIchimokuSenkouB.offer(ichimoku[ICHIMOKU_SENKOU_B])) { this.q5mIchimokuSenkouB.poll(); this.q5mIchimokuSenkouB.offer(ichimoku[ICHIMOKU_SENKOU_B]); } if (!this.q5mMamaFama.offer(mama[MAMA_FAMA])) { this.q5mMamaFama.poll(); this.q5mMamaFama.offer(mama[MAMA_FAMA]); } if (!this.q5mFractalLineHigh.offer(fractal[FRACTALLINES_HIGH])) { this.q5mFractalLineHigh.poll(); this.q5mFractalLineHigh.offer(fractal[FRACTALLINES_HIGH]); } if (!this.q5mFractalLineLow.offer(fractal[FRACTALLINES_LOW])) { this.q5mFractalLineLow.poll(); this.q5mFractalLineLow.offer(fractal[FRACTALLINES_LOW]); } if (!this.q5mRci.offer(rci)) { this.q5mRci.poll(); this.q5mRci.offer(rci); } double awesomeVal; if (!Double.isNaN(awesome[AWESOME_POSITIVE])) { awesomeVal = awesome[AWESOME_POSITIVE]; } else { awesomeVal = awesome[AWESOME_NEGATIVE]; } if (!this.q5mAwesome.offer(awesomeVal)) { this.q5mAwesome.poll(); this.q5mAwesome.offer(awesomeVal); } if (!this.q5mStochFK.offer(stochF[STOCHF_FAST_K])) { this.q5mStochFK.poll(); this.q5mStochFK.offer(stochF[STOCHF_FAST_K]); } if (!this.q5mStochFD.offer(stochF[STOCHF_FAST_D])) { this.q5mStochFD.poll(); this.q5mStochFD.offer(stochF[STOCHF_FAST_D]); } if (!this.q5mAtrS.offer(atrS)) { this.q5mAtrS.poll(); this.q5mAtrS.offer(atrS); } if (!this.q5mAtrL.offer(atrL)) { this.q5mAtrL.poll(); this.q5mAtrL.offer(atrL); } if (!this.q5mWaddahAttar.offer(waddahAttar)) { this.q5mWaddahAttar.poll(); this.q5mWaddahAttar.offer(waddahAttar); } if (!this.q5mKeltnerMid.offer(keltner[KELTNER_MID])) { this.q5mKeltnerMid.poll(); this.q5mKeltnerMid.offer(keltner[KELTNER_MID]); } if (!this.q5mVolumeWAP.offer(volumeWAP)) { this.q5mVolumeWAP.poll(); this.q5mVolumeWAP.offer(volumeWAP); } if (!this.q5mT3.offer(t3)) { this.q5mT3.poll(); this.q5mT3.offer(t3); } // IBarのストア IBar hBidBar = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.BID, shift); IBar hAskBar = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.ASK, shift); if (!this.q5mIBarBid.offer(hBidBar)) { this.q5mIBarBid.poll(); this.q5mIBarBid.offer(hBidBar); } if (!this.q5mIBarAsk.offer(hAskBar)) { this.q5mIBarAsk.poll(); this.q5mIBarAsk.offer(hAskBar); } return true; } private boolean updateMarketInfo15Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { return updateMarketInfo15Mins(instrument, period, askBar, bidBar, 1); } private boolean updateMarketInfo15Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException { return true; } private boolean updateMarketInfo1Hour(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException { return updateMarketInfo1Hour(instrument, period, askBar, bidBar, 1); } private boolean updateMarketInfo1Hour(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException { long targetBarStartTime = this.history.getTimeForNBarsBack(Period.ONE_HOUR, bidBar.getTime(), shift); java.util.List bars = this.history.getBars(instrument, period, OfferSide.BID, Filter.ALL_FLATS, 24, targetBarStartTime, 0); long pastNBarStartTime = bars.get(0).getTime(); double keltner[][] = this.indicators.keltner(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 12, Filter.ALL_FLATS, pastNBarStartTime, targetBarStartTime); double[] keltnerUp = keltner[KELTNER_UP]; double[] keltnerMid = keltner[KELTNER_MID]; double[] keltnerLow = keltner[KELTNER_LOW]; double[] ima = this.indicators.ma(instrument, period, OfferSide.BID, IIndicators.AppliedPrice.CLOSE, 4, IIndicators.MaType.TRIMA, Filter.ALL_FLATS, pastNBarStartTime, targetBarStartTime); // if (!q1hKeltnerUp.offer(keltnerUp[keltnerUp.length - 1])) { q1hKeltnerUp.poll(); q1hKeltnerUp.offer(keltnerUp[keltnerUp.length - 1]); } if (!q1hKeltnerMid.offer(keltnerMid[keltnerMid.length - 1])) { q1hKeltnerMid.poll(); q1hKeltnerMid.offer(keltnerMid[keltnerMid.length - 1]); } if (!q1hKeltnerLow.offer(keltnerLow[keltnerLow.length - 1])) { q1hKeltnerLow.poll(); q1hKeltnerLow.offer(keltnerLow[keltnerLow.length - 1]); } if (!q1hMaPrice.offer(ima[ima.length - 1])) { q1hMaPrice.poll(); q1hMaPrice.offer(ima[ima.length - 1]); } return true; } public Deque getQ5mEma() { return this.q5mEma; } public Deque getQ5mIchimokuSenkouB() { return this.q5mIchimokuSenkouB; } public Deque getQ5mMamaFama() { return this.q5mMamaFama; } public Deque getQ5mFractalLineHigh() { return this.q5mFractalLineHigh; } public Deque getQ5mFractalLineLow() { return this.q5mFractalLineLow; } public Deque getQ5mRci() { return this.q5mRci; } public Deque getQ5mAwesome() { return this.q5mAwesome; } public Deque getQ5mAtrS() { return this.q5mAtrS; } public Deque getQ5mAtrL() { return this.q5mAtrL; } public Deque getQ5mVolumeWAP() { return this.q5mVolumeWAP; } public Deque getQ5mT3() { return this.q5mT3; } public Deque getQ5mIBarBid() { return this.q5mIBarBid; } } class LfxTradingMode { private Instrument managingInstrument; private TradingState tradingState; private boolean isBullTrend; private long setUpTime; private long setDownTime; private long reSetUpTime; // マーケット情報に依存する private LfxMarketInfo marketInfo; // チャート描写機能を全般的に検討する必要がある // 処理本体では、モード切り替えを判別するのが困難なので、このインスタンスでなんとかするのが良いと思う private LfxChartUtil chartUtil; // ストラテジパラメータ private final long FORBID_NEXT_SET_UP_DURATION = 1000 * 60 * 15; private final double SETUP_MARGIN_PIPS = 2; private final double SETDOWN_MARGIN_PIPS = 2; public LfxTradingMode(LfxMarketInfo marketInfo, Instrument instrument) { this.marketInfo = marketInfo; this.setDownTime = 0; this.setUpTime = 0; this.tradingState = TradingState.LESS; this.managingInstrument = instrument; } // トレンドの状態。各遷移する public static enum TradingState { SET_UP("1-SET_UP"), CALM_DOWN("2-CALM_DOWN"), RE_SET_UP("3-RE_SET_UP"), SET_DOWN("4-SET_DOWN"), LESS("0-LESS"); private String name; TradingState(String name) { this.name = name; } public String getName() { return name; } } public boolean isTradableMarketState(long processTime) throws JFException { // 状況アナライズ analyzeForTradingState(processTime); if (getTradingState() == TradingState.LESS || getTradingState() == TradingState.SET_DOWN) { return false; } else { return true; } } public TradingState analyzeForTradingState(long processTime) throws JFException { // セットアップ禁止期間なら禁止解除期間まで経過していないかチェックする if (getTradingState() == TradingState.SET_DOWN) { if ((processTime - setDownTime) > FORBID_NEXT_SET_UP_DURATION) { setTradingState(TradingState.LESS); } } // LESSならセットアップを探す if (getTradingState() == TradingState.LESS) { // 0: T3_MAを利用してチェックする方向のあたりをつける setUpSearchingDirection(); // セットアップを探すメソッド searchForTradingSetUp(processTime); } else if (getTradingState() == TradingState.SET_UP || getTradingState() == TradingState.RE_SET_UP) { // UP済みなら、弱くなっていないかチェックする searchForTradingCalmDown(processTime); } if (getTradingState() == TradingState.SET_UP || //getTradingState() == TradingState.RE_SET_UP || getTradingState() == TradingState.CALM_DOWN) { // セットダウン条件かチェックする searchForTradingSetDown(processTime); // 再度強いトレンド状態に復帰していないかチェックする if (getTradingState() != TradingState.SET_DOWN) { searchForTradingSetUp(processTime); } } return getTradingState(); } public TradingState getTradingState() { return this.tradingState; } private void setTradingState(TradingState state) { this.tradingState = state; } // 状態の遷移を管理するメソッド(内部処理) // 多分使ってないし、使わないかも private TradingState getNextTradingSet(TradingState currentTradingSet) { return TradingState.LESS; } // トレンドの方向を取得するメソッド // true: 上昇(BULL)トレンドの処理中 public boolean isBullTrend() { return this.isBullTrend; } // いつからトレーディングモードになったのか判別が必要 public long getSetUpTime() { return this.setUpTime; } private void setSetUpTime(long setUpTime) { this.setUpTime = setUpTime; } private void setSetDownTime(long setDownTime) { this.setDownTime = setDownTime; } private void setReSetUpTime(long reSetUpTime) { this.reSetUpTime = reSetUpTime; } public long getReSetUpTime() { return this.reSetUpTime; } public long getLastSetUpTime() { if (this.setUpTime > this.reSetUpTime) { return this.setUpTime; } else { return this.reSetUpTime; } } public String toString() { StringBuffer sb = new StringBuffer(); return sb.toString(); } //////////////////////////////////////////////////////////////////////////////// // ロジック本体:::セットアップを管理する - ここの実装で多様性が生まれる、というテンプレート //////////////////////////////////////////////////////////////////////////////// // セットアップを調査する売買方向をチェックする private void setUpSearchingDirection() throws JFException { Iterator it = this.marketInfo.getQ5mT3().descendingIterator(); double cur = it.next(); double pre = it.next(); double prepre = it.next(); double preprepre = it.next(); if (cur > pre && pre > prepre && prepre > preprepre) { this.isBullTrend = true; } else if (cur < pre && pre < prepre && prepre < preprepre) { this.isBullTrend = false; } } // セットアップをチェックする private TradingState searchForTradingSetUp(long processTime) throws JFException { boolean blCkLogic1 = false; boolean blCkLogic2 = false; boolean blCkLogic3 = false; boolean blCkLogic4 = false; // 1: 30分以内の範囲で、ロースク足がフラクタルラインをブレイクしていないかチェックする if (isBullTrend()) { Iterator candle = this.marketInfo.getQ5mIBarBid().descendingIterator(); Iterator fractal = this.marketInfo.getQ5mFractalLineHigh().descendingIterator(); // 30分 for (int i = 0; i < 6; i++) { if (candle.next().getClose() > fractal.next()) { blCkLogic1 = true; } } } else { Iterator candle = this.marketInfo.getQ5mIBarBid().descendingIterator(); Iterator fractal = this.marketInfo.getQ5mFractalLineLow().descendingIterator(); // 30分 for (int i = 0; i < 6; i++) { if (candle.next().getClose() < fractal.next()) { blCkLogic1 = true; } } } // 2: 現時点でVolumeWAPがフラクタルラインをブレイクしているか(ダマシマージンチェックあり) if (isBullTrend()) { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast(); if (volumeWAP > (fractal + (SETUP_MARGIN_PIPS * this.managingInstrument.getPipValue()))) { blCkLogic2 = true; } } else { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineLow().getLast(); if (volumeWAP < (fractal - (SETUP_MARGIN_PIPS * this.managingInstrument.getPipValue()))) { blCkLogic2 = true; } } // 3: Awesomeが順方向か if (isBullTrend()) { double awesome = this.marketInfo.getQ5mAwesome().getLast(); if (awesome > 0) { blCkLogic3 = true; } } else { double awesome = this.marketInfo.getQ5mAwesome().getLast(); if (awesome < 0) { blCkLogic3 = true; } } // 4: 短期ATRが長期ATRを上回っているか (3期間チェックして、2期間その通りならOK) int l4Count = 0; Iterator atrS = this.marketInfo.getQ5mAtrS().descendingIterator(); Iterator atrL = this.marketInfo.getQ5mAtrL().descendingIterator(); for (int i = 0; i < 3; i++) { if (atrS.next() > atrL.next()) { l4Count++; } } if (l4Count >= 2) { blCkLogic4 = true; } // 5: RCI(30)がある方向への遷移を示しているか // チェックOKなら // TODO:: SET_UPとRE_SET_UPを分離する。特にATR系は逆方向に強く動く場合があることを意識して、RE_SET_UPの時は条件を外すべき if (blCkLogic1 && blCkLogic2 && blCkLogic3 && blCkLogic4) { if (getTradingState() == TradingState.LESS) { // LESSなら // セットアップタイムを設定 setSetUpTime(processTime); // セットアップ状態へ遷移 setTradingState(TradingState.SET_UP); } else if (getTradingState() == TradingState.CALM_DOWN) { // CALM_DOWNなら // 再セットアップタイムを設定 setReSetUpTime(processTime); // 再度セットアップ状態へ遷移 setTradingState(TradingState.RE_SET_UP); } } return getTradingState(); } // セットアップが弱くなっていないかチェックする private TradingState searchForTradingCalmDown(long processTime) throws JFException { boolean blCkLogic1 = false; boolean blCkLogic2 = false; boolean blCkLogic3 = false; boolean blCkLogic5 = false; // 1: RCIのはがれ落ち。セットアップの2時間後に有効になるロジック if ((processTime - getLastSetUpTime()) > 1000 * 60 * 60 * 2) { Iterator iRci = this.marketInfo.getQ5mRci().descendingIterator(); int l1Count = 0; double rciNext = iRci.next(); double rciPre; if (isBullTrend()) { for (int i = 0; i < 18; i++) { rciPre = iRci.next(); if (rciPre > rciNext) { l1Count++; } rciNext = rciPre; } } else { for (int i = 0; i < 18; i++) { rciPre = iRci.next(); if (rciPre < rciNext) { l1Count++; } rciNext = rciPre; } } if (l1Count >= 14) { blCkLogic1 = true; } } // 2: RCIの振動 if ((processTime - getLastSetUpTime()) > 1000 * 60 * 60 * 2) { Iterator iRci = this.marketInfo.getQ5mRci().descendingIterator(); int l2CountPlus = 0; int l2CountMinus = 0; double rciNext = iRci.next(); double rciPre; for (int i = 0; i < 50; i++) { rciPre = iRci.next(); if (rciPre < rciNext) { l2CountPlus++; } else { l2CountMinus++; } rciNext = rciPre; } if (Math.abs(l2CountPlus - l2CountMinus) < 10) { blCkLogic2 = true; } } // 3: フラクタルのなかに転落していないか if (isBullTrend()) { double price = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast(); if (price < fractal) { blCkLogic3 = true; } } else { double price = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineLow().getLast(); if (price > fractal) { blCkLogic3 = true; } } // 4: Awesomeは順方向であること(逆方向になっていると黄色信号) // 5: 先行スパンBをローソク足がブレイクアウトしていないこと(直近3ローソク中2本) int l5Count = 0; Iterator price = this.marketInfo.getQ5mIBarBid().descendingIterator(); Iterator senkouB = this.marketInfo.getQ5mIchimokuSenkouB().descendingIterator(); if (isBullTrend()) { for (int i = 0; i < 3; i++) { if (price.next().getClose() < senkouB.next()) { l5Count++; } } } else { for (int i = 0; i < 3; i++) { if (price.next().getClose() > senkouB.next()) { l5Count++; } } } if (l5Count >= 2) { blCkLogic5 = true; } // 6: MAMAはほぼ全て順方向であること // 2ロジック以上がチェックOKなら // CALM_DOWN状態へ遷移 int ckCount = 0; if (blCkLogic1) ckCount++; if (blCkLogic2) ckCount++; if (blCkLogic3) ckCount++; if (blCkLogic5) ckCount++; if (ckCount >= 2) { // CALM_DOWN setTradingState(TradingState.CALM_DOWN); } return getTradingState(); } // セットダウンの時か private TradingState searchForTradingSetDown(long processTime) throws JFException { boolean blCkLogic1 = false; boolean blCkLogic2 = false; // 1: 逆フラクタルをブレイクしていたらアウト if ((processTime - getSetUpTime()) > 1000 * 60 * 10) { if (isBullTrend()) { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineLow().getLast(); if (volumeWAP < fractal) { blCkLogic1 = true; } } else { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast(); if (volumeWAP > fractal) { blCkLogic1 = true; } } } // 2: MAMAが長期に渡り逆にしなっているとアウト(チェック期間の80%とする) if ((processTime - getSetUpTime()) > 1000 * 60 * 60 * 2) { final int ck2Threshold = (int)(this.marketInfo.getQueueSize() * 0.8); int ck2i = 0; Iterator ite2 = this.marketInfo.getQ5mMamaFama().iterator(); double val1; double val2; for (val1 = ite2.next(); ite2.hasNext(); ) { val2 = ite2.next(); // BULL状態でBEARに長期間なるとNGというチェックロジック if (isBullTrend()) { if (val2 < val1) { ck2i++; } } else { if (val2 > val1) { ck2i++; } } val1 = val2; } if (ck2i > ck2Threshold) { blCkLogic2 = true; } else { blCkLogic2 = false; } } // 3: Awesomeが反対方向になっていると黄色信号 // 4: RCIが反対方向へ向かって落ちすぎているとアウト // 5: ダイバージェンスを検出したら黄色信号 // 6: ATRが加速していると黄色信号 // アウトを検出したらセットダウン if (blCkLogic1 || blCkLogic2) { setTradingState(TradingState.SET_DOWN); setSetDownTime(processTime); } // セットダウン時間を設定する return getTradingState(); } } // TODO::稼働途中でチャートが閉じられると、nullpointerで落ちると思う class LfxChartUtil { private IEngine engine; private IConsole console; private IHistory history; private IContext context; private IIndicators indicators; private IChart chart; private Instrument managingInstrument; private boolean isDrawable; public LfxChartUtil(IContext context, IChart chart, Instrument instrument) { this.engine = context.getEngine(); this.console = context.getConsole(); this.history = context.getHistory(); this.context = context; this.indicators = context.getIndicators(); this.managingInstrument = instrument; this.chart = chart; if (chart == null) { this.isDrawable = false; } else { this.isDrawable = true; } } public void setUndrawable() { this.isDrawable = false; } private boolean isManagingInstrument(Instrument instrument) { if (instrument == this.managingInstrument) { return true; } else { return false; } } public void drawArrowUpOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException { if (this.isDrawable) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); ISignalUpChartObject signal = factory.createSignalUp("AU" + bar.getTime(), bar.getTime(), bar.getLow()); signal.setColor(color); this.chart.addToMainChart(signal); } } public void drawArrowDownOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException { if (this.isDrawable) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); ISignalDownChartObject signal = factory.createSignalDown("AD" + bar.getTime(), bar.getTime(), bar.getHigh()); signal.setColor(color); chart.addToMainChart(signal); signal.setColor(color); this.chart.addToMainChart(signal); } } public void drawEllipseOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); IEllipseChartObject ellipse = factory.createEllipse("EM" + bar.getTime(), this.history.getPreviousBarStart(period, bar.getTime()), bar.getClose() + (1.5 * instrument.getPipValue()), this.history.getNextBarStart(period, bar.getTime()), bar.getClose() - (1.5 * instrument.getPipValue())); ellipse.setColor(color); chart.addToMainChart(ellipse); } } public void drawEllipseOnChart(Instrument instrument, Period period, IBar bar, Color color, double price) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); IEllipseChartObject ellipse = factory.createEllipse("EM" + bar.getTime(), this.history.getPreviousBarStart(period, bar.getTime()), price + (1.5 * instrument.getPipValue()), this.history.getNextBarStart(period, bar.getTime()), price - (1.5 * instrument.getPipValue())); ellipse.setColor(color); chart.addToMainChart(ellipse); } } public void drawShortLineOnChart(Instrument instrument, Period period, IBar bar, Color color, double price) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); IShortLineChartObject sline = factory.createShortLine("SL" + bar.getTime(), this.history.getPreviousBarStart(period, bar.getTime()), price, this.history.getNextBarStart(period, bar.getTime()), price); sline.setColor(color); chart.addToMainChart(sline); } } // セットアップ public void drawTradingSetUpOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); IRectangleChartObject rectangle = factory.createRectangle("REC" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 2), bar.getClose() + (7 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, this.history.getPreviousBarStart(period, bar.getTime()), 6), bar.getClose() - (7 * instrument.getPipValue())); rectangle.setColor(Color.CYAN); chart.addToMainChart(rectangle); if (isLong) { drawArrowUpOnChart(instrument, period, bar, Color.CYAN); } else { drawArrowDownOnChart(instrument, period, bar, Color.CYAN); } } } // トレンドの小康状態 public void drawTradingCalmDownOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException { drawTradingStateOnChartSub(instrument, period, bar, Color.PINK, isLong); } // トレンドの復調 public void drawTradingReSetUpOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException { drawTradingStateOnChartSub(instrument, period, bar, Color.CYAN, isLong); } private void drawTradingStateOnChartSub(Instrument instrument, Period period, IBar bar, Color color, boolean isLong) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); ITriangleChartObject triangle1; ITriangleChartObject triangle2; if (isLong) { // 上の三角が小さい triangle1 = factory.createTriangle("TRI1" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 2), bar.getClose() + (1.2 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, bar.getTime(), 2), bar.getClose() + (1.2 * instrument.getPipValue()), bar.getTime(), bar.getClose() + (0.1 * instrument.getPipValue())); // 下の三角が大きい triangle2 = factory.createTriangle("TRI2" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 3), bar.getClose() - (3 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, bar.getTime(), 3), bar.getClose() - (3 * instrument.getPipValue()), bar.getTime(), bar.getClose() - (0.1 * instrument.getPipValue())); } else { // 上の三角が大きい triangle1 = factory.createTriangle("TRI1" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 3), bar.getClose() + (3 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, bar.getTime(), 3), bar.getClose() + (3 * instrument.getPipValue()), bar.getTime(), bar.getClose() + (0.1 * instrument.getPipValue())); // 下の三角が小さい triangle2 = factory.createTriangle("TRI2" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 2), bar.getClose() - (1.2 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, bar.getTime(), 2), bar.getClose() - (1.2 * instrument.getPipValue()), bar.getTime(), bar.getClose() - (0.1 * instrument.getPipValue())); } triangle1.setColor(color); triangle2.setColor(color); chart.addToMainChart(triangle1); chart.addToMainChart(triangle2); } } // セットダウン public void drawTradingSetDownOnChart(Instrument instrument, Period period, IBar bar) throws JFException { if (this.isDrawable && isManagingInstrument(instrument)) { IChartObjectFactory factory = this.chart.getChartObjectFactory(); IRectangleChartObject rectangle = factory.createRectangle("SETDOWN" + bar.getTime(), this.history.getTimeForNBarsBack(period, bar.getTime(), 3), bar.getClose() + (2 * instrument.getPipValue()), this.history.getTimeForNBarsForward(period, bar.getTime(), 3), bar.getClose() - (2 * instrument.getPipValue())); rectangle.setColor(Color.PINK); chart.addToMainChart(rectangle); drawShortLineOnChart(instrument, period, bar, Color.PINK, bar.getClose()); } } } class LfxOrderUtil { private IEngine engine; private IConsole console; private IHistory history; private String sID; private String rID; private String uID; private String instanceID; private int orderCounter; private Instrument managingInstrument; public LfxOrderUtil(IEngine engine, IConsole console, IHistory history, String sID, String rID, String uID, Instrument instrument) { this.engine = engine; this.console = console; this.history = history; this.sID = sID; this.rID = rID; this.uID = uID; this.orderCounter = 1; this.managingInstrument = instrument; this.instanceID = sID + rID + uID; } public String getNextLabel(Date date, Instrument instrument) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm"); DecimalFormat df = new DecimalFormat("00000"); String label = this.instanceID + instrument.name() + sdf.format(date) + df.format(this.orderCounter); this.orderCounter++; return label; } public String getPosLabelIdentifier() { return this.instanceID + this.managingInstrument.name(); } public void submitOrder(IEngine.OrderCommand orderCommand) throws JFException { Instrument instrument = this.managingInstrument; double amount = 0.01; // 1万通貨 double takeProfitInPips = 25; double stopLossInPips = 11; double currentPrice; OfferSide offerSide = null; String label = getNextLabel(new Date(this.history.getTimeOfLastTick(this.managingInstrument)), instrument); if (orderCommand == IEngine.OrderCommand.BUY) { currentPrice = this.history.getLastTick(this.managingInstrument).getAsk(); } else { currentPrice = this.history.getLastTick(this.managingInstrument).getBid(); } if (this.engine.getType() == IEngine.Type.TEST || this.engine.getType() == IEngine.Type.DEMO) { IOrder order = this.engine.submitOrder(label, this.managingInstrument, orderCommand, amount, 0, 3, currentPrice + getStopLoss(instrument, orderCommand, stopLossInPips), currentPrice + getTakeProfit(instrument, orderCommand, takeProfitInPips)); } } public void closePositions() throws JFException { if (this.engine.getType() == IEngine.Type.TEST || this.engine.getType() == IEngine.Type.DEMO) { for (IOrder order : this.engine.getOrders(this.managingInstrument)) { if (order.getState() == IOrder.State.FILLED && order.getLabel().startsWith(getPosLabelIdentifier())) { order.close(); } } } } public static double getStopLoss(Instrument instrument, IEngine.OrderCommand orderCommand, double stopLossInPips) { if (orderCommand == IEngine.OrderCommand.BUY || orderCommand == IEngine.OrderCommand.BUYLIMIT || orderCommand == IEngine.OrderCommand.BUYSTOP) { return stopLossInPips * instrument.getPipValue() * -1; } else { return stopLossInPips * instrument.getPipValue(); } } public static double getTakeProfit(Instrument instrument, IEngine.OrderCommand orderCommand, double takeProfitInPips) { if (orderCommand == IEngine.OrderCommand.BUY || orderCommand == IEngine.OrderCommand.BUYLIMIT || orderCommand == IEngine.OrderCommand.BUYSTOP) { return takeProfitInPips * instrument.getPipValue(); } else { return takeProfitInPips * instrument.getPipValue() * -1; } } }