忍者ブログ

forex tester2のインディケーターを自作

fx(外国為替証拠金取引)のトレードの練習やルールの検証をForex Tester 2というソフトでしてみます。 Meta Trader4(MT4)のインディケーター(indicator)をForex Tester2用に移植できたらしてみます。

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


Forex Tester2 で少し嵌った

ForexTester2のインディケーターを作成している時に少し嵌りました。

何故かバー(ロウソク足)が1本増える度に
全てのバーを再描画するようにCalculateが呼べれまくりました。


原因は
IndicatorBuffers(0);
だと思われます。
ダミーのバッファを作成して
IndicatorBuffers(1);
とすれば起こらなくなりました。

TIndexBuffer を使用しない場合でも最低1つは作成して登録しておかないと駄目みたいです。
登録するとプロパティに表示されるのが気に入らないです。

PR

RCI_3Line

MetaTrader4用のRCI_3Line_v130.mq4のようなものを
ForexTester2用に移植しました。こちらで販売中です。 


動画です。



販売中のインディケーター一覧はこちらです。

dumpbin.exe の実行方法

少しはまりそうになったので報告します。

Visual c++ 2010  に付属のdumpbinですが
通常のコマンドプロンプトから実行しようとすると
link.exe - システムエラー
「コンピュータに mspdb100.dll がないため、プログラムを開始できません。この問題を解決するには、プログラムを再インストールしてみてください。」
とメッセージが表示されます。

mspdb100.dllをdumpbin.exeと同じフォルダにコピーをしたらこの問題は解決できるのですが
絶対にコピーしてはいけません。

コピーした状態でビルドしようとしたら
「fatal error C1902: プログラム データベース マネージャーが一致していません。セットアップが正しく行われているか確認してください。」
というエラーが発生して悩むことになります。


正しい dumpbin.exe の実行の仕方は
スタートメニューから Visual Studio コマンド プロンプト (2010) を実行するか
Visual StudioからVisual Studio コマンド プロンプトを実行してそこで実行するものと思われます。




販売開始しました。

作成したインディケーターはgogojungleで販売することにしました。



販売中のインジケーター一覧はこちらです。


とりあえずMicrosoft Visual C++ 2010 Expressで基本形を作ってみる 

今までは無料のDelphiみたいなlazarusでインディケーター作っていましたが
Microsoft Visual C++ 2010 Expressで作ることにしました。

lazarusだとdllのサイズが大きすぎるのが1番の理由です。

c++のほうが自分の中ではポピュラーで情報が手に入れやすく
無料の開発環境が存在するのも乗り換える理由です。
cとc#が少しできるからc++も大丈夫かなと思っています。

製品のVisual C++ よりは劣るらしいですが
Forex Tester2から呼ばれる関数を作るだけならExpress Editionでも
全く問題ないと考えています。

では早速コンパイルしてみましょう。


スタートページで新しいプロジェクトを選択します。


Win32 コンソールアプリケーションを選択します。
今回はプロジェクト名とソリューション名をDemoIndiとします。



アプリケーションの種類をDLLとし空のプロジェクトをチェックします。



コードは適当につくってみました。
間違えているのは分かっていますがこのままで進めます。
ファイル名はDemoIndi.cppとしてプロジェクトのフォルダに作成します。
#include <windows.h> #include "IndicatorInterfaceUnit.h" #include "TechnicalFunctions.h" EXPORT void __stdcall Init() { IndicatorShortName("DemoStrategy"); SetOutputWindow(ow_SeparateWindow); AddLevel(0, psDot, 1, 0xFF0000); AddLevel(80, psDashDot, 1, 0x00FF00); AddLevel(-80, psDashDotDot, 1, 0x0000FF); Print("Hello, world! Init"); } EXPORT void __stdcall Done() { Print("Hello, world! Done"); } EXPORT void __stdcall Calculate() { Print("Hello, world! Calculate"); }


プロジェクトにDemoIndi.cppとIndicatorInterfaceUnit.hとTechnicalFunctions.hを追加します。
ヘッダファイルは C:\ForexTester2\Examples\Indicators\C++ にありました。



プロジェクトのフォルダ内は以下のようになっています。



この状態でビルドしたら成功してプロジェクトフォルダの下にReleaseフォルダができていました。
でもその中にはdllファイルがありませんでした。

いろいろやってみましたがどうしてもdllファイルが作成できませんでした。

諦めそうになりましたがソリューションフォルダの下にもReleaseフォルダができていて
その中にdllファイルが存在しました。

2つもReleaseフォルダができていることは知らなかったので悩んでしまいました。


dllをForexTester2のフォルダにコピーして起動してみました。
(TDllIndicator.create) Can not load indicator DemoIndi.dll, have error: Can not get "Init" proc address
というエラーが出て読み込めませんでした。



Initが無いようなのでVisual Studioに付属のdumpbinコマンドで調べてみました。
実行結果は以下です。


>dumpbin /EXPORTS DemoIndi.dll Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file C:\ForexTester2\Indicators\DemoIndi.dll File Type: DLL Section contains the following exports for DemoIndi.dll 00000000 characteristics 4C3F194D time date stamp Thu Jul 15 23:21:01 2010 0.00 version 1 ordinal base 5 number of functions 5 number of names ordinal hint RVA name 1 0 00001140 ?Calculate@@YGXXZ = ?Calculate@@YGXXZ (void __stdcall Calculate(void)) 2 1 00001120 ?Done@@YGXXZ = ?Done@@YGXXZ (void __stdcall Done(void) ) 3 2 00001080 ?Init@@YGXXZ = ?Init@@YGXXZ (void __stdcall Init(void) ) 4 3 00003358 ?IntrfProcsRec@@3UTInterfaceProcRec@@A = ?IntrfProcsRe c@@3UTInterfaceProcRec@@A (struct TInterfaceProcRec IntrfProcsRec) 5 4 00001030 ?ReplaceStr@@YGXAAPADPAD@Z = ?ReplaceStr@@YGXAAPADPAD@ Z (void __stdcall ReplaceStr(char * &,char *)) Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .rsrc 1000 .text



Initの名前が変になっています。
調査した結果defファイルを使用しないと名前が変になるようなので作成しました。
DemoIndi.def という名前で以下の内容としました。

 

LIBRARY DEMOINDI EXPORTS Init Calculate ReplaceStr IntrfProcsRec


プロジェクトのプロパティでdefファイルを指定します。

 

これでビルドして同様にdumpbinで確認してみると以下のようになりました。Init となりました。

 

>dumpbin /EXPORTS DemoIndi.dll Microsoft (R) COFF/PE Dumper Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file C:\ForexTester2\Indicators\DemoIndi.dll File Type: DLL Section contains the following exports for DEMOINDI.dll 00000000 characteristics 4C3F29E5 time date stamp Fri Jul 16 00:31:49 2010 0.00 version 1 ordinal base 5 number of functions 5 number of names ordinal hint RVA name 2 0 00001120 ?Done@@YGXXZ = ?Done@@YGXXZ (void __stdcall Done(void) ) 1 1 00001140 Calculate = ?Calculate@@YGXXZ (void __stdcall Calculat e(void)) 3 2 00001080 Init = ?Init@@YGXXZ (void __stdcall Init(void)) 4 3 00003358 IntrfProcsRec = ?IntrfProcsRec@@3UTInterfaceProcRec@@A (struct TInterfaceProcRec IntrfProcsRec) 5 4 00001030 ReplaceStr = ?ReplaceStr@@YGXAAPADPAD@Z (void __stdcal l ReplaceStr(char * &,char *)) Summary 1000 .data 1000 .rdata 1000 .reloc 1000 .rsrc 1000 .text

 

 

これでもう一度Forex Tester2を起動してみます。
エラーがなくなりました。
 



このままDemoIndiを表示させようとしたらForex Tester2が落ちました。
Init内で IndicatorBuffers(0); を実行していなかったからです。
バッファを使用しない場合は0を設定しましょう。
 
 
IndicatorBuffers(0);を追加することで無事に読み込むことができました。


わざとCalculateの引数を無しにしていましたがこちらは得に問題なしでした。



defファイルについて少し調査した結果
EXPORTS には以下の4つが必須ということが分かりました。
Init Calculate  ReplaceStr  IntrfProcsRec
以下の2つは存在すればcallされるようです。
Done  OnParamsChange
ほかにも何かあるのかもしれません。
ストラテジーは別のものが必須だと予想されますがある程度レベルが上がってから挑戦します。

パラメータを変更するタイミングで OnParamsChangeがcallされます。
インディケーターを削除するタイミングでDoneがcallされます。
存在しなければcallされないだけです。そのタイミングで処理が必要なら作成しましょう。


この状態でのdllのサイズが8kbyteです。
lazarusで作成したものは1466kbyteくらいなので約180倍です。
配布とかする場合はlazarusで作成するより
Microsoft Visual C++ 2010 Expressで作成するほうが好ましいです。



Microsoft Visual C++ 2010 Expressをインストールしていない
PCでも動作するか試しておきたいところです。





 


lazarusの日本語化

lazarusをインストールした直後は英語表示ですが
設定を変更して起動しなおすと日本語表示になります。

メニューから Environment->Options ... を選択します。

DesktopのLanguageをJapanese [ja]に変更します。
 
 


起動しなおすと日本語表示になります。

 


RCIを適当に移植

RCI_3Line_v130.mq4  を移植してみました。
Rank Correlation Index というみたいです。

以下がコードです。同じことを3回繰り返しているだけです。


procedure Calculate(index: integer); stdcall; var di : Array [ 0..51 ] of Integer; i,j,d: integer; begin if (Bars - index) < RCIPeriod3 then exit; if index <> 1 then exit; for i:=0 to RCIPeriod1 - 1 do di[i] := -2*i; d := 0; for i:=0 to RCIPeriod1 - 2 do begin for j:=i+1 to RCIPeriod1 - 1 do begin if Close(i+index) < Close(j+index) then di[i] := di[i] + 2 else if Close(i+index) > Close(j+index) then di[j] := di[j] + 2 else begin di[i] := di[i] + 1; di[j] := di[j] + 1; end; end; d := d + (di[i] * di[i]); end; d := d + (di[RCIPeriod1 - 1] * di[RCIPeriod1 - 1]); RCI1[index] := (1 - (6 * d / (RCIPeriod1 * ((RCIPeriod1 * RCIPeriod1) - 1))) / 4) * 100; for i:=0 to RCIPeriod2 - 1 do di[i] := -2*i; d := 0; for i:=0 to RCIPeriod2 - 2 do begin for j:=i+1 to RCIPeriod2 - 1 do begin if Close(i+index) < Close(j+index) then di[i] := di[i] + 2 else if Close(i+index) > Close(j+index) then di[j] := di[j] + 2 else begin di[i] := di[i] + 1; di[j] := di[j] + 1; end; end; d := d + (di[i] * di[i]); end; d := d + (di[RCIPeriod2 - 1] * di[RCIPeriod2 - 1]); RCI2[index] := (1 - (6 * d / (RCIPeriod2 * ((RCIPeriod2 * RCIPeriod2) - 1))) / 4) * 100; for i:=0 to RCIPeriod3 - 1 do di[i] := -2*i; d := 0; for i:=0 to RCIPeriod3 - 2 do begin for j:=i+1 to RCIPeriod3 - 1 do begin if Close(i+index) < Close(j+index) then di[i] := di[i] + 2 else if Close(i+index) > Close(j+index) then di[j] := di[j] + 2 else begin di[i] := di[i] + 1; di[j] := di[j] + 1; end; end; d := d + (di[i] * di[i]); end; d := d + (di[RCIPeriod3 - 1] * di[RCIPeriod3 - 1]); RCI3[index] := (1 - (6 * d / (RCIPeriod3 * ((RCIPeriod3 * RCIPeriod3) - 1))) / 4) * 100; end;




MT4と同じ感じに表示されます。


 


 



http://www.tantalusonline.com/Traditional.htm
こちらでは$14.97で販売しています。

Sessions.mq4 をForexTester2用に移植成功

適当に作ってみました。

楽するために週を跨ぐ時に強制的にBOXを閉じるようにしています。
また7:00にBOX表示開始するようにしていて週の開始が8:00のときは
そのBOXは表示しません。


procedure Calculate(index: integer); stdcall; var timeindex: TDateTime; nowdaystr: string; nowday: Word; nowhour: word; begin if first = 0 then begin if AsiaEndTime <= AsiaBeginTime then AsiaEndTime := AsiaBeginTime + 1; first := 1; print(IntToStr(AsiaBeginTime) + ' ' + IntToStr(AsiaEndTime)); end; if (index <> 0) then exit; timeindex := Time(index); nowday := DayOf(timeindex); nowhour := HourOf(timeindex); nowdaystr := IntToStr(nowday); case modeA of 0: begin if nowhour = AsiaBeginTime then begin AsiaMax := High(index); AsiaMin := Low(index); AsiaObjectName := 'A' + nowdaystr; if not(ObjectExists(AsiaObjectName + 'u')) then begin ObjectCreate(AsiaObjectName + 'u', obj_TrendLine, 0, timeindex, 0, timeindex, 0); ObjectCreate(AsiaObjectName + 'd', obj_TrendLine, 0, timeindex, 0, timeindex, 0); ObjectCreate(AsiaObjectName + 'l', obj_TrendLine, 0, timeindex, 0, timeindex, 0); ObjectCreate(AsiaObjectName + 'r', obj_TrendLine, 0, timeindex, 0, timeindex, 0); ObjectSet(AsiaObjectName + 'u', OBJPROP_COLOR, AsiaColor); ObjectSet(AsiaObjectName + 'd', OBJPROP_COLOR, AsiaColor); ObjectSet(AsiaObjectName + 'l', OBJPROP_COLOR, AsiaColor); ObjectSet(AsiaObjectName + 'r', OBJPROP_COLOR, AsiaColor); ObjectSet(AsiaObjectName + 'u', OBJPROP_WIDTH, 3); ObjectSet(AsiaObjectName + 'd', OBJPROP_WIDTH, 3); ObjectSet(AsiaObjectName + 'l', OBJPROP_WIDTH, 3); ObjectSet(AsiaObjectName + 'r', OBJPROP_WIDTH, 3); end; ObjectSet(AsiaObjectName + 'l', OBJPROP_TIME1, timeindex); ObjectSet(AsiaObjectName + 'l', OBJPROP_PRICE1, AsiaMax); ObjectSet(AsiaObjectName + 'l', OBJPROP_TIME2, timeindex); ObjectSet(AsiaObjectName + 'l', OBJPROP_PRICE2, AsiaMin); ObjectSet(AsiaObjectName + 'u', OBJPROP_TIME1, timeindex); ObjectSet(AsiaObjectName + 'u', OBJPROP_PRICE1, AsiaMax); ObjectSet(AsiaObjectName + 'u', OBJPROP_TIME2, timeindex); ObjectSet(AsiaObjectName + 'u', OBJPROP_PRICE2, AsiaMax); ObjectSet(AsiaObjectName + 'd', OBJPROP_TIME1, timeindex); ObjectSet(AsiaObjectName + 'd', OBJPROP_PRICE1, AsiaMin); ObjectSet(AsiaObjectName + 'd', OBJPROP_TIME2, timeindex); ObjectSet(AsiaObjectName + 'd', OBJPROP_PRICE2, AsiaMin); if not(ObjectExists('Atu')) then begin ObjectCreate('Atu', obj_Text, 0, 0, 0); ObjectSet('Atu', OBJPROP_VALIGNMENT, tlBottom); ObjectSet('Atu', OBJPROP_HALIGNMENT, taRightJustify); ObjectCreate('Atd', obj_Text, 0, 0, 0); ObjectSet('Atd', OBJPROP_VALIGNMENT, tlTop); ObjectSet('Atd', OBJPROP_HALIGNMENT, taRightJustify); end; ObjectSet('Atu', OBJPROP_TIME1, timeindex); ObjectSet('Atu', OBJPROP_PRICE1, AsiaMax); ObjectSetText('Atu', FloatToStr(AsiaMax)); ObjectSet('Atd', OBJPROP_TIME1, timeindex); ObjectSet('Atd', OBJPROP_PRICE1, AsiaMin); ObjectSetText('Atd', FloatToStr(AsiaMin)); AsiaEndTime2 := IncHour(timeindex, AsiaEndTime - AsiaBeginTime); modeA := 1; end; end; 1: begin if AsiaMax < High(index) then begin AsiaMax := High(index); ObjectSet(AsiaObjectName + 'l', OBJPROP_PRICE1, AsiaMax); ObjectSet(AsiaObjectName + 'u', OBJPROP_PRICE1, AsiaMax); ObjectSet(AsiaObjectName + 'u', OBJPROP_PRICE2, AsiaMax); ObjectSet('Atu', OBJPROP_PRICE1, AsiaMax); ObjectSetText('Atu', FloatToStr(AsiaMax)); end; if AsiaMin > Low(index) then begin AsiaMin := Low(index); ObjectSet(AsiaObjectName + 'l', OBJPROP_PRICE2, AsiaMin); ObjectSet(AsiaObjectName + 'd', OBJPROP_PRICE1, AsiaMin); ObjectSet(AsiaObjectName + 'd', OBJPROP_PRICE2, AsiaMin); ObjectSet('Atd', OBJPROP_PRICE1, AsiaMin); ObjectSetText('Atd', FloatToStr(AsiaMin)); end; ObjectSet(AsiaObjectName + 'u', OBJPROP_TIME2, timeindex); ObjectSet(AsiaObjectName + 'd', OBJPROP_TIME2, timeindex); ObjectSet('Atu', OBJPROP_TIME1, timeindex); ObjectSet('Atd', OBJPROP_TIME1, timeindex); if timeindex >= AsiaEndTime2 then begin ObjectSet(AsiaObjectName + 'u', OBJPROP_TIME2, AsiaEndTime2); ObjectSet(AsiaObjectName + 'd', OBJPROP_TIME2, AsiaEndTime2); ObjectSet(AsiaObjectName + 'r', OBJPROP_TIME1, AsiaEndTime2); ObjectSet(AsiaObjectName + 'r', OBJPROP_PRICE1, AsiaMax); ObjectSet(AsiaObjectName + 'r', OBJPROP_TIME2, AsiaEndTime2); ObjectSet(AsiaObjectName + 'r', OBJPROP_PRICE2, AsiaMin); ObjectSet('Atu', OBJPROP_TIME1, AsiaEndTime2); ObjectSet('Atd', OBJPROP_TIME1, AsiaEndTime2); modeA := 0; end; end; end; end;


このインディケーターはAsia用ですがObject名の被らないものを作ってEuro、Ny用のインディケーターにしています。
1つのインディケーターにすることも可能でしょう。



こんな感じで表示します。枠内の塗りつぶしはできないと思われます。




同じ頃の本家の表示です。大体似通っています。 
 


動画もあります


ちょっとはまったこと

オブジェクトの作成で少しはまりました。
Initの中でObjectを作成しているのに全く表示できませんでした。
Calculateの中の処理が悪いと思いいろいろ試しましたが無理でした。

そこで ObjectCreate の戻り値を確認してみたらInitでは失敗していました。

以下が確認用のコードです。


procedure Init; stdcall; begin IndicatorShortName('i-session'); SetOutputWindow(ow_ChartWindow); IndicatorBuffers(0); if not(ObjectExists('t1')) then begin print('create object t1 in Init'); if ObjectCreate('t1', obj_Text, 0, 0, 0) then print('ok') else print('ng'); ObjectSetText('t1', 'Init', 12, 'Arial', clYellow); end; end; procedure Done; stdcall; begin ObjectDelete('t1'); end; procedure Calculate(index: integer); stdcall; begin if (index <> 0) or (Bars < 80) then exit; print(IntToStr(Timeframe())); if not(ObjectExists('t1')) then begin print('create object t1 in Calc'); if ObjectCreate('t1', obj_Text, 0, 0, 0) then print('ok') else print('ng'); end; ObjectSetText('t1', FormatDateTime('YYYY/MM/DD HH:MM:SS',Time(0)), 12, 'Arial', clYellow); ObjectSet('t1', OBJPROP_TIME1, Time(0)); ObjectSet('t1', OBJPROP_PRICE1, High(0)); end;


Doneの中のObject削除処理は有効でした。
Calculateの中でObjectを作成する必要があるようです。






それともうひとつふたつ確認したことがあります。Timeframe()の戻り値はチャートに同期するかです。
結果はインディケーターを読み込んだ時の値(分)が返ります。
1H足表示中にインディケーターを追加したら
それ以外の時間のチャートにしても常に60を返します。


時間を変えて同じインディケーターを追加した場合は
後から追加したインディケーターのCalculateが後から実行されます。



ラッキーBINGO 初当選

Infoseek 楽天 ラッキービンゴに初当選しました。
一生当たらないものと思ってましたが稀に当たるようです。



自分で選べるようです。たぶんどれでも50ポイントだと思います。


やっぱり



forex tester2 販売
Forex Tester 2の購入はこちらから。
カレンダー
03 2025/04 05
S M T W T F S
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
カテゴリー
フリーエリア



最新記事
最新CM
最新TB
RSS
ブログ内検索
アーカイブ
最古記事
プロフィール
HN:
fx練習生
年齢:
50
性別:
男性
誕生日:
1975/04/03
職業:
夢は専業トレーダー
趣味:
fx
自己紹介:
fxの専業トレーダーになって経済的自由と時間的自由を手に入れたいです。
自分で売買ルールを作成してテストして自信をもってリアルトレードしたいです。
P R