自分のプログラムを信じて

今日は、自分のプログラムを信じて、実際に真面目にトレードをしてみました。

まだまだ、精度の低いエントリポイントの表示機能しかありませんが、これが規則ですので、、、というわけでトライ。

 

今日はたまたま、うまく行きました。。。

1110-jf

 

精度はまだまだですね。

とは言え、手のうちは全て把握している手法を機械的に実行しているだけですので、信じるしかないです。

 

これが信じられないということは、自分を信じられない、、、ということに。

 

 

 

 

 

 

やっぱり、エントリ前に概算想定リスク(益も損も)を割り出せると、一人前のプログラムといえますね。まだまだ先は長いですね。

 

この戦略のソースコードは、今度こそ最後の掲載でしょう。。。きっと。

どなたかの戦略作りに役立つことを願い。。。

 

 

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

  :: TODOがたくさんある
*/

public class GarnetGlow implements IStrategy, IFeedListener {

    private final String sNAME = "GarnetGlow";
    private final String sID = "GGL";
    private final String rID = "00";
    private       String uID;

    // TODO::後々アノテーションで、configurableにする
    private Instrument targetInstrument = Instrument.EURUSD;

    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;
    private LfxTradingAdvisor tradingAdvisor;

    private boolean blDoneOnStartProcess;

    private Period customPeriod5S;
    private Period customPeriod10S;
    private ConcurrentLinkedQueue<LfxTradingUnit> delaySlot;

    public void onStart(IContext context) throws JFException {

        //
        this.uID = UUID.randomUUID().toString().toUpperCase().substring(0, 6);
        //

        int pastHours = 24;
        this.blDoneOnStartProcess = false;

        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();
        this.userInterface = context.getUserInterface();

        this.context.setSubscribedInstruments(Collections.singleton(this.targetInstrument));

        this.delaySlot = new ConcurrentLinkedQueue<LfxTradingUnit>();

        this.mkInfo = new LfxMarketInfo(context, this.targetInstrument);

        // TODO::ストラテジ開始の指定時間前に遡って初期化する。ただし、週末フィルタが必要だが未実装
        this.mkInfo.init(pastHours);

        this.tradingMode = new LfxTradingMode(this.mkInfo, this.targetInstrument);
        this.orderUtil = new LfxOrderUtil(this.engine, this.console, this.history, this.sID, this.rID, this.uID, this.targetInstrument);

        // 実運用(REAL or DEMOモード)なら新規チャートを開く。バックテストモードならテスターのチャートを利用する。
        IChart targetChart = null;
        if (this.engine.getType() == IEngine.Type.TEST) {
            targetChart = this.context.getChart(this.targetInstrument);
        } else {
            IFeedDescriptor feedDescriptor = new TimePeriodAggregationFeedDescriptor(this.targetInstrument, Period.FIVE_MINS, OfferSide.BID, Filter.ALL_FLATS);
            targetChart = this.context.openChart(feedDescriptor);
        }

        this.chartUtil = new LfxChartUtil(context, targetChart, this.targetInstrument, this.orderUtil);
        this.chartUtil.setupChartTemplate();

        this.tradingAdvisor = new LfxTradingAdvisor(this.console, this.tradingMode, this.mkInfo, this.chartUtil);

        // 時間経過をフォワードして、現時点の市場状態を捉える
        simulateTimeForwardingOnStart(pastHours);

        // 初期化中に、onBar本体のマーケット情報の更新を防ぐフラグ
        this.blDoneOnStartProcess = true;

        // onStart中のWidgetからの発注を防ぐ
        this.chartUtil.setupWidget(this.sNAME, this.rID);

        // onBarフォワード後に、カスタムFeedDescriptorを追加する
        this.customPeriod5S = Period.createCustomPeriod(Unit.Second, 5);
        this.customPeriod10S = Period.createCustomPeriod(Unit.Second, 10);
        TimePeriodAggregationFeedDescriptor fd = new TimePeriodAggregationFeedDescriptor(this.targetInstrument, this.customPeriod5S, OfferSide.BID, Filter.WEEKENDS);
        this.context.subscribeToFeed(fd, this);
        fd = new TimePeriodAggregationFeedDescriptor(this.targetInstrument, this.customPeriod10S, OfferSide.BID, Filter.WEEKENDS);
        this.context.subscribeToFeed(fd, this);

        TicksFeedDescriptor tfd = new TicksFeedDescriptor(this.targetInstrument);
        this.context.subscribeToFeed(tfd, this);
    }

    /**
     *
     */
    public void onFeedData(IFeedDescriptor feedDescriptor, ITimedData feedData) {

        if (this.tradingMode.getTradingState() == TradingState.SET_UP ||
	    this.tradingMode.getTradingState() == TradingState.CALM_DOWN ||
            this.tradingMode.getTradingState() == TradingState.RE_SET_UP) {

            try {

                ITick tick = this.history.getLastTick(feedDescriptor.getInstrument());
                double diffAskBid = tick.getAsk() - tick.getBid();

                IBar bidBar = (IBar)feedData;

                // Askバーは疑似生成する
                IBar askBar = new LfxIBar(bidBar.getClose() + diffAskBid,
                                          bidBar.getHigh() + diffAskBid,
                                          bidBar.getLow() + diffAskBid,
                                          bidBar.getOpen() + diffAskBid,
                                          bidBar.getVolume(),
                                          bidBar.getTime());

                onBar(feedDescriptor.getInstrument(),
                      feedDescriptor.getPeriod(),
                      askBar,
                      bidBar);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 指定時間から市場状態をフォワードする。
     * これにより、ストラテジ開始直後にも直にトレード判断が可能になる
     * @param pastHours フォワード開始時間(現時点から遡る時間。少なくとも12時間以上を推奨)
     */
    private void simulateTimeForwardingOnStart(int pastHours) throws JFException {

        long currentStrategyProcessingTime = this.history.getTimeOfLastTick(this.targetInstrument);
        int nBarsBackFor1M = 60 * pastHours;
        int nBarsBackFor5M = 12 * pastHours;
        int nBarsBackFor15M = 4 * pastHours;
        int nBarsBackFor1H  = pastHours;

        // 時間順繰りでシミュレートする必要あり
        for (int i = 0; (nBarsBackFor1M - i) > 0; i++) {

            // TODO::境界条件に自信なし。未検証。とりあえず作ろう
            // 動かした感じ、やっぱりロウソク1本分、ずれている気がする

            // 1min の onBar
            this.mkInfo.updateMarketInfo(this.targetInstrument,
                                         Period.ONE_MIN,
                                         this.history.getBar(this.targetInstrument, Period.ONE_HOUR, OfferSide.ASK, nBarsBackFor1M - i),
                                         this.history.getBar(this.targetInstrument, Period.ONE_HOUR, OfferSide.BID, nBarsBackFor1M - i),
                                         nBarsBackFor1M - i);

            // ロジックメモ
            // マーケット情報をマニュアルアップデートする (5M と 1H)
            // onBarを呼び出して、状態を進める (5Mだけで良い)
            if ((nBarsBackFor1M - i) % 60 == 0) {
                this.mkInfo.updateMarketInfo(this.targetInstrument,
                                             Period.ONE_HOUR,
                                             this.history.getBar(this.targetInstrument, Period.ONE_HOUR, OfferSide.ASK, (nBarsBackFor1M - i) / 60),
                                             this.history.getBar(this.targetInstrument, Period.ONE_HOUR, OfferSide.BID, (nBarsBackFor1M - i) / 60),
                                             (nBarsBackFor1M - i) / 60);
            }

            if ((nBarsBackFor1M - i) % 5 == 0) {
                IBar askBarForProcess = this.history.getBar(this.targetInstrument, Period.FIVE_MINS, OfferSide.ASK, (nBarsBackFor1M - i) / 5);
                IBar bidBarForProcess = this.history.getBar(this.targetInstrument, Period.FIVE_MINS, OfferSide.BID, (nBarsBackFor1M - i) / 5);

                this.mkInfo.updateMarketInfo(this.targetInstrument,
                                             Period.FIVE_MINS,
                                             askBarForProcess,
                                             bidBarForProcess,
                                             (nBarsBackFor1M - i) / 5);

                onBar(this.targetInstrument, Period.FIVE_MINS, askBarForProcess, bidBarForProcess);
            }

        }

    }

    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {

        // skip day off
        if (askBar.getVolume() < 0.5) {
            return;
        }

        if (instrument != this.targetInstrument) {
            return;
        }

        // 遡及初期化の為のロジック
        if (this.blDoneOnStartProcess) {
            this.mkInfo.updateMarketInfo(instrument, period, askBar, bidBar);
        }

        if (period == Period.FIVE_MINS) {

            TradingState s = this.tradingMode.getTradingState();
            this.tradingMode.analyzeForTradingState(bidBar.getTime());

            // チャートへの描写
            if (s != this.tradingMode.getTradingState()) {

                this.chartUtil.drawTradingStateOnChart(this.tradingMode.getTradingState(),
                                                       period,
                                                       bidBar,
                                                       this.tradingMode.isBullTrend());
            }

            if (this.tradingMode.getTradingState() == TradingState.SET_UP ||
                this.tradingMode.getTradingState() == TradingState.CALM_DOWN ||
                this.tradingMode.getTradingState() == TradingState.RE_SET_UP) {

                if (this.tradingAdvisor.getAdvisorState() == AdvisorState.DETECTING_PULLBACK) {

                    if (this.tradingAdvisor.detectPullBack(bidBar, this.tradingMode.isBullTrend())) {
                        this.chartUtil.drawBeginingOfPullbackOnChart(instrument, Period.FIVE_MINS, bidBar, this.tradingMode.isBullTrend());
                    }

                } else if (this.tradingAdvisor.getAdvisorState() == AdvisorState.WAITING_FOR_TRADINGPOINT) {
                    this.tradingAdvisor.offerTradingSignal(askBar, bidBar, this.tradingMode.isBullTrend());
                }

            } else if (this.tradingMode.getTradingState() == TradingState.SET_DOWN ||
                       this.tradingMode.getTradingState() == TradingState.LESS) {
                this.delaySlot.clear();
                this.tradingAdvisor.performSetDownProcess();
            }
        }

        // TODO::やっつけ。後できちんと再構築しよう
        if (period.equals(this.customPeriod5S) &&
            (
             this.tradingMode.getTradingState() == TradingState.SET_UP ||
             this.tradingMode.getTradingState() == TradingState.CALM_DOWN ||
             this.tradingMode.getTradingState() == TradingState.RE_SET_UP
             ) &&
            this.tradingAdvisor.getAdvisorState() == AdvisorState.WAITING_FOR_TRADINGPOINT) {
            //
            ArrayList<LfxTradingUnit> units = this.tradingAdvisor.offerTradingSignal(askBar, bidBar, this.tradingMode.isBullTrend());

            for (LfxTradingUnit unit: units) {

                if (unit.getState() == TradingUnitState.EXPIRE) {
                    this.chartUtil.drawResultOfPullbackOnChart(instrument, Period.FIVE_MINS, unit.getPullbackDetectInfoBar(), bidBar, unit.isLong(), unit.getState());
                } else if (unit.getState() == TradingUnitState.REACHED) {

                    unit.setTrailing(true);
                    unit.setTrailingBeginingInfoBar(bidBar);
                    this.delaySlot.add(unit);

                    this.chartUtil.drawResultOfPullbackOnChart(instrument, Period.FIVE_MINS, unit.getPullbackDetectInfoBar(), bidBar, unit.isLong(), unit.getState());
                }
            }
        }
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {

        if (this.delaySlot.size() > 0) {

            boolean crossToLong = false;

            double maFast = this.indicators.hma(instrument,
                                                customPeriod10S,
                                                OfferSide.BID,
                                                IIndicators.AppliedPrice.CLOSE,
                                                20,
                                                0);

            double maSlow = this.indicators.t3(instrument,
                                               customPeriod10S,
                                               OfferSide.BID,
                                               IIndicators.AppliedPrice.CLOSE,
                                               20,
                                               0.9,
                                               0);

            /*
              double butterworth = this.indicators.butterworthFilter(instrument,
              customPeriod5S,
              OfferSide.BID,
              IIndicators.AppliedPrice.CLOSE,
              15,
              0);
            */

            if (maFast > maSlow) {
                crossToLong = true;
            } else {
                crossToLong = false;
            }

            for (LfxTradingUnit unit : this.delaySlot) {

                // 重複処理(発注や、インディケート)を防ぐため、絶対にロックが必要
                synchronized(unit) {
                    if (unit.isTrailing()) {
                        if (unit.isLong() && crossToLong) {
                            unit.setTrailing(false);

                            // スロットからの除去
                            this.delaySlot.remove(unit);

                            // note// 現時点情報を格納
                            IBar bar = this.history.getBar(instrument, customPeriod5S, OfferSide.BID, 0);
                            unit.setTrailingFinishedInfoBar(bar);

                            // チャート描写
                            this.chartUtil.drawResultOfTrailingOnChart(instrument,
                                                                       unit.getTrailingBeginingInfoBar(),
                                                                       bar,
                                                                       unit.isLong());
                        } else if (!(unit.isLong()) && !(crossToLong)) {
                            unit.setTrailing(false);

                            // スロットからの除去
                            this.delaySlot.remove(unit);

                            // note// 現時点情報を格納
                            IBar bar = this.history.getBar(instrument, customPeriod5S, OfferSide.BID, 0);
                            unit.setTrailingFinishedInfoBar(bar);

                            // チャート描写
                            this.chartUtil.drawResultOfTrailingOnChart(instrument,
                                                                       unit.getTrailingBeginingInfoBar(),
                                                                       bar,
                                                                       unit.isLong());
                        }
                    }
                }
            }
        }
    }

    /********************************************************************************
     * NOT USE
     ********************************************************************************/
    public void onMessage(IMessage message) throws JFException {
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onStop() throws JFException {
        this.chartUtil.disableWidgetButtons();
    }
}

enum AdvisorState {
    DETECTING_PULLBACK("1"),
        WAITING_FOR_TRADINGPOINT("2"),
        ANALYZING_FOR_EXITPOINT("3"),
        LESS("4");

    private String name;
    AdvisorState(String name) { this.name = name; }
    public String getName() { return name; }
}

enum TargetPullbackLevel {
    KELTNER(1),
        EMA(2),
        ICHIMOKU(3),
        PASTPEAK(4),
        FRACTAL(5);

    private final int level;
    private TargetPullbackLevel(int level) { this.level = level; }
    public int getLevel() { return level; }
}

class LfxTradingAdvisor {

    // for debugging
    private IConsole console;

    private Instrument managingInstrument;

    private LfxTradingMode tradingMode;
    private LfxMarketInfo marketInfo;
    private LfxChartUtil chartUtil;
    private LinkedBlockingDeque<LfxTradingUnit> tradingUnitQueue;

    private AdvisorState advisorState;

    private String pullbackKey;
    private long pullbackBeginingTime;

    public LfxTradingAdvisor(IConsole console, LfxTradingMode tradingMode, LfxMarketInfo marketInfo, LfxChartUtil chartUtil) {
        this.console = console;

        this.managingInstrument = marketInfo.getManagingInstrument();
        this.tradingMode = tradingMode;
        this.marketInfo = marketInfo;
        this.pullbackBeginingTime = 0;
        this.chartUtil = chartUtil;
        // TODO::ちょっとこの制御どうなのよ
        this.advisorState = AdvisorState.DETECTING_PULLBACK;

        this.tradingUnitQueue = new LinkedBlockingDeque<LfxTradingUnit>(60);
    }

    public AdvisorState getAdvisorState() {
        return this.advisorState;
    }

    public void performSetDownProcess() {
        this.tradingUnitQueue.clear();
    }

    // プルバック検出
    // 5分足間隔で稼働するロジック
    public boolean detectPullBack(IBar bar, boolean isLong) throws JFException {

        long processTime = bar.getTime();

        // method1
        // T3(4)とAwesome,stochfを利用してプルバックを検出する
        // stochfの場合、1個前と今とで、クロス方向がトレンドに対し逆なら成立

        boolean checkResult = false;

        boolean ckLogic1 = true;
        boolean ckLogic2 = true;
        Iterator<Double> stockFK = this.marketInfo.getQ5mStochFK().descendingIterator();
        Iterator<Double> stockFD = this.marketInfo.getQ5mStochFD().descendingIterator();
        Iterator<Double> awesome = this.marketInfo.getQ5mAwesome().descendingIterator();
        double cur = awesome.next();
        double pre = awesome.next();
        double prepre = awesome.next();

        if (isLong) {

            // 少なくとも2期間は、プルバックである必要
            for (int i = 0; i < 2; i++) {
                if (stockFK.next() > stockFD.next()) {

                    // ロングのプルバックではない
                    ckLogic1 = false;
                }
            }

            // 少なくとも2期間は、プルバックする必要
            if (cur < pre && pre < prepre) {
                // OK
            } else {
                // ロングのプルバックではない
                ckLogic2 = false;
            }

        } else {

            // 少なくとも2期間は、プルバックである必要
            for (int i = 0; i < 2; i++) {
                if (stockFK.next() < stockFD.next()) {

                    // ショートのプルバックではない
                    ckLogic1 = false;
                }
            }

            // 少なくとも2期間は、プルバックする必要
            if (cur > pre && pre > prepre) {
                // OK
            } else {
                // ショートのプルバックではない
                ckLogic2 = false;
            }
        }

        if (ckLogic1 && ckLogic2) {
            this.advisorState = AdvisorState.WAITING_FOR_TRADINGPOINT;
            this.pullbackBeginingTime = processTime;
            this.pullbackKey = "PBK" + processTime;

            // TODO::やっつけロジック
            checkResult = true;
        } else {

            checkResult = false;
        }

        // TODO::method2
        // 長大ロウソク後のレンジを検出する(超強くエントリ実施時)

        // どこをプルバックのターゲットとするか決定する
        if (checkResult) {
            String unitKey = Long.toString(processTime);
            LfxTradingUnit tradingUnit = new LfxTradingUnit(this.marketInfo, unitKey, bar, isLong);

            analyzeTargetPullbackPoint(processTime, isLong, tradingUnit);

            if (!this.tradingUnitQueue.offer(tradingUnit)) {
                this.tradingUnitQueue.poll();
                this.tradingUnitQueue.offer(tradingUnit);
            }
        }

        return checkResult;
    }

    private void analyzeTargetPullbackPoint(long processTime, boolean isLong, LfxTradingUnit tradingUnit) throws JFException {

        // TODO::途中
        // 過去のピークプライスを格納する<-これはやったけど
        // 過去のピークプライスがどこまで押しているかを格納する件が残っている
        analyzePastPeakPoint(processTime, isLong, tradingUnit);

        TradingState tradingState = this.tradingMode.getTradingState();

        if (tradingState == TradingState.SET_UP ||
            tradingState == TradingState.RE_SET_UP) {

            // TODO:: 過去のプライスアクションを考慮してどこを目指すべきか決定すべき(その努力をするべき)
            // 強気の設定 (EMA20かKELTNER10)
            if (this.tradingUnitQueue.size() == 0) {
                tradingUnit.setTargetPullbackLevel(TargetPullbackLevel.KELTNER);
            } else {
                tradingUnit.setTargetPullbackLevel(TargetPullbackLevel.EMA);
            }

        } else if (tradingState == TradingState.CALM_DOWN) {

            // 弱気の設定 (一目先行Bか、前回の水準、かEMA20)
            tradingUnit.setTargetPullbackLevel(TargetPullbackLevel.ICHIMOKU);
        }

    }

    private void analyzePastPeakPoint(long processTime, boolean isLong, LfxTradingUnit tradingUnit) throws JFException {

        // 最初の実装 単純にZigZagを利用する
        ArrayList<Double> peakPrice = new ArrayList<Double>();
        ArrayList<Long> peakPriceTimeInfo = new ArrayList<Long>();
        ArrayList<Integer> peakPriceShiftInfo = new ArrayList<Integer>();

        Iterator<Double> itZigZag = this.marketInfo.getQ5mZigZag().descendingIterator();
        Iterator<IBar> itIBar = this.marketInfo.getQ5mIBarBid().descendingIterator();

        double zigzag;
        long peakTime;
        int dataCount = 0;
        int shiftCount = 1;

        // 過去のピークプライスをZigZagを利用して求める、上下各5点の計10点
        // 最初の2期間は捨てる
        itZigZag.next();
        itZigZag.next();
        itIBar.next();
        itIBar.next();

        while(itZigZag.hasNext()) {
            zigzag = itZigZag.next();
            peakTime = itIBar.next().getTime();

            if (!Double.isNaN(zigzag)) {
                peakPrice.add(zigzag);
                peakPriceTimeInfo.add(peakTime);
                peakPriceShiftInfo.add(shiftCount);
                dataCount++;
            }

            if (dataCount >= 10) {
                break;
            }
            shiftCount++;
        }

        // 超絶一方的な展開で、ピークポイントがなかったら情報が利用できない
        if (peakPrice.isEmpty()) {
            return;
        }

        ArrayList<Double> topPriceList = new ArrayList<Double>();
        ArrayList<Long> topPriceTimeList = new ArrayList<Long>();
        ArrayList<Integer> topPriceShiftList = new ArrayList<Integer>();

        ArrayList<Double> bottomPriceList = new ArrayList<Double>();
        ArrayList<Long> bottomPriceTimeList = new ArrayList<Long>();
        ArrayList<Integer> bottomPriceShiftList = new ArrayList<Integer>();

        Iterator<Double> itPeakPrice = peakPrice.iterator();
        Iterator<Long> itPeakPriceTimeInfo = peakPriceTimeInfo.iterator();
        Iterator<Integer> itPeakPriceShiftInfo = peakPriceShiftInfo.iterator();

        double peakPriceVal1 = itPeakPrice.next();
        double peakPriceVal2;
        long peakPriceTime;
        int shift;

        // ここで、トップとボトムを振り分ける。価格情報と時間に関する情報を同期して格納する
        while (itPeakPrice.hasNext()) {

            peakPriceVal2 = itPeakPrice.next();

            peakPriceTime = itPeakPriceTimeInfo.next();
            shift = itPeakPriceShiftInfo.next();

            if (peakPriceVal1 > peakPriceVal2) {
                topPriceList.add(peakPriceVal1);
                topPriceTimeList.add(peakPriceTime);
                topPriceShiftList.add(shift);
            } else {
                bottomPriceList.add(peakPriceVal1);
                bottomPriceTimeList.add(peakPriceTime);
                bottomPriceShiftList.add(shift);
            }

            peakPriceVal1 = peakPriceVal2;
        }

        tradingUnit.buildPeakPriceInfo(topPriceList,
                                       topPriceTimeList,
                                       topPriceShiftList,
                                       bottomPriceList,
                                       bottomPriceTimeList,
                                       bottomPriceShiftList);

        /*
          for (int i = 0; i < topPriceList.size(); i++) {

          this.chartUtil.drawEllipseOnChart(this.managingInstrument,
          Period.FIVE_MINS,
          Color.ORANGE,
          topPriceList.get(i),
          topPriceTimeList.get(i));
          }
        */

        // TODO::途中
        ////////////////////////////////////////////////////////////////////////////////
        // 前回の押しの水準も合わせてアナライズして、どこを押し目水準とするべきかここで判断しても
        // 良いと思われる
        // 当然、トレンドの強弱があるので、第1候補、第2候補の2点を決定すべき 
        //
        ////////////////////////////////////////////////////////////////////////////////
        IBar peakBar;
        double distanceEma;
        double distanceIchimoku;
        double distanceFractal;

        if (isLong) {
            int queueSize = this.marketInfo.getQueueSize();
            ArrayList<IBar>  arIBar = new ArrayList<IBar>(this.marketInfo.getQ5mIBarBid());
            ArrayList<Double> arEma = new ArrayList<Double>(this.marketInfo.getQ5mEma());
            ArrayList<Double> arIchimokuB = new ArrayList<Double>(this.marketInfo.getQ5mIchimokuSenkouB());
            ArrayList<Double> arFractal = new ArrayList<Double>(this.marketInfo.getQ5mFractalLineLow());

            for (int peakShift : bottomPriceShiftList) {

                // TODO:: シフトの扱い方がかなりシビア。現状 -2 としないと1キャンドル分ずれちゃう
                // outof したらごめんなさい
                peakBar = arIBar.get(queueSize - peakShift - 2);
                distanceEma = Math.abs(peakBar.getLow() - arEma.get(queueSize - peakShift - 2));
                distanceIchimoku = Math.abs(peakBar.getLow() - arIchimokuB.get(queueSize - peakShift - 2));
                distanceFractal = Math.abs(peakBar.getLow() - arFractal.get(queueSize - peakShift - 2));

                // キャンドルに対して、ターニングポイントとして最良のポイントを調査する
                if (distanceEma < distanceIchimoku) {
                    if (distanceIchimoku < distanceFractal) {
                        // Ema
                    } else if (distanceEma < distanceFractal) {
                        // Ema
                    } else {
                        // fractal
                    }
                } else {
                    // つまり  distanceEma > distanceIchimoku

                    if (distanceIchimoku < distanceFractal) {
                        // Ichimoku
                    } else {
                        // Fractal
                    }
                }

                /* DebugCode
		   this.console.getOut().println("shift: " + peakShift);
		   this.chartUtil.drawArrowUpOnChart(Instrument.EURUSD,
		   Period.FIVE_MINS,
		   peakBar,
		   Color.RED);

		   this.chartUtil.drawShortLineOnChart(Instrument.EURUSD,
		   Period.FIVE_MINS,
		   peakBar,
		   Color.RED,
		   arEma.get(queueSize - peakShift - 2));
                */
            }
        } else {
            /*
	      topPriceList.add(peakPriceVal1);
	      topPriceTimeList.add(peakPriceTime);
	      topPriceShiftList.add(shift);
            */
        }

    }

    public ArrayList<LfxTradingUnit> offerTradingSignal(IBar askBar, IBar bidBar, boolean isLong) throws JFException {

        long processTime = bidBar.getTime();

        // TODO::ここのロジックの修正は絶対に必要。サンプル実装
        if ((processTime - this.pullbackBeginingTime) > 1000 * 60 * 60) {
            this.advisorState = AdvisorState.DETECTING_PULLBACK;
        }

        ArrayList<LfxTradingUnit> result = new ArrayList<LfxTradingUnit>();

        Iterator<LfxTradingUnit> itUnit = this.tradingUnitQueue.descendingIterator();

        for ( ; itUnit.hasNext() ; ) {
            LfxTradingUnit unit = itUnit.next();

            if (!unit.isAlive()) {
                // TODO::見直し必要。シグナルが多くなるにつれて、若干パフォーマンスが劣化する
                continue;
            }

            // 撤退ラインに到達していないかチェックする
            // サンプル実装::撤退ラインを、目標プライス + 2 * ATR(長期間の方:現時点)としてみよう

            // 反転して出遅れていないかチェックする
            // サンプル実装::大きめの移動平均線のクロスとしよう

            // 時間切れとなっていないかチェックする
            // サンプル実装::40分とする
            if ((processTime - unit.getPullbackDetectTime()) > 1000 * 60 * 40) {
                unit.expireUnit(processTime, TradingUnitState.EXPIRE);
                result.add(unit);
                continue;
            }

            ////////
            // 目標ポイントに対して、どうなっているかをチェックする
            double targetPrice = unit.getCurrentTargetPrice(processTime);

            //// 到達している
            if (isLong) {

                if (bidBar.getClose() < targetPrice) {
                    unit.expireUnit(processTime, TradingUnitState.REACHED);
                    result.add(unit);
                    continue;
                }

            } else {

                if (bidBar.getClose() > targetPrice) {
                    unit.expireUnit(processTime, TradingUnitState.REACHED);
                    result.add(unit);
                    continue;
                }
            }

            //// 届きそう

            //// まだまだか

        }

        return result;
    }

}

enum TradingUnitState {
    WAIT,
        REACHED,
        EXPIRE;
}

class LfxTradingUnit {

    private String unitID;
    private LfxMarketInfo marketInfo;
    private Period checkPeriod;
    private boolean isOnTickProcess;
    private boolean isLong;

    private TradingUnitState state;

    private boolean isAlive;
    private IBar pullbackDetectInfoBar;
    private double pullbackDetectPrice;
    private long pullbackDetectTime;
    private long unitExpireTime;

    private boolean isTrailing;
    private IBar trailingBeginingInfoBar;
    private IBar trailingFinishedInfoBar;

    private double orderTargetPrice;
    private long   orderTragetTime;

    private ArrayList<Double> topPriceList;
    private ArrayList<Long> topPriceTimeList;
    private ArrayList<Integer> topPriceShiftList;

    private ArrayList<Double> bottomPriceList;
    private ArrayList<Long> bottomPriceTimeList;
    private ArrayList<Integer> bottomPriceShiftList;

    private TargetPullbackLevel targetPullbackLevel;

    public LfxTradingUnit(LfxMarketInfo marketInfo, String unitID, IBar bar, boolean isLong) {

        this.unitID = unitID;
        this.isAlive = true;
        this.isLong  = isLong;

        this.marketInfo = marketInfo;

        this.pullbackDetectInfoBar = bar;
        this.pullbackDetectPrice = bar.getClose();
        this.pullbackDetectTime = bar.getTime();

        this.state = TradingUnitState.WAIT;

        this.isTrailing = false;
    }

    public void buildPeakPriceInfo(ArrayList<Double> topPriceList, ArrayList<Long> topPriceTimeList, ArrayList<Integer> topPriceShiftList,
                                   ArrayList<Double> bottomPriceList, ArrayList<Long> bottomPriceTimeList, ArrayList<Integer> bottomPriceShiftList) {
        this.topPriceList = topPriceList;
        this.topPriceTimeList = topPriceTimeList;
        this.topPriceShiftList = topPriceShiftList;

        this.bottomPriceList = bottomPriceList;
        this.bottomPriceTimeList = bottomPriceTimeList;
        this.bottomPriceShiftList = bottomPriceShiftList;
    }

    public IBar getPullbackDetectInfoBar() {
        return this.pullbackDetectInfoBar;
    }

    public double getPullbackDetectPrice() {
        return this.pullbackDetectPrice;
    }

    public long getPullbackDetectTime() {
        return this.pullbackDetectTime;
    }

    public void setTargetPullbackLevel(TargetPullbackLevel targetLevel) {
        this.targetPullbackLevel = targetLevel;
    }

    public boolean isAlive() {
        return this.isAlive;
    }

    public void setTrailing(boolean trailing) {
        this.isTrailing = trailing;
    }

    public boolean isTrailing() {
        return this.isTrailing;
    }

    public void setTrailingBeginingInfoBar(IBar bar) {
        this.trailingBeginingInfoBar = bar;
    }

    public IBar getTrailingBeginingInfoBar() {
        return this.trailingBeginingInfoBar;
    }

    public void setTrailingFinishedInfoBar(IBar bar) {
        this.trailingFinishedInfoBar = bar;
    }

    public IBar getTrailingFinishedInfoBar() {
        return this.trailingFinishedInfoBar;
    }

    public double getCurrentTargetPrice(long expireTime) {

        double targetPrice;

        switch (this.targetPullbackLevel.getLevel()) {
        case 1:
            targetPrice = this.marketInfo.getZ5mKeltner();
            break;
        case 2:
            targetPrice = this.marketInfo.getZ5mEma();
            break;
        case 3:
            targetPrice = this.marketInfo.getZ5mIchimokuSenkouB();
            break;
        case 4:
            // TODO
            targetPrice = 0;
            break;
        case 5:
            if (this.isLong) {
                targetPrice = this.marketInfo.getZ5mFractalLineLow();
            } else {
                targetPrice = this.marketInfo.getZ5mFractalLineHigh();
            }
            break;
        default:
            targetPrice = 0;
        }

        return targetPrice;
    }

    public void expireUnit(long expireTime, TradingUnitState state) {
        this.isAlive = false;
        this.state = state;
        this.unitExpireTime = expireTime;
    }

    public TradingUnitState getState() {
        return this.state;
    }

    public boolean isLong() {
        return this.isLong;
    }

    public ArrayList<Double> getTopPriceList() {
        return this.topPriceList;
    }

    public ArrayList<Long> getTopPriceTimeList() {
        return this.topPriceTimeList;
    }

    public ArrayList<Integer> getTopPriceShiftList() {
        return this.topPriceShiftList;
    }

    public ArrayList<Double> getBottomPriceList() {
        return this.bottomPriceList;
    }

    public ArrayList<Long> getBottomPriceTimeList() {
        return this.bottomPriceTimeList;
    }

    public ArrayList<Integer> getBottomPriceShiftList() {
        return this.bottomPriceShiftList;
    }

}

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 = 100;
    private LinkedBlockingDeque<Double> q1mMamaFama;
    private LinkedBlockingDeque<Double> q1mHma;
    private LinkedBlockingDeque<IBar>   q1mIBarBid;
    private LinkedBlockingDeque<IBar>   q1mIBarAsk;

    private double z5mKeltner;
    private double z5mEma;
    private double z5mIchimokuSenkouB;
    private double z5mFractalLineHigh;
    private double z5mFractalLineLow;

    private LinkedBlockingDeque<Double> q5mEma;
    private LinkedBlockingDeque<Double> q5mIchimokuSenkouB;
    private LinkedBlockingDeque<Double> q5mMamaFama;
    private LinkedBlockingDeque<Double> q5mFractalLineHigh;
    private LinkedBlockingDeque<Double> q5mFractalLineLow;
    private LinkedBlockingDeque<Double> q5mRci;
    private LinkedBlockingDeque<Double> q5mAwesome;
    private LinkedBlockingDeque<Double> q5mStochFK;
    private LinkedBlockingDeque<Double> q5mStochFD;
    private LinkedBlockingDeque<Double> q5mAtrS;
    private LinkedBlockingDeque<Double> q5mAtrL;
    private LinkedBlockingDeque<Double> q5mWaddahAttar;
    private LinkedBlockingDeque<Double> q5mKeltnerMid;
    private LinkedBlockingDeque<Double> q5mVolumeWAP;
    private LinkedBlockingDeque<Double> q5mT3;
    private LinkedBlockingDeque<Double> q5mZigZag;
    private LinkedBlockingDeque<IBar>   q5mIBarBid;
    private LinkedBlockingDeque<IBar>   q5mIBarAsk;

    private LinkedBlockingDeque<Double> q1hKeltnerUp;
    private LinkedBlockingDeque<Double> q1hKeltnerMid;
    private LinkedBlockingDeque<Double> q1hKeltnerLow;
    private LinkedBlockingDeque<Double> 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.q1mMamaFama = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q1mHma = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q1mIBarBid = new LinkedBlockingDeque<IBar>(QUEUE_SIZE);
        this.q1mIBarAsk = new LinkedBlockingDeque<IBar>(QUEUE_SIZE);

        this.q5mEma = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mIchimokuSenkouB = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mMamaFama = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mFractalLineHigh = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mFractalLineLow = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mRci = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mAwesome = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mStochFK = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mStochFD = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mAtrS = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mAtrL = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mWaddahAttar = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mKeltnerMid = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mVolumeWAP = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mT3 = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mZigZag = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q5mIBarBid = new LinkedBlockingDeque<IBar>(QUEUE_SIZE);
        this.q5mIBarAsk = new LinkedBlockingDeque<IBar>(QUEUE_SIZE);

        this.q1hKeltnerUp = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q1hKeltnerMid = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q1hKeltnerLow = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
        this.q1hMaPrice = new LinkedBlockingDeque<Double>(QUEUE_SIZE);
    }

    public boolean isInitialized() {
        return this.isInitialized;
    }

    public int getQueueSize() {
        return QUEUE_SIZE;
    }

    public Instrument getManagingInstrument() {
        return this.managingInstrument;
    }

    public void init() throws JFException {

        int i;
        IBar firstBidbar1M = this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.BID, 1);
        IBar firstAskbar1M = this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.ASK, 1);

        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.ONE_MIN, firstAskbar1M, firstBidbar1M, 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;
    }

    public void init(int pastHours) throws JFException {

        // 現在の時刻を取得
        long currentStrategyProcessingTime = this.history.getTimeOfLastTick(this.managingInstrument);
        int nBarsBackFor1M = 60 * pastHours + 1;
        int nBarsBackFor5M = 12 * pastHours + 1;
        int nBarsBackFor15M = 4 * pastHours + 1;
        int nBarsBackFor1H  = pastHours + 1;

        // TODO::週末、年末Filter非対応版。対応するとなると大手術ですね。。。
        for (int i = QUEUE_SIZE; i > 0; i--) {
            updateMarketInfo(this.managingInstrument,
                             Period.ONE_MIN,
                             this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.ASK, nBarsBackFor1M + i),
                             this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.BID, nBarsBackFor1M + i),
                             nBarsBackFor1M + i);

            updateMarketInfo(this.managingInstrument,
                             Period.FIVE_MINS,
                             this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.ASK, nBarsBackFor5M + i),
                             this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.BID, nBarsBackFor5M + i),
                             nBarsBackFor5M + i);

            updateMarketInfo(this.managingInstrument,
                             Period.FIFTEEN_MINS,
                             this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.ASK, nBarsBackFor15M + i),
                             this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.BID, nBarsBackFor15M + i),
                             nBarsBackFor15M + i);

            updateMarketInfo(this.managingInstrument,
                             Period.ONE_HOUR,
                             this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.ASK, nBarsBackFor1H + i),
                             this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.BID, nBarsBackFor1H + i),
                             nBarsBackFor1H + i);
        }

        this.isInitialized = true;
    }

    public boolean updateMarketInfo(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {

        if (period == Period.ONE_MIN) {
            updateMarketInfo1Min(instrument, period, askBar, bidBar, shift);
        }  else 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.ONE_MIN) {
            updateMarketInfo1Min(instrument, period, askBar, bidBar);
        } else 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 updateMarketInfo1Min(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
        return  updateMarketInfo1Min(instrument, period, askBar, bidBar, 1);
    }

    private boolean updateMarketInfo1Min(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {

        Period processPeriod5M = Period.FIVE_MINS;

        // プルバックチェック用 1分毎に5分足のリアルタイム指標値を計算する
        // MAはOPENを利用する方が、クロスしやすいはず
        this.z5mKeltner = this.indicators.keltner(instrument,
                                                  processPeriod5M,
                                                  OfferSide.BID,
                                                  IIndicators.AppliedPrice.OPEN,
                                                  10,
                                                  0)[KELTNER_MID];

        this.z5mEma = this.indicators.ema(instrument,
                                          processPeriod5M,
                                          OfferSide.BID,
                                          IIndicators.AppliedPrice.OPEN,
                                          20,
                                          0);

        //
        this.z5mIchimokuSenkouB = this.indicators.ichimoku(instrument,
                                                           processPeriod5M,
                                                           OfferSide.BID,
                                                           9,
                                                           26,
                                                           52,
                                                           0)[ICHIMOKU_SENKOU_B];

        double[] fractal5M = this.indicators.fractalLines(instrument,
                                                          processPeriod5M,
                                                          OfferSide.BID,
                                                          12,
                                                          0);
        this.z5mFractalLineHigh = fractal5M[FRACTALLINES_HIGH];
        this.z5mFractalLineLow = fractal5M[FRACTALLINES_HIGH];

        // INDICATOR-01
        double hma = this.indicators.hma(instrument,
                                         period,
                                         OfferSide.BID,
                                         IIndicators.AppliedPrice.CLOSE,
                                         20,
                                         shift);

        if (!this.q1mHma.offer(hma)) {
            this.q1mHma.poll();
            this.q1mHma.offer(hma);
        }

        // IBarのストア
        IBar hBidBar = this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.BID, shift);
        IBar hAskBar = this.history.getBar(this.managingInstrument, Period.ONE_MIN, OfferSide.ASK, shift);
        if (!this.q1mIBarBid.offer(hBidBar)) {
            this.q1mIBarBid.poll();
            this.q1mIBarBid.offer(hBidBar);
        }

        if (!this.q1mIBarAsk.offer(hAskBar)) {
            this.q1mIBarAsk.poll();
            this.q1mIBarAsk.offer(hAskBar);
        }

        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 {

        long processTime = this.history.getBar(instrument, period, OfferSide.BID, shift).getTime();

        // 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.MAMA,
                                                 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);

        // INDICATOR-14
        double[] zigzag = this.indicators.zigzag(instrument,
                                                 period,
                                                 OfferSide.BID,
                                                 6,
                                                 5,
                                                 6,
                                                 Filter.NO_FILTER,
                                                 getQueueSize(),
                                                 processTime,
                                                 0);

        //
        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);
        }

        this.q5mZigZag.clear();
        for (double zigzagVal: zigzag) {
            if (!this.q5mZigZag.offer(zigzagVal)) {
                this.q5mZigZag.poll();
                this.q5mZigZag.offer(zigzagVal);
            }
        }

        // 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<IBar> 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 double getZ5mKeltner() {
        return this.z5mKeltner;
    }

    public double getZ5mEma() {
        return this.z5mEma;
    }

    public double getZ5mIchimokuSenkouB() {
        return this.z5mIchimokuSenkouB;
    }

    public double getZ5mFractalLineHigh() {
        return this.z5mFractalLineHigh;
    }

    public double getZ5mFractalLineLow() {
        return this.z5mFractalLineLow;
    }

    public Deque<Double> getQ5mEma() {
        return  this.q5mEma;
    }

    public Deque<Double> getQ5mIchimokuSenkouB() {
        return  this.q5mIchimokuSenkouB;
    }

    public Deque<Double> getQ5mMamaFama() {
        return  this.q5mMamaFama;
    }

    public Deque<Double> getQ5mFractalLineHigh() {
        return this.q5mFractalLineHigh;
    }

    public Deque<Double> getQ5mFractalLineLow() {
        return this.q5mFractalLineLow;
    }

    public Deque<Double> getQ5mRci() {
        return this.q5mRci;
    }

    public Deque<Double> getQ5mAwesome() {
        return this.q5mAwesome;
    }

    public Deque<Double> getQ5mStochFK() {
        return this.q5mStochFK;
    }

    public Deque<Double> getQ5mStochFD() {
        return this.q5mStochFD;
    }

    public Deque<Double> getQ5mAtrS() {
        return this.q5mAtrS;
    }

    public Deque<Double> getQ5mAtrL() {
        return this.q5mAtrL;
    }

    public Deque<Double> getQ5mVolumeWAP() {
        return this.q5mVolumeWAP;
    }

    public Deque<Double> getQ5mT3() {
        return this.q5mT3;
    }

    public Deque<Double> getQ5mZigZag() {
        return this.q5mZigZag;
    }

    public Deque<IBar> getQ5mIBarBid() {
        return this.q5mIBarBid;
    }
}

// トレンドの状態
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; }
}

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

    // トレンドの方向を取得するメソッド
    // 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<Double> 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<IBar> candle = this.marketInfo.getQ5mIBarBid().descendingIterator();
            Iterator<Double> fractal = this.marketInfo.getQ5mFractalLineHigh().descendingIterator();

            // 30分
            for (int i = 0; i < 6; i++) {

                if (candle.next().getClose() > fractal.next()) {
                    blCkLogic1 = true;
                }
            }
        } else {
            Iterator<IBar> candle = this.marketInfo.getQ5mIBarBid().descendingIterator();
            Iterator<Double> 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<Double> atrS = this.marketInfo.getQ5mAtrS().descendingIterator();
        Iterator<Double> 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<Double> 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<Double> 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<IBar> price = this.marketInfo.getQ5mIBarBid().descendingIterator();
        Iterator<Double> 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;
        boolean blCkLogic3 = 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<Double> 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: 急激な方向転換を察知したらアウト
        if ((processTime - getSetUpTime()) > 1000 * 60 * 30) {
            double atr = this.marketInfo.getQ5mAtrL().getLast();
            double ichimokuB = this.marketInfo.getQ5mIchimokuSenkouB().getLast();
            Iterator<IBar> candles = this.marketInfo.getQ5mIBarBid().descendingIterator();
            double closePriceOfLastBar = candles.next().getClose();
            candles.next();
            double openPriceOfLast2Bar = candles.next().getOpen();

            if (isBullTrend()) {
                if (closePriceOfLastBar < ichimokuB &&
                    openPriceOfLast2Bar > ichimokuB &&
                    (openPriceOfLast2Bar - closePriceOfLastBar) > (atr * 4)) {
                    blCkLogic3 = true;
                }
            } else {
                if (closePriceOfLastBar > ichimokuB &&
                    openPriceOfLast2Bar < ichimokuB &&
                    (closePriceOfLastBar - openPriceOfLast2Bar) > (atr * 4)) {
                    blCkLogic3 = true;
                }
            }
        }

        // 3: Awesomeが反対方向になっていると黄色信号

        // 4: RCIが反対方向へ向かって落ちすぎているとアウト

        // 5: ダイバージェンスを検出したら黄色信号

        // 6: ATRが加速していると黄色信号

        // アウトを検出したらセットダウン
        if (blCkLogic1 || blCkLogic2 || blCkLogic3) {
            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;
    private LfxOrderUtil orderUtil;
    private String keyOfMainWidget;

    public LfxChartUtil(IContext context, IChart chart, Instrument instrument, LfxOrderUtil orderUtil) {

        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();

        this.managingInstrument = instrument;
        this.orderUtil = orderUtil;
        this.keyOfMainWidget = null;

        this.chart = chart;
        if (chart == null) {
            this.isDrawable = false;
        } else {
            this.isDrawable = true;
        }
    }

    public void setUndrawable() {
        this.isDrawable = false;
    }

    public IChart getChartInstance() {
        return this.chart;
    }

    public void setupChartTemplate() {

        if (this.isDrawable) {

            this.chart.add(this.indicators.getIndicator("FRACTALLINES"),
                           new Object[]{12},
                           new Color[]{Color.ORANGE, Color.ORANGE},
                           new DrawingStyle[]{DrawingStyle.LINE, DrawingStyle.LINE},
                           new int[]{2,2});

            this.chart.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});
            */

        }
    }

    public void setupWidget(String strategyName, String version) {

        if (this.isDrawable) {
            ICustomWidgetChartObject widget = this.chart.getChartObjectFactory().createChartWidget();
            widget.setText(" STRATEGY:: " + strategyName + "   ver:" + version);
            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));

            this.keyOfMainWidget = widget.getKey();
            this.chart.add(widget);
        }
    }

    public void drawTradingStateOnChart(TradingState state, Period period, IBar bar, boolean isBullTrend) throws JFException {

        if (state == TradingState.SET_UP) {
            drawTradingSetUpOnChart(this.managingInstrument, period, bar, isBullTrend);

        } else if (state == TradingState.CALM_DOWN) {
            drawTradingCalmDownOnChart(this.managingInstrument, period, bar, isBullTrend);

        } else if (state == TradingState.RE_SET_UP) {
            drawTradingReSetUpOnChart(this.managingInstrument, period, bar, isBullTrend);

        } else if (state == TradingState.SET_DOWN) {
            drawTradingSetDownOnChart(this.managingInstrument, period, bar);
        }

    }

    public void disableWidgetButtons() {

        if (this.isDrawable) {

            for (IChartObject chartObj : this.chart.getAll()) {

                // 管理チャートからウィジェットを識別して取得する
                if (chartObj instanceof ICustomWidgetChartObject &&
                    chartObj.getKey().equals(this.keyOfMainWidget)) {

                    JPanel contentPanel = ((ICustomWidgetChartObject)chartObj).getContentPanel();

                    Component[] components = contentPanel.getComponents();

                    // ウィジェットのボタンを無効化する
                    for (int i = 0; i < components.length ; i++) {

                        if (components[i] instanceof JButton) {

                            ((JButton)components[i]).setEnabled(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 drawEllipseOnChart(Instrument instrument, Period period, Color color, double price, long time) throws JFException {

        if (this.isDrawable && isManagingInstrument(instrument)) {
            IChartObjectFactory factory = this.chart.getChartObjectFactory();

            IEllipseChartObject ellipse = factory.createEllipse("EM" + time,
                                                                this.history.getPreviousBarStart(period, time),
                                                                price + (1 * instrument.getPipValue()),
                                                                this.history.getNextBarStart(period, time),
                                                                price - (1 * instrument.getPipValue()));
            ellipse.setColor(color);
            chart.addToMainChart(ellipse);
        }
    }

    /**
     * プルバック描写用
     */
    public void drawShortLineOnChart(Instrument instrument, Period period, IBar bar1, IBar bar2, Color color) throws JFException {

        if (this.isDrawable && isManagingInstrument(instrument)) {
            IChartObjectFactory factory = this.chart.getChartObjectFactory();

            IShortLineChartObject sline = factory.createShortLine("PBSL" + bar2.getTime(),
                                                                  bar1.getTime(),
                                                                  bar1.getOpen(),
                                                                  bar2.getTime(),
                                                                  bar2.getClose());
            sline.setColor(color);
            sline.setLineStyle(LineStyle.DASH);
            chart.addToMainChart(sline);
        }
    }

    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.ORANGE, 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();

            IRectangleChartObject rectangle = factory.createRectangle("STAT" + bar.getTime(),
                                                                      this.history.getTimeForNBarsBack(period, bar.getTime(), 3),
                                                                      bar.getClose() + (3 * instrument.getPipValue()),
                                                                      this.history.getTimeForNBarsForward(period, this.history.getPreviousBarStart(period, bar.getTime()), 3),
                                                                      bar.getClose() - (3 * instrument.getPipValue()));
            rectangle.setColor(color);
            chart.addToMainChart(rectangle);

            /*
              if (isLong) {
              drawArrowUpOnChart(instrument, period, bar, color);
              } else {
              drawArrowDownOnChart(instrument, period, bar, color);
              }
            */
        }
    }

    // セットダウン
    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());
        }
    }

    // プルバック開始
    public void drawBeginingOfPullbackOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException {
        if (this.isDrawable && isManagingInstrument(instrument)) {
            IChartObjectFactory factory = this.chart.getChartObjectFactory();
            ITriangleChartObject triangle;

            if (isLong) {
                triangle = factory.createTriangle("BPBL" + bar.getTime(),
                                                  this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
                                                  bar.getHigh() + (1 * instrument.getPipValue()),
                                                  this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
                                                  bar.getHigh() + (1 * instrument.getPipValue()),
                                                  bar.getTime(),
                                                  bar.getHigh() + (0.1 * instrument.getPipValue()));
            } else {
                triangle = factory.createTriangle("BPBS" + bar.getTime(),
                                                  this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
                                                  bar.getLow() - (1 * instrument.getPipValue()),
                                                  this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
                                                  bar.getLow() - (1 * instrument.getPipValue()),
                                                  bar.getTime(),
                                                  bar.getLow() - (0.1 * instrument.getPipValue()));

            }

            triangle.setColor(Color.GREEN);
            chart.addToMainChart(triangle);
        }
    }

    // プルバック終了
    public void drawResultOfPullbackOnChart(Instrument instrument, Period period, IBar sBar, IBar bar, boolean isLong, TradingUnitState state) throws JFException {

        if (this.isDrawable && isManagingInstrument(instrument)) {

            Color color;
            if (state == TradingUnitState.EXPIRE) {
                drawEllipseOnChart(instrument, Period.FIVE_MINS, bar, Color.WHITE);
                drawShortLineOnChart(instrument, Period.FIVE_MINS, sBar, bar, Color.WHITE);
            } else if (state == TradingUnitState.REACHED) {
                IChartObjectFactory factory = this.chart.getChartObjectFactory();
                ITriangleChartObject triangle;

                if (isLong) {
                    triangle = factory.createTriangle("EPBS" + bar.getTime(),
                                                      this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
                                                      bar.getLow() - (1 * instrument.getPipValue()),
                                                      this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
                                                      bar.getLow() - (1 * instrument.getPipValue()),
                                                      bar.getTime(),
                                                      bar.getLow() - (0.1 * instrument.getPipValue()));
                } else {
                    triangle = factory.createTriangle("EPBL" + bar.getTime(),
                                                      this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
                                                      bar.getHigh() + (1 * instrument.getPipValue()),
                                                      this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
                                                      bar.getHigh() + (1 * instrument.getPipValue()),
                                                      bar.getTime(),
                                                      bar.getHigh() + (0.1 * instrument.getPipValue()));
                }
                triangle.setColor(Color.CYAN);
                chart.addToMainChart(triangle);
                drawShortLineOnChart(instrument, Period.FIVE_MINS, sBar, bar, Color.CYAN);
            } else {
                drawEllipseOnChart(instrument, Period.FIVE_MINS, bar, Color.WHITE);
            }
        }
    }

    // トレイル終了
    public void drawResultOfTrailingOnChart(Instrument instrument, IBar sBar, IBar bar, boolean isLong) throws JFException {

        if (this.isDrawable && isManagingInstrument(instrument)) {

            drawEllipseOnChart(instrument, Period.ONE_MIN, sBar, Color.CYAN);
            drawShortLineOnChart(instrument, Period.FIVE_MINS, sBar, bar, Color.ORANGE);

            if (isLong) {
                drawArrowUpOnChart(instrument, Period.ONE_SEC, bar, Color.ORANGE);
            } else {
                drawArrowDownOnChart(instrument, Period.ONE_SEC, bar, Color.ORANGE);
            }
        }
    }
}

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

class LfxIBar implements IBar {

    private double close;
    private double high;
    private double low;
    private double open;
    private double volume;
    private long   time;

    public LfxIBar(double close, double high, double low, double open, double volume, long time) {
        this.close = close;
        this.high  = high;
        this.low   = low;
        this.open  = open;
        this.volume = volume;
        this.time  = time;

    }

    public double getClose() {
        return this.close;
    }

    public double getHigh() {
        return this.high;
    }

    public double getLow() {
        return this.low;
    }

    public double getOpen() {
        return this.open;
    }

    public double getVolume() {
        return this.volume;
    }

    public long getTime() {
        return this.time;
    }
}

 

 

 

 

 

コメントを残す