目標を、スキャルピングでコンスタントに利益を上げること、としてjForexのストラテジの開発を続けています。
トレンドが出ている時に、押し目買い・戻り売りを効率的に実行(最終的には半自動化と自動化の両方)できるように、あれやこれやしています。
以前は、自動売買システムをいきなり組んでいましたが、最近はインディケータ的なものを最初につくり、その有効性が確認された後に、自動売買に落とし込んだ方が良い気がしています。
とりあえず、
- そこそこの精度で、セットアップの強弱にあわせて、”ここら辺のレートで買え!、ここで辺で決済しろ!”がチャートに出て
- 注文ボタンをポチッとすると、ストラテジ内でアクションを留保し、より有利なレートで発注するように、トレールする
- さらに、損切りポイントが明確にわかる
なんて機能を実用レベルで実装することが目標です。なんて高い山だ。今まで何度も挫折してきましたが、、、
最近、国内でjForexのストラテジを扱ったサイトさんを見かけ無くなってきて寂しです。MT4のモデルより、絶対jForexがいいと思うんですけどね。
(現状)
仕掛けのセットアップを表示したり、
仕掛けの強弱を表現したり
ぐらいの機能を実装しています。。。
とにかく、Tick処理に耐えられる実装にしなくては。
サンプルコード::: (発注機能や、上記に記載した夢の機能は未実装です)
このストラテジに関しては、最後の掲載だと思います。
記事投稿時に、ジェネリクス表記が自動カットされてしまうようですので、このままではコンパイルできないです。
package jforex;
import java.awt.*;
import java.math.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import com.dukascopy.api.*;
import com.dukascopy.api.drawings.*;
import com.dukascopy.api.feed.*;
import com.dukascopy.api.feed.util.*;
import com.dukascopy.api.indicators.OutputParameterInfo.DrawingStyle;
import com.dukascopy.api.IEngine.OrderCommand;
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.IIndicators.MaType;
/*
@author: Life with FX
*/
public class GarnetGlow implements IStrategy {
private final String sID = "GGL";
private final String rID = "000";
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;
public void onStart(IContext context) throws JFException {
this.engine = context.getEngine();
this.console = context.getConsole();
this.history = context.getHistory();
this.context = context;
this.indicators = context.getIndicators();
this.userInterface = context.getUserInterface();
// TODO::ゆくゆくはHashMap化して、多通貨対応とする
this.context.setSubscribedInstruments(Collections.singleton(Instrument.EURUSD));
this.mkInfo = new LfxMarketInfo(context, Instrument.EURUSD);
this.mkInfo.init();
// 実運用(REAL or DEMOモード)なら新規チャートを開く。バックテストモードならテスターのチャートを利用する。
IChart targetChart = null;
if (this.engine.getType() == IEngine.Type.TEST) {
targetChart = this.context.getChart(Instrument.EURUSD);
} else {
IFeedDescriptor feedDescriptor = new TimePeriodAggregationFeedDescriptor(Instrument.EURUSD, Period.FIVE_MINS, OfferSide.BID, Filter.ALL_FLATS);
targetChart = this.context.openChart(feedDescriptor);
}
//TODO:: テンプレートセット(これも後で綺麗にしよう)
targetChart.add(this.indicators.getIndicator("FRACTALLINES"),
new Object[]{12},
new Color[]{Color.GREEN.darker(), Color.GREEN.darker()},
new DrawingStyle[]{DrawingStyle.LINE, DrawingStyle.LINE},
new int[]{2,2});
targetChart.add(this.indicators.getIndicator("EMA"),
new Object[]{20},
new Color[]{Color.PINK.darker()},
new DrawingStyle[]{DrawingStyle.LINE},
new int[]{1});
// プログラムからの描写は無理。先行スパンBだけ欲しい。現状、必ずクラウドが表示され、ずれの指定の仕方もわからない。後のせで行きましょう。
/*
targetChart.add(this.indicators.getIndicator("ICHIMOKU"),
new Object[]{9, 26, 52},
new Color[]{null, null, null, null, Color.BLUE.brighter(), null},
new DrawingStyle[]{DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.NONE, DrawingStyle.LINE, DrawingStyle.NONE},
new int[]{1, 1, 1, 1, 1, 1});
*/
this.chartUtil = new LfxChartUtil(context, targetChart, Instrument.EURUSD);
// TODO::後でもうちょっと綺麗にしよう
if (targetChart != null) {
ICustomWidgetChartObject widget = targetChart.getChartObjectFactory().createChartWidget();
widget.setText("GarnetGlow -- devel");
widget.setFillOpacity(0.1f);
widget.setColor(Color.RED.darker());
targetChart.add(widget);
}
this.tradingMode = new LfxTradingMode(this.mkInfo, Instrument.EURUSD);
}
public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
// skip day off
if (askBar.getVolume() < 0.5) {
return;
}
if (instrument != Instrument.EURUSD) {
return;
}
this.mkInfo.updateMarketInfo(instrument, period, askBar, bidBar);
if (period == Period.FIVE_MINS) {
LfxTradingMode.TradingState s = this.tradingMode.getTradingState();
this.tradingMode.analyzeForTradingState(bidBar.getTime());
//this.console.getOut().println(tradingMode.getTradingState());
// TODO::チャートオブジェクト側に移動してしまおう。後で。。。
if (s != this.tradingMode.getTradingState()) {
if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.SET_UP) {
this.chartUtil.drawTradingSetUpOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend());
} else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.CALM_DOWN) {
this.chartUtil.drawTradingCalmDownOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend());
} else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.RE_SET_UP) {
this.chartUtil.drawTradingReSetUpOnChart(instrument, period, bidBar, this.tradingMode.isBullTrend());
} else if (this.tradingMode.getTradingState() == LfxTradingMode.TradingState.SET_DOWN) {
this.chartUtil.drawTradingSetDownOnChart(instrument, period, bidBar);
}
}
}
}
/********************************************************************************
* NOT USE
********************************************************************************/
public void onMessage(IMessage message) throws JFException {
}
public void onAccount(IAccount account) throws JFException {
}
public void onStop() throws JFException {
}
public void onTick(Instrument instrument, ITick tick) throws JFException {
}
}
class LfxMarketInfo {
public static final int ICHIMOKU_TENKAN = 0;
public static final int ICHIMOKU_KIJYUN = 1;
public static final int ICHIMOKU_CHIKOU = 2;
public static final int ICHIMOKU_SENKOU_A = 3;
public static final int ICHIMOKU_SENKOU_B = 4;
public static final int ICHIMOKU_CLOUD_A = 5;
public static final int ICHIMOKU_CLOUD_B = 6;
public static final int MAMA_MAMA = 0;
public static final int MAMA_FAMA = 1;
public static final int FRACTALLINES_HIGH = 0;
public static final int FRACTALLINES_LOW = 1;
public static final int AWESOME_ZERO = 0;
public static final int AWESOME_POSITIVE = 1;
public static final int AWESOME_NEGATIVE = 2;
public static final int STOCHF_FAST_K = 0;
public static final int STOCHF_FAST_D = 1;
public static final int KELTNER_UP = 0;
public static final int KELTNER_MID = 1;
public static final int KELTNER_LOW = 2;
private IEngine engine;
private IConsole console;
private IHistory history;
private IContext context;
private IIndicators indicators;
private Instrument managingInstrument;
private boolean isInitialized;
private final int QUEUE_SIZE = 80;
private LinkedBlockingDeque q5mEma;
private LinkedBlockingDeque q5mIchimokuSenkouB;
private LinkedBlockingDeque q5mMamaFama;
private LinkedBlockingDeque q5mFractalLineHigh;
private LinkedBlockingDeque q5mFractalLineLow;
private LinkedBlockingDeque q5mRci;
private LinkedBlockingDeque q5mAwesome;
private LinkedBlockingDeque q5mStochFK;
private LinkedBlockingDeque q5mStochFD;
private LinkedBlockingDeque q5mAtrS;
private LinkedBlockingDeque q5mAtrL;
private LinkedBlockingDeque q5mWaddahAttar;
private LinkedBlockingDeque q5mKeltnerMid;
private LinkedBlockingDeque q5mVolumeWAP;
private LinkedBlockingDeque q5mT3;
private LinkedBlockingDeque q5mIBarBid;
private LinkedBlockingDeque q5mIBarAsk;
private LinkedBlockingDeque q1hKeltnerUp;
private LinkedBlockingDeque q1hKeltnerMid;
private LinkedBlockingDeque q1hKeltnerLow;
private LinkedBlockingDeque q1hMaPrice;
public LfxMarketInfo(IContext context, Instrument instrument) {
this.engine = context.getEngine();
this.console = context.getConsole();
this.history = context.getHistory();
this.context = context;
this.indicators = context.getIndicators();
this.managingInstrument = instrument;
this.q5mEma = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mIchimokuSenkouB = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mMamaFama = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mFractalLineHigh = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mFractalLineLow = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mRci = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mAwesome = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mStochFK = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mStochFD = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mAtrS = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mAtrL = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mWaddahAttar = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mKeltnerMid = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mVolumeWAP = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mT3 = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mIBarBid = new LinkedBlockingDeque(QUEUE_SIZE);
this.q5mIBarAsk = new LinkedBlockingDeque(QUEUE_SIZE);
this.q1hKeltnerUp = new LinkedBlockingDeque(QUEUE_SIZE);
this.q1hKeltnerMid = new LinkedBlockingDeque(QUEUE_SIZE);
this.q1hKeltnerLow = new LinkedBlockingDeque(QUEUE_SIZE);
this.q1hMaPrice = new LinkedBlockingDeque(QUEUE_SIZE);
}
public boolean isInitialized() {
return this.isInitialized;
}
public int getQueueSize() {
return QUEUE_SIZE;
}
public void init() throws JFException {
int i;
IBar firstBidbar5M = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.BID, 1);
IBar firstAskbar5M = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.ASK, 1);
IBar firstBidbar15M = this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.BID, 1);
IBar firstAskbar15M = this.history.getBar(this.managingInstrument, Period.FIFTEEN_MINS, OfferSide.ASK, 1);
IBar firstBidbar1H = this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.BID, 1);
IBar firstAskbar1H = this.history.getBar(this.managingInstrument, Period.ONE_HOUR, OfferSide.ASK, 1);
for (i = QUEUE_SIZE; i > 0; i--) {
updateMarketInfo(this.managingInstrument, Period.FIVE_MINS, firstAskbar5M, firstBidbar5M, i);
updateMarketInfo(this.managingInstrument, Period.FIFTEEN_MINS, firstAskbar15M, firstBidbar15M, i);
updateMarketInfo(this.managingInstrument, Period.ONE_HOUR, firstAskbar1H, firstBidbar1H, i);
}
this.isInitialized = true;
}
private boolean updateMarketInfo(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {
if (period == Period.FIVE_MINS) {
updateMarketInfo5Mins(instrument, period, askBar, bidBar, shift);
} else if (period == Period.FIFTEEN_MINS) {
updateMarketInfo15Mins(instrument, period, askBar, bidBar, shift);
} else if (period == Period.ONE_HOUR) {
updateMarketInfo1Hour(instrument, period, askBar, bidBar, shift);
} else {
return false;
}
return true;
}
public boolean updateMarketInfo(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
if (instrument != this.managingInstrument) {
this.console.getOut().println("wrong instrument!!");
return false;
}
if (period == Period.FIVE_MINS) {
updateMarketInfo5Mins(instrument, period, askBar, bidBar);
} else if (period == Period.FIFTEEN_MINS) {
updateMarketInfo15Mins(instrument, period, askBar, bidBar);
} else if (period == Period.ONE_HOUR) {
updateMarketInfo1Hour(instrument, period, askBar, bidBar);
} else {
return false;
}
return true;
}
private boolean updateMarketInfo5Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
return updateMarketInfo5Mins(instrument, period, askBar, bidBar, 1);
}
private boolean updateMarketInfo5Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {
// INDICATOR-01
double ema = this.indicators.ema(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
20,
shift);
// INDICATOR-02
double[] ichimoku = this.indicators.ichimoku(instrument,
period,
OfferSide.BID,
9,
26,
52,
shift);
// INDICATOR-03
double[] mama = this.indicators.mama(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
0.3,
0.05,
shift);
// INDICATOR-04
double[] fractal = this.indicators.fractalLines(instrument,
period,
OfferSide.BID,
12,
shift);
// INDICATOR-05
double rci = this.indicators.rci(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
36,
shift);
// INDICATOR-06
double[] awesome = this.indicators.awesome(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
4,
IIndicators.MaType.T3,
21,
IIndicators.MaType.T3,
shift);
// INDICATOR-07
double[] stochF = this.indicators.stochF(instrument,
period,
OfferSide.BID,
12,
4,
IIndicators.MaType.EMA,
shift);
// INDICATOR-08
double atrS = this.indicators.atr(instrument,
period,
OfferSide.BID,
12,
shift);
// INDICATOR-09
double atrL = this.indicators.atr(instrument,
period,
OfferSide.BID,
24,
shift);
// INDICATOR-10
double waddahAttar = this.indicators.waddahAttar(instrument,
period,
OfferSide.BID,
shift);
// INDICATOR-11
double keltner[] = this.indicators.keltner(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
10,
shift);
// INDICATOR-12
double volumeWAP = this.indicators.volumeWAP(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.WEIGHTED_CLOSE,
4,
shift);
// INDICATOR-13
double t3 = this.indicators.t3(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
20,
0.8,
shift);
//
if (!this.q5mEma.offer(ema)) {
this.q5mEma.poll();
this.q5mEma.offer(ema);
}
if (!this.q5mIchimokuSenkouB.offer(ichimoku[ICHIMOKU_SENKOU_B])) {
this.q5mIchimokuSenkouB.poll();
this.q5mIchimokuSenkouB.offer(ichimoku[ICHIMOKU_SENKOU_B]);
}
if (!this.q5mMamaFama.offer(mama[MAMA_FAMA])) {
this.q5mMamaFama.poll();
this.q5mMamaFama.offer(mama[MAMA_FAMA]);
}
if (!this.q5mFractalLineHigh.offer(fractal[FRACTALLINES_HIGH])) {
this.q5mFractalLineHigh.poll();
this.q5mFractalLineHigh.offer(fractal[FRACTALLINES_HIGH]);
}
if (!this.q5mFractalLineLow.offer(fractal[FRACTALLINES_LOW])) {
this.q5mFractalLineLow.poll();
this.q5mFractalLineLow.offer(fractal[FRACTALLINES_LOW]);
}
if (!this.q5mRci.offer(rci)) {
this.q5mRci.poll();
this.q5mRci.offer(rci);
}
double awesomeVal;
if (!Double.isNaN(awesome[AWESOME_POSITIVE])) {
awesomeVal = awesome[AWESOME_POSITIVE];
} else {
awesomeVal = awesome[AWESOME_NEGATIVE];
}
if (!this.q5mAwesome.offer(awesomeVal)) {
this.q5mAwesome.poll();
this.q5mAwesome.offer(awesomeVal);
}
if (!this.q5mStochFK.offer(stochF[STOCHF_FAST_K])) {
this.q5mStochFK.poll();
this.q5mStochFK.offer(stochF[STOCHF_FAST_K]);
}
if (!this.q5mStochFD.offer(stochF[STOCHF_FAST_D])) {
this.q5mStochFD.poll();
this.q5mStochFD.offer(stochF[STOCHF_FAST_D]);
}
if (!this.q5mAtrS.offer(atrS)) {
this.q5mAtrS.poll();
this.q5mAtrS.offer(atrS);
}
if (!this.q5mAtrL.offer(atrL)) {
this.q5mAtrL.poll();
this.q5mAtrL.offer(atrL);
}
if (!this.q5mWaddahAttar.offer(waddahAttar)) {
this.q5mWaddahAttar.poll();
this.q5mWaddahAttar.offer(waddahAttar);
}
if (!this.q5mKeltnerMid.offer(keltner[KELTNER_MID])) {
this.q5mKeltnerMid.poll();
this.q5mKeltnerMid.offer(keltner[KELTNER_MID]);
}
if (!this.q5mVolumeWAP.offer(volumeWAP)) {
this.q5mVolumeWAP.poll();
this.q5mVolumeWAP.offer(volumeWAP);
}
if (!this.q5mT3.offer(t3)) {
this.q5mT3.poll();
this.q5mT3.offer(t3);
}
// IBarのストア
IBar hBidBar = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.BID, shift);
IBar hAskBar = this.history.getBar(this.managingInstrument, Period.FIVE_MINS, OfferSide.ASK, shift);
if (!this.q5mIBarBid.offer(hBidBar)) {
this.q5mIBarBid.poll();
this.q5mIBarBid.offer(hBidBar);
}
if (!this.q5mIBarAsk.offer(hAskBar)) {
this.q5mIBarAsk.poll();
this.q5mIBarAsk.offer(hAskBar);
}
return true;
}
private boolean updateMarketInfo15Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
return updateMarketInfo15Mins(instrument, period, askBar, bidBar, 1);
}
private boolean updateMarketInfo15Mins(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {
return true;
}
private boolean updateMarketInfo1Hour(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
return updateMarketInfo1Hour(instrument, period, askBar, bidBar, 1);
}
private boolean updateMarketInfo1Hour(Instrument instrument, Period period, IBar askBar, IBar bidBar, int shift) throws JFException {
long targetBarStartTime = this.history.getTimeForNBarsBack(Period.ONE_HOUR, bidBar.getTime(), shift);
java.util.List bars = this.history.getBars(instrument,
period,
OfferSide.BID,
Filter.ALL_FLATS,
24,
targetBarStartTime,
0);
long pastNBarStartTime = bars.get(0).getTime();
double keltner[][] = this.indicators.keltner(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
12,
Filter.ALL_FLATS,
pastNBarStartTime,
targetBarStartTime);
double[] keltnerUp = keltner[KELTNER_UP];
double[] keltnerMid = keltner[KELTNER_MID];
double[] keltnerLow = keltner[KELTNER_LOW];
double[] ima = this.indicators.ma(instrument,
period,
OfferSide.BID,
IIndicators.AppliedPrice.CLOSE,
4,
IIndicators.MaType.TRIMA,
Filter.ALL_FLATS,
pastNBarStartTime,
targetBarStartTime);
//
if (!q1hKeltnerUp.offer(keltnerUp[keltnerUp.length - 1])) {
q1hKeltnerUp.poll();
q1hKeltnerUp.offer(keltnerUp[keltnerUp.length - 1]);
}
if (!q1hKeltnerMid.offer(keltnerMid[keltnerMid.length - 1])) {
q1hKeltnerMid.poll();
q1hKeltnerMid.offer(keltnerMid[keltnerMid.length - 1]);
}
if (!q1hKeltnerLow.offer(keltnerLow[keltnerLow.length - 1])) {
q1hKeltnerLow.poll();
q1hKeltnerLow.offer(keltnerLow[keltnerLow.length - 1]);
}
if (!q1hMaPrice.offer(ima[ima.length - 1])) {
q1hMaPrice.poll();
q1hMaPrice.offer(ima[ima.length - 1]);
}
return true;
}
public Deque getQ5mEma() {
return this.q5mEma;
}
public Deque getQ5mIchimokuSenkouB() {
return this.q5mIchimokuSenkouB;
}
public Deque getQ5mMamaFama() {
return this.q5mMamaFama;
}
public Deque getQ5mFractalLineHigh() {
return this.q5mFractalLineHigh;
}
public Deque getQ5mFractalLineLow() {
return this.q5mFractalLineLow;
}
public Deque getQ5mRci() {
return this.q5mRci;
}
public Deque getQ5mAwesome() {
return this.q5mAwesome;
}
public Deque getQ5mAtrS() {
return this.q5mAtrS;
}
public Deque getQ5mAtrL() {
return this.q5mAtrL;
}
public Deque getQ5mVolumeWAP() {
return this.q5mVolumeWAP;
}
public Deque getQ5mT3() {
return this.q5mT3;
}
public Deque getQ5mIBarBid() {
return this.q5mIBarBid;
}
}
class LfxTradingMode {
private Instrument managingInstrument;
private TradingState tradingState;
private boolean isBullTrend;
private long setUpTime;
private long setDownTime;
private long reSetUpTime;
// マーケット情報に依存する
private LfxMarketInfo marketInfo;
// チャート描写機能を全般的に検討する必要がある
// 処理本体では、モード切り替えを判別するのが困難なので、このインスタンスでなんとかするのが良いと思う
private LfxChartUtil chartUtil;
// ストラテジパラメータ
private final long FORBID_NEXT_SET_UP_DURATION = 1000 * 60 * 15;
private final double SETUP_MARGIN_PIPS = 2;
private final double SETDOWN_MARGIN_PIPS = 2;
public LfxTradingMode(LfxMarketInfo marketInfo, Instrument instrument) {
this.marketInfo = marketInfo;
this.setDownTime = 0;
this.setUpTime = 0;
this.tradingState = TradingState.LESS;
this.managingInstrument = instrument;
}
// トレンドの状態。各遷移する
public static enum TradingState {
SET_UP("1-SET_UP"),
CALM_DOWN("2-CALM_DOWN"),
RE_SET_UP("3-RE_SET_UP"),
SET_DOWN("4-SET_DOWN"),
LESS("0-LESS");
private String name;
TradingState(String name) { this.name = name; }
public String getName() { return name; }
}
public boolean isTradableMarketState(long processTime) throws JFException {
// 状況アナライズ
analyzeForTradingState(processTime);
if (getTradingState() == TradingState.LESS ||
getTradingState() == TradingState.SET_DOWN) {
return false;
} else {
return true;
}
}
public TradingState analyzeForTradingState(long processTime) throws JFException {
// セットアップ禁止期間なら禁止解除期間まで経過していないかチェックする
if (getTradingState() == TradingState.SET_DOWN) {
if ((processTime - setDownTime) > FORBID_NEXT_SET_UP_DURATION) {
setTradingState(TradingState.LESS);
}
}
// LESSならセットアップを探す
if (getTradingState() == TradingState.LESS) {
// 0: T3_MAを利用してチェックする方向のあたりをつける
setUpSearchingDirection();
// セットアップを探すメソッド
searchForTradingSetUp(processTime);
} else if (getTradingState() == TradingState.SET_UP||
getTradingState() == TradingState.RE_SET_UP) {
// UP済みなら、弱くなっていないかチェックする
searchForTradingCalmDown(processTime);
}
if (getTradingState() == TradingState.CALM_DOWN) {
// セットダウン条件かチェックする
searchForTradingSetDown(processTime);
// 再度強いトレンド状態に復帰していないかチェックする
if (getTradingState() != TradingState.SET_DOWN) {
searchForTradingSetUp(processTime);
}
}
return getTradingState();
}
public TradingState getTradingState() {
return this.tradingState;
}
private void setTradingState(TradingState state) {
this.tradingState = state;
}
// 状態の遷移を管理するメソッド(内部処理)
// 多分使ってないし、使わないかも
private TradingState getNextTradingSet(TradingState currentTradingSet) {
return TradingState.LESS;
}
// トレンドの方向を取得するメソッド
// true: 上昇(BULL)トレンドの処理中
public boolean isBullTrend() {
return this.isBullTrend;
}
// いつからトレーディングモードになったのか判別が必要
public long getSetUpTime() {
return this.setUpTime;
}
private void setSetUpTime(long setUpTime) {
this.setUpTime = setUpTime;
}
private void setSetDownTime(long setDownTime) {
this.setDownTime = setDownTime;
}
private void setReSetUpTime(long reSetUpTime) {
this.reSetUpTime = reSetUpTime;
}
public long getReSetUpTime() {
return this.reSetUpTime;
}
public long getLastSetUpTime() {
if (this.setUpTime > this.reSetUpTime) {
return this.setUpTime;
} else {
return this.reSetUpTime;
}
}
public String toString() {
StringBuffer sb = new StringBuffer();
return sb.toString();
}
////////////////////////////////////////////////////////////////////////////////
// ロジック本体:::セットアップを管理する - ここの実装で多様性が生まれる、というテンプレート
////////////////////////////////////////////////////////////////////////////////
// セットアップを調査する売買方向をチェックする
private void setUpSearchingDirection() throws JFException {
Iterator it = this.marketInfo.getQ5mT3().descendingIterator();
double cur = it.next();
double pre = it.next();
double prepre = it.next();
double preprepre = it.next();
if (cur > pre && pre > prepre && prepre > preprepre) {
this.isBullTrend = true;
} else if (cur < pre && pre < prepre && prepre < preprepre) {
this.isBullTrend = false;
}
}
// セットアップをチェックする
private TradingState searchForTradingSetUp(long processTime) throws JFException {
boolean blCkLogic1 = false;
boolean blCkLogic2 = false;
boolean blCkLogic3 = false;
boolean blCkLogic4 = false;
// 1: 30分以内の範囲で、ロースク足がフラクタルラインをブレイクしていないかチェックする
if (isBullTrend()) {
Iterator candle = this.marketInfo.getQ5mIBarBid().descendingIterator();
Iterator fractal = this.marketInfo.getQ5mFractalLineHigh().descendingIterator();
// 30分
for (int i = 0; i < 6; i++) { if (candle.next().getClose() > fractal.next()) {
blCkLogic1 = true;
}
}
} else {
Iterator candle = this.marketInfo.getQ5mIBarBid().descendingIterator();
Iterator fractal = this.marketInfo.getQ5mFractalLineLow().descendingIterator();
// 30分
for (int i = 0; i < 6; i++) {
if (candle.next().getClose() < fractal.next()) { blCkLogic1 = true; } } } // 2: 現時点でVolumeWAPがフラクタルラインをブレイクしているか(ダマシマージンチェックあり) if (isBullTrend()) { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast(); if (volumeWAP > (fractal + (SETUP_MARGIN_PIPS * this.managingInstrument.getPipValue()))) {
blCkLogic2 = true;
}
} else {
double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast();
double fractal = this.marketInfo.getQ5mFractalLineLow().getLast();
if (volumeWAP < (fractal - (SETUP_MARGIN_PIPS * this.managingInstrument.getPipValue()))) { blCkLogic2 = true; } } // 3: Awesomeが順方向か if (isBullTrend()) { double awesome = this.marketInfo.getQ5mAwesome().getLast(); if (awesome > 0) {
blCkLogic3 = true;
}
} else {
double awesome = this.marketInfo.getQ5mAwesome().getLast();
if (awesome < 0) {
blCkLogic3 = true;
}
}
// 4: 短期ATRが長期ATRを上回っているか (3期間チェックして、2期間その通りならOK)
int l4Count = 0;
Iterator atrS = this.marketInfo.getQ5mAtrS().descendingIterator();
Iterator atrL = this.marketInfo.getQ5mAtrL().descendingIterator();
for (int i = 0; i < 3; i++) { if (atrS.next() > atrL.next()) {
l4Count++;
}
}
if (l4Count >= 2) {
blCkLogic4 = true;
}
// 5: RCI(30)がある方向への遷移を示しているか
// チェックOKなら
if (blCkLogic1 && blCkLogic2 && blCkLogic3 && blCkLogic4) {
if (getTradingState() == TradingState.LESS) {
// LESSなら
// セットアップタイムを設定
setSetUpTime(processTime);
// セットアップ状態へ遷移
setTradingState(TradingState.SET_UP);
} else if (getTradingState() == TradingState.CALM_DOWN) {
// CALM_DOWNなら
// 再セットアップタイムを設定
setReSetUpTime(processTime);
// 再度セットアップ状態へ遷移
setTradingState(TradingState.RE_SET_UP);
}
}
return getTradingState();
}
// セットアップが弱くなっていないかチェックする
private TradingState searchForTradingCalmDown(long processTime) throws JFException {
boolean blCkLogic1 = false;
boolean blCkLogic2 = false;
boolean blCkLogic3 = false;
boolean blCkLogic5 = false;
// 1: RCIのはがれ落ち。セットアップの2時間後に有効になるロジック
if ((processTime - getLastSetUpTime()) > 1000 * 60 * 60 * 2) {
Iterator iRci = this.marketInfo.getQ5mRci().descendingIterator();
int l1Count = 0;
double rciNext = iRci.next();
double rciPre;
if (isBullTrend()) {
for (int i = 0; i < 18; i++) { rciPre = iRci.next(); if (rciPre > rciNext) {
l1Count++;
}
rciNext = rciPre;
}
} else {
for (int i = 0; i < 18; i++) {
rciPre = iRci.next();
if (rciPre < rciNext) { l1Count++; } rciNext = rciPre; } } if (l1Count >= 14) {
blCkLogic1 = true;
}
}
// 2: RCIの振動
if ((processTime - getLastSetUpTime()) > 1000 * 60 * 60 * 2) {
Iterator iRci = this.marketInfo.getQ5mRci().descendingIterator();
int l2CountPlus = 0;
int l2CountMinus = 0;
double rciNext = iRci.next();
double rciPre;
for (int i = 0; i < 50; i++) {
rciPre = iRci.next();
if (rciPre < rciNext) {
l2CountPlus++;
} else {
l2CountMinus++;
}
rciNext = rciPre;
}
if (Math.abs(l2CountPlus - l2CountMinus) < 10) {
blCkLogic2 = true;
}
}
// 3: フラクタルのなかに転落していないか
if (isBullTrend()) {
double price = this.marketInfo.getQ5mVolumeWAP().getLast();
double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast();
if (price < fractal) { blCkLogic3 = true; } } else { double price = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineLow().getLast(); if (price > fractal) {
blCkLogic3 = true;
}
}
// 4: Awesomeは順方向であること(逆方向になっていると黄色信号)
// 5: 先行スパンBをローソク足がブレイクアウトしていないこと(直近3ローソク中2本)
int l5Count = 0;
Iterator price = this.marketInfo.getQ5mIBarBid().descendingIterator();
Iterator senkouB = this.marketInfo.getQ5mIchimokuSenkouB().descendingIterator();
if (isBullTrend()) {
for (int i = 0; i < 3; i++) {
if (price.next().getClose() < senkouB.next()) {
l5Count++;
}
}
} else {
for (int i = 0; i < 3; i++) { if (price.next().getClose() > senkouB.next()) {
l5Count++;
}
}
}
if (l5Count >= 2) {
blCkLogic5 = true;
}
// 6: MAMAはほぼ全て順方向であること
// チェックOKなら
// CALM_DOWN状態へ遷移
if (blCkLogic1 || blCkLogic2 || blCkLogic3 || blCkLogic5) {
// CALM_DOWN
setTradingState(TradingState.CALM_DOWN);
}
return getTradingState();
}
// セットダウンの時か
private TradingState searchForTradingSetDown(long processTime) throws JFException {
boolean blCkLogic1 = false;
boolean blCkLogic2 = false;
//テスト実装
if ((processTime - this.setUpTime) > 1000 * 60 * 60 * 24) {
setTradingState(TradingState.SET_DOWN);
setSetDownTime(processTime);
}
// 1: 逆フラクタルをブレイクしていたらアウト
if (isBullTrend()) {
double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast();
double fractal = this.marketInfo.getQ5mFractalLineLow().getLast();
if (volumeWAP < fractal) { blCkLogic1 = true; } } else { double volumeWAP = this.marketInfo.getQ5mVolumeWAP().getLast(); double fractal = this.marketInfo.getQ5mFractalLineHigh().getLast(); if (volumeWAP > fractal) {
blCkLogic1 = true;
}
}
// 2: MAMAが長期に渡り逆にしなっているとアウト(チェック期間の80%とする)
if ((processTime - getSetUpTime()) > 1000 * 60 * 60 * 2) {
final int ck2Threshold = (int)(this.marketInfo.getQueueSize() * 0.8);
int ck2i = 0;
Iterator ite2 = this.marketInfo.getQ5mMamaFama().iterator();
double val1;
double val2;
for (val1 = ite2.next(); ite2.hasNext(); ) {
val2 = ite2.next();
// BULL状態でBEARに長期間なるとNGというチェックロジック
if (isBullTrend()) {
if (val2 < val1) { ck2i++; } } else { if (val2 > val1) {
ck2i++;
}
}
val1 = val2;
}
if (ck2i > ck2Threshold) {
blCkLogic2 = true;
} else {
blCkLogic2 = false;
}
}
// 3: Awesomeが反対方向になっていると黄色信号
// 4: RCIが反対方向へ向かって落ちすぎているとアウト
// 5: ダイバージェンスを検出したら黄色信号
// 6: ATRが加速していると黄色信号
// アウトを検出したらセットダウン
if (blCkLogic1 || blCkLogic2) {
setTradingState(TradingState.SET_DOWN);
setSetDownTime(processTime);
}
// セットダウン時間を設定する
return getTradingState();
}
}
// TODO::稼働途中でチャートが閉じられると、nullpointerで落ちると思う
class LfxChartUtil {
private IEngine engine;
private IConsole console;
private IHistory history;
private IContext context;
private IIndicators indicators;
private IChart chart;
private Instrument managingInstrument;
private boolean isDrawable;
public LfxChartUtil(IContext context, IChart chart, Instrument instrument) {
this.engine = context.getEngine();
this.console = context.getConsole();
this.history = context.getHistory();
this.context = context;
this.indicators = context.getIndicators();
this.managingInstrument = instrument;
this.chart = chart;
if (chart == null) {
this.isDrawable = false;
} else {
this.isDrawable = true;
}
}
public void setUndrawable() {
this.isDrawable = false;
}
private boolean isManagingInstrument(Instrument instrument) {
if (instrument == this.managingInstrument) {
return true;
} else {
return false;
}
}
public void drawArrowUpOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException {
if (this.isDrawable) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
ISignalUpChartObject signal = factory.createSignalUp("AU" + bar.getTime(),
bar.getTime(),
bar.getLow());
signal.setColor(color);
this.chart.addToMainChart(signal);
}
}
public void drawArrowDownOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException {
if (this.isDrawable) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
ISignalDownChartObject signal = factory.createSignalDown("AD" + bar.getTime(),
bar.getTime(),
bar.getHigh());
signal.setColor(color);
chart.addToMainChart(signal);
signal.setColor(color);
this.chart.addToMainChart(signal);
}
}
public void drawEllipseOnChart(Instrument instrument, Period period, IBar bar, Color color) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
IEllipseChartObject ellipse = factory.createEllipse("EM" + bar.getTime(),
this.history.getPreviousBarStart(period, bar.getTime()),
bar.getClose() + (1.5 * instrument.getPipValue()),
this.history.getNextBarStart(period, bar.getTime()),
bar.getClose() - (1.5 * instrument.getPipValue()));
ellipse.setColor(color);
chart.addToMainChart(ellipse);
}
}
public void drawEllipseOnChart(Instrument instrument, Period period, IBar bar, Color color, double price) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
IEllipseChartObject ellipse = factory.createEllipse("EM" + bar.getTime(),
this.history.getPreviousBarStart(period, bar.getTime()),
price + (1.5 * instrument.getPipValue()),
this.history.getNextBarStart(period, bar.getTime()),
price - (1.5 * instrument.getPipValue()));
ellipse.setColor(color);
chart.addToMainChart(ellipse);
}
}
public void drawShortLineOnChart(Instrument instrument, Period period, IBar bar, Color color, double price) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
IShortLineChartObject sline = factory.createShortLine("SL" + bar.getTime(),
this.history.getPreviousBarStart(period, bar.getTime()),
price,
this.history.getNextBarStart(period, bar.getTime()),
price);
sline.setColor(color);
chart.addToMainChart(sline);
}
}
// セットアップ
public void drawTradingSetUpOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
IRectangleChartObject rectangle = factory.createRectangle("REC" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
bar.getClose() + (7 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, this.history.getPreviousBarStart(period, bar.getTime()), 6),
bar.getClose() - (7 * instrument.getPipValue()));
rectangle.setColor(Color.CYAN);
chart.addToMainChart(rectangle);
if (isLong) {
drawArrowUpOnChart(instrument, period, bar, Color.CYAN);
} else {
drawArrowDownOnChart(instrument, period, bar, Color.CYAN);
}
}
}
// トレンドの小康状態
public void drawTradingCalmDownOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException {
drawTradingStateOnChartSub(instrument, period, bar, Color.PINK, isLong);
}
// トレンドの復調
public void drawTradingReSetUpOnChart(Instrument instrument, Period period, IBar bar, boolean isLong) throws JFException {
drawTradingStateOnChartSub(instrument, period, bar, Color.CYAN, isLong);
}
private void drawTradingStateOnChartSub(Instrument instrument, Period period, IBar bar, Color color, boolean isLong) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
ITriangleChartObject triangle1;
ITriangleChartObject triangle2;
if (isLong) {
// 上の三角が小さい
triangle1 = factory.createTriangle("TRI1" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
bar.getClose() + (1.2 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
bar.getClose() + (1.2 * instrument.getPipValue()),
bar.getTime(),
bar.getClose() + (0.1 * instrument.getPipValue()));
// 下の三角が大きい
triangle2 = factory.createTriangle("TRI2" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 3),
bar.getClose() - (3 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, bar.getTime(), 3),
bar.getClose() - (3 * instrument.getPipValue()),
bar.getTime(),
bar.getClose() - (0.1 * instrument.getPipValue()));
} else {
// 上の三角が大きい
triangle1 = factory.createTriangle("TRI1" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 3),
bar.getClose() + (3 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, bar.getTime(), 3),
bar.getClose() + (3 * instrument.getPipValue()),
bar.getTime(),
bar.getClose() + (0.1 * instrument.getPipValue()));
// 下の三角が小さい
triangle2 = factory.createTriangle("TRI2" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 2),
bar.getClose() - (1.2 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, bar.getTime(), 2),
bar.getClose() - (1.2 * instrument.getPipValue()),
bar.getTime(),
bar.getClose() - (0.1 * instrument.getPipValue()));
}
triangle1.setColor(color);
triangle2.setColor(color);
chart.addToMainChart(triangle1);
chart.addToMainChart(triangle2);
}
}
// セットダウン
public void drawTradingSetDownOnChart(Instrument instrument, Period period, IBar bar) throws JFException {
if (this.isDrawable && isManagingInstrument(instrument)) {
IChartObjectFactory factory = this.chart.getChartObjectFactory();
IRectangleChartObject rectangle = factory.createRectangle("SETDOWN" + bar.getTime(),
this.history.getTimeForNBarsBack(period, bar.getTime(), 3),
bar.getClose() + (2 * instrument.getPipValue()),
this.history.getTimeForNBarsForward(period, bar.getTime(), 3),
bar.getClose() - (2 * instrument.getPipValue()));
rectangle.setColor(Color.PINK);
chart.addToMainChart(rectangle);
drawShortLineOnChart(instrument, period, bar, Color.PINK, bar.getClose());
}
}
}
class LfxOrderUtil {
private String sID;
private String rID;
private int orderCounter;
public LfxOrderUtil(String sID, String rID) {
this.sID = sID;
this.rID = rID;
this.orderCounter = 1;
}
public String getNextLabel(Date date, Instrument instrument) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm");
DecimalFormat df = new DecimalFormat("00000");
String label = this.sID + this.rID + instrument.name() + sdf.format(date) + df.format(this.orderCounter);
this.orderCounter++;
return label;
}
public String getPosLabelIdentifier(Instrument instrument) {
return this.sID + this.rID + instrument.name();
}
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;
}
}
}
