このセクションでは、同じストラテジーロジックを使って、10分バーから蓮子2pipsバー(または30秒のカスタムバー)へフィードデータ型を変更します。
異なるデータフィードで動作するストラテジーを作成するには、IFeedListenerインターフェースを実装したクラスを作成する必要があります。
このクラスはIFeedListener.onFeedDataメソッドを実装する必要があります。
このメソッドはフィードデータを受信する度に呼び出されます。
サンプルコードの例は、以前作成したStopLossStrategy.javaを変更します。
フィードタイプの宣言
以下インポートを追加します。
import com.dukascopy.api.IIndicators.AppliedPrice;
import com.dukascopy.api.feed.IFeedDescriptor;
import com.dukascopy.api.feed.IFeedListener;
import com.dukascopy.api.feed.util.RenkoFeedDescriptor;
import com.dukascopy.api.feed.util.TimePeriodAggregationFeedDescriptor;
フィードデータを取得するIFeedListenerインターフェースを実装する必要があります。
IStrategyインターフェースが実装されている同じクラスにIFeedListenerインターフェースを実装します。
public class Feeds implements IStrategy, IFeedListener {
}
コード内で複数のデータフィードタイプを使用している場合、使用している通貨ペア、オーダータイプ、価格期間、時間軸をフィード登録する必要があります。
これらの値は全てIFeedDescriptor要素から取得出来ます。
フィードタイプに応じて、今まで使用していた同じ役割の変数をIFeedDescriptorメソッドから呼ばれる変数に置換します。
なので、myInstrumentとmyOfferSideとmyPeriodのパラメータは削除します。
この例では、ストラテジー起動時に2種類のフィードタイプのどちらかを選択出来るようにするので、新しいパラメータを追加します。
ユーザーが選択したフィードを登録する必要があります。
その為、新しいenum型を宣言します。
このenum型は定数のIFeedDescriptor変数(final修飾子付き変数)の為のコンストラクタを持っています。
これにより、選択されたデータフィード(通貨ペア、時間軸等)についての情報を取得するenum(FeedType)のコンストラクタを使う事が出来ます。
@Configurable("フィードタイプ")
public FeedType myFeed = FeedType.RENKO_2_PIPS_EURGBP_BID;
public enum FeedType {
RENKO_2_PIPS_EURGBP_BID ( new RenkoFeedDescriptor(Instrument.EURGBP, PriceRange.TWO_PIPS, OfferSide.BID)),
TIME_BAR_30_SEC_EURGBP_BID (new TimePeriodAggregationFeedDescriptor(Instrument.EURGBP, Period.THIRTY_SECS,
OfferSide.BID, Filter.WEEKENDS));
private final IFeedDescriptor feedDescriptor;
FeedType(IFeedDescriptor feedDescriptor) {
this.feedDescriptor = feedDescriptor;
}
public IFeedDescriptor getFeedDescriptor(){
return feedDescriptor;
}
}
フィードの登録
onStartメソッドで、指定されたフィードタイプを登録します。
フィード登録のコードを追加します。
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();
this.openedChart = context.getChart(myFeed.getFeedDescriptor().getInstrument());
this.factory = openedChart.getChartObjectFactory();
Set<Instrument> instruments = new HashSet<Instrument>();
instruments.add(myFeed.getFeedDescriptor().getInstrument());
context.setSubscribedInstruments(instruments, true);
if( drawSMA ) {
if( !addToChart(openedChart) ){
printMeError("チャート上にインジケータをプロット出来ませんでした。チャートの設定値を確認して下さい。");
}
}
context.subscribeToFeed(myFeed.getFeedDescriptor(), this);
}
IFeedListenerインターフェースの実装
前回onBarメソッドに書いたコードを全てonFeedDataメソッドに移動します。
データフィードを使用しているので、onBarメソッドで実行する必要が無くなりました。
新しいデータを受信する度にロジックを実行するようにします。
IFeedListenerインターフェース実装する為に必要なデータを取得しまう。
このインターフェースはonFeedDataメソッドの為だけに宣言されます。
前回作成したストラテジーとの違いは、IFeedDescriptorオブジェクトから取得して通貨ペアとオーダータイプの値です。
メソッドの一部(例えば、IIndicators.calculateIndicator等)をJFExceptionに投げるので、コードは全てをtry-catchブロックに入れます。
IBarオブジェクトはITimedDataオブジェクト(IBarインタフェースはITimedDataインターフェースの拡張)をダウンキャストして受け取ります。
IFeedDescriptorオブジェクトを使用しているので、IIndicators.smaメソッドの代わりにIIndicators.calculateIndicatorメソッドを使っています、
その為、インジケータのフィードは少し異なる方法で検索します。
@Override
public void onFeedData(IFeedDescriptor feedDescriptor, ITimedData feedData) {
Instrument myInstrument = feedDescriptor.getInstrument();
OfferSide myOfferSide = feedDescriptor.getOfferSide();
try {
if ( !(feedData instanceof IBar) ) {
printMeError("ティックフィードデータは使えません。");
return;
}
IBar bar = (IBar) feedData;
int candlesBefore = 2, candlesAfter = 0;
long completedBarTimeL = bar.getTime();
Object[] smaObjectsFeed = indicators.calculateIndicator(feedDescriptor, new OfferSide[]{myOfferSide}, "SMA",
new AppliedPrice[]{AppliedPrice.CLOSE}, new Object[]{smaTimePeriod}, candlesBefore,
feedData.getTime(), candlesAfter);
double[] sma = (double[]) smaObjectsFeed[0];
IEngine.OrderCommand myCommand = null;
printMe(String.format("SMAの値: 2つ前 = %.5f; 1つ前 = %.5f", sma[SECOND_TO_LAST], sma[PREV]));
if( sma[PREV] > sma[SECOND_TO_LAST]){
printMe("SMA上昇");
myCommand = IEngine.OrderCommand.BUY;
currentSMADirection = SMATrend.UP;
} else if ( sma[PREV] < sma[SECOND_TO_LAST]){
printMe("SMA下降");
myCommand = IEngine.OrderCommand.SELL;
currentSMADirection = SMATrend.DOWN;
} else {
return;
}
double lastTickBid = history.getLastTick(myInstrument).getBid();
double lastTickAsk = history.getLastTick(myInstrument).getAsk();
double stopLossValueForLong = myInstrument.getPipValue() * stopLossPips;
double stopLossValueForShort = myInstrument.getPipValue() * takeProfitPips;
double stopLossPrice = myCommand.isLong() ? (lastTickBid - stopLossValueForLong ) : (lastTickAsk + stopLossValueForLong);
double takeProfitPrice = myCommand.isLong() ? (lastTickBid + stopLossValueForShort) : (lastTickAsk - stopLossValueForShort);
if ( currentSMADirection != previousSMADirection ) {
previousSMADirection = currentSMADirection;
IOrder newOrder = engine.submitOrder("MyStrategyOrder" + uniqueOrderCounter++, myInstrument, myCommand,
0.001, 0, 1, stopLossPrice, takeProfitPrice);
createdOrderMap.put(newOrder, false);
if( openedChart == null ){
return;
}
long time = history.getFeedData(feedDescriptor, 0).getTime();
double space = myInstrument.getPipValue() * 2;
IChartDependentChartObject signal = myCommand.isLong()
? factory.createSignalUp( "signalUpKey" + signals++, time, bar.getLow() - space)
: factory.createSignalDown("signalDownKey" + signals++, time, bar.getHigh() + space);
signal.setText("MyStrategyOrder" + (uniqueOrderCounter - 1));
openedChart.addToMainChart(signal);
}
} catch (Exception e) {
}
}
onBarメソッドの中身は空っぽのまま残っています。
public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
}
チャートチェックメソッドの変更
チャートチェックメソッドでは、通貨ペアとオーダータイプオブジェクトの取得方法を置換します。
代わりにIFeedDescriptorオブジェクトを使用します。
チャートチェックメソッドのロジックもまた、前回作成したソースから少し異なった方法に変更します。
データフィードのタイプに応じてチャートをチェックする必要があります。
private boolean checkChart(IChart chart) {
if (chart == null ) {
printMeError( myFeed.getFeedDescriptor().getInstrument() + " のチャートが開かれていません。" );
return false;
}
if (chart.getSelectedOfferSide() != myFeed.getFeedDescriptor().getOfferSide()) {
printMeError( myFeed.getFeedDescriptor().getOfferSide() + "のオーダータイプのチャートが開かれていません。" );
return false;
}
if (chart.getFeedDescriptor().getDataType() == DataType.RENKO) {
if (chart.getPriceRange() != myFeed.getFeedDescriptor().getPriceRange()) {
printMeError(myFeed.getFeedDescriptor().getPriceRange() + "の価格期間のチャートが開かれていません。" );
return false;
}
} else if (chart.getFeedDescriptor().getDataType() == DataType.TIME_PERIOD_AGGREGATION) {
if (chart.getSelectedPeriod() != myFeed.getFeedDescriptor().getPeriod()) {
printMeError( myFeed.getFeedDescriptor().getPeriod() + "の時間軸のチャートが開かれていません。" );
return false;
}
}
if (chart.getFilter() != this.filter) {
printMeError( this.filter + "のチャートフィルターではありません " );
return false;
}
return true;
}
サンプルソースコード:
Feeds.java
ストラテジーのテスト
この例では30秒足と2pips価格期間を使います。
ストラテジーを起動する前に、パラメータで指定する期間のチャートを開く必要があります。
チャートに新しい期間の選択を追加するには、
"ツール" ⇒ "オプション設定"を選択します。
オプション設定のウインドウが開いたら、"期間"クリックします。
30秒足を追加するには、"時間ベースの期間"のラジオボタンをクリックします。
"Units"で"Sec"を選択し、"タイム・フレーム"で"30秒"を選択し、ウインドウ中央の
ボタンをクリックします。
蓮子2pips足を追加するには、"価格ベースの期間"のラジオボタンをクリックします。
"種類"で"練行足"を選択し、"数量"で"2 ピップス"を選択し、ウインドウ中央の
ボタンをクリックします。
蓮子フィードでのテスト
蓮子2pipsのテストを行うには以下のパラメータ設定でストラテジーを起動します。
蓮子2pipsフィードタイプでの結果です。
SMAインジケータとロング/ショートオーダーがチャートに追加されている事が分かります。
カスタム時間足でのテスト
30秒足のテストを行うには以下のパラメータ設定でストラテジーを起動します。
30秒足フィードタイプでの結果です。