2015年6月5日金曜日

MT4のEA稼働するには仮想サーバー(VPS)が必須です。その2 初期設定

Gewinn2ですが、昨日の下げ局面でポジションを取りましたが、ロンドンフィキシング後の値動きでストップで決済されていました・・・。うーん。ストップ値の値幅の取り方に少し工夫が必要かもしれませんね。ATRベースの変動リミットで効果があるかどうかを別途検証したいと思います。随時改善を進めていきます。

昨日の引き続きVPSのお話です。

ではVPS会社を比較しましたが、実際にVPSを契約した際、やっておくべき初期設定を書いていきたいと思います。

リモートデスクトップでVPSにアクセスできた所からスタートします。
リモートデスクトップでのVPSアクセス方法は、VPSサーバ業者に詳しく書いてありますし、不明な所があればメール等でも対応してもらえますので、そちらをご確認ください。

VPS上ではなるべくソフトを動かさないのが基本的な考え方となります。ネットワークを経由してファイルが必要な場合、ローカルのPCで取得した後に、VPSにコピーするようにしましょう。

■MT4のインストール
MT4のインストーラーをリモートデスクトップで接続しているVPSにコピーして、起動します。
ローカル側のPCで、インストールファイルをエクスプローラーで選択して右クリックメニューコピーもしくはCtrl+Cでコピーします。
localinstaller.PNG
VPS上のエクスプローラーを選択して適当なフォルダに貼り付けすると、ファイルがネットワークを経由してコピーされます。
serverInstaller.PNG
serverInstaller2.PNG

あとはVPS上にコピーしたインストーラーをダブルクリックで起動して、MT4をインストールします。以後このようにローカルPCからVPSへファイルをコピーしていく前提でお話しします。

■表示をWindowsクラシックモードへ
VPSのデスクトップ上の右クリックして個人設定を選択します。
vps1.PNG


コンピュータの視覚効果を「Windowsクラシック」に変更します。契約したVPSがサーバーOSの場合は最初から選択されている場合もあります。
vps2.png
WindowsVistaから採用されているDirectXを使用した視覚効果は見た目がきれいになりますが、MT4向けのVPSとしては不要です。クラシックテーマにすることでメモリを節約できます。

■不要なサービスの停止
コントロールパネルからローカルサービスの表示を選択します。
vps3.png

不要と思われるサービスを右クリックしてプロパティを選択します。
vps4.png

スタートアップの種類を手動に変更した後、停止ボタンを押し、最後にOKを押します。
vps5.png
MT4を稼働させるだけなら、最低、次のサービスは止めてしまって問題ありません。
Windows Audio
Windows Audio Endpoint Builder

念のため、リモートデスクトップ接続時のリモートオーディオも無効にしておきます。
vps6.png

■自動ログオン
メンテナンス等で、VPSが再起動してしまった場合、MT4も止まってしまいます。
そこで、自動ログオン設定とスタートアップにMT4を入れておくことで再起動時も自動的にMT4が再開できるようにしておきます。

Autologon for WindowsからAutoLogon.zipを入手します。
解凍後、Autologon.exeファイルをVPSにファイルコピーし実行します。
vps7.png
UsernameとPasswordは、リモートデスクトップでログインするときの物です。
Domainは、起動段階で既に入力されていると思われますが、VPSの場合コンピュータ名となります。
コントロールパネルのシステムから確認できます。必要な情報を入力後Enableボタンを押して設定完了です。×ボタンでプログラムを終了します。
vps8.png


スタートメニューの全てのプログラムからスタートアップを選択して右クリックをエクスプローラーを選択します。
vps9.png


MT4をインストールするとデスクトップにショートカットができていると思いますので、そのショートカットをスタートアップの中にコピーします。
vps10.png

設定終了後、VPSを再起動してみましょう。数分待ってからリモートデスクトップでアクセスしてMT4が初期起動されていれば設定成功です。

■WindowsUpdateの設定
コントロールパネルからWindows Update→設定の変更を開きます。
「更新プログラムを自動的にインストールする」になっていると勝手に再起動がかかります。「更新プログラムを自動的にインストールする」を選択する場合は、新しい更新プログラムのインストールの欄を「毎週 土曜日 12:00」など相場が開いていない時間帯に設定します。
自分で確認してアップデートする場合は「・・・・・インストールを行うかどうかは選択する」を選択します。ダウンロードするかどうかは好みです。
vps11.png

と、ここまでがVPSで必要な設定となります。運用していくうえで、また発生するかもしれませんが、その都度追記します。次回はGewinn2を動作させるためのMT4設定を書きたいと思います。



MT4のEA稼働するには仮想サーバー(VPS)が必須です。その1 VPS会社検討

FXCMジャパンにて本番運用を開始しました。OANDAのスタンダード口座が両建て可能になったら移動するかも?
みんなのMT4で履歴を公開しています。ブログパーツをはりつけました。しかし今週はシグナルに達しないなぁ^^;;

Gewinn2ですが、基本24時間稼働放置が基本となっています。夏時間もPC時間から自動判定しますので、PCの時計がそこそこ正確であれば、チャート上の時間にかかわらず動作するよう設計しています。雇用統計は自動的に避けます。となると、家に置いてあるPCを24時間稼働させておく必要がありますが、もっと簡単に安全に仮想サーバーをレンタルするという手段があります。

月額1000円~レンタルできるため、電気代やトラブル発生時の対応を考えると、自前で用意するよりレンタルしたほうがお得です。
ということでGewinn2の連続稼働用のVPSを契約する為にあれこれ調査しました。調査内容をメモ代わりに残します。

■VPSの種類は大きく分けてLinux系とWindows系
まずVPSの種類ですが、大きく分けてLinux系とWindows系に分かれます。お値段は基本的にLinux系の方がお安いですが、MT4はWindowsプログラムです。Windows系のVPSを契約したほうが無難です。
なお、Linuxサーバー管理にそこそこ慣れている場合、WineというLinux上でWindowsアプリを動作させる仕組みを使ってMT4を稼働させることも可能です。

■サーバーの場所について
VPSを提供している会社が日本であれば、基本、日本にサーバを設置しています。
使用している証券会社のMT4サーバーが日本にあれば日本で問題ありません。ちょうどFX-ONさんに面白い記事を書いている方を見つけました。「一番早いブローカーはどこだ!?国内MT4ブローカー約定速度ランキング

ここでのFXCMとOANDAの約定速度差はネットワーク遅延の影響を受けている影響が見えます。
私の環境から測定したFXCMとOANDAのネットワーク遅延差です。
・OANDA 150ms
・FXCMジャパン  10ms
ネットワーク遅延差を差し引くとFXCMもOANDAも約定速度は変わらないように思われます。

上記からMT4サーバがアメリカにある場合は、アメリカサーバVPSを選択できる業者を選ぶと日本と比較して140msほど注文速度に差がつくことが分かります。約定拒否も少なくなることが予想されますから、スキャル系のEAを動かす場合には重要な差になりそうです。Gewinn2が動作対象としている証券会社のサーバ設置個所はこのようになります。大体MT4のFAQコーナーに記述されていますので、皆様も契約している業者のFAQページを確認してみてください。
日本サーバーアメリカサーバー
FXCMジャパン
外為ファイネスト
FOREX
OANDA
物理的距離とネットワーク的距離の関係性については、さらに議論の余地がありますが、近いほど有利なのは変わりありませんので割愛します。そのうちの記事ネタとしてとっときます^^;;tracertというルーターを追いかけるコマンドで測定すると、太平洋を横断するのに90ms、アメリカ西海岸から東海岸へが40msほどかかります。

■VPS業者比較
VPS業界も価格競争が激しく安く高性能にとなっていますが大体の最低のプランで2000円前後の価格帯となります。
大手と言えるVPSサーバー業者をピックアップしてみました。

業者名 お名前.comつかえるネット1stレンタルサーバーBeeks FX
価格帯(月額)1417~1781~ 1791~ 900~3210~
スペック
CPU:1
MEM:768M
SSD:30G
CPU:1
MEM:1G
HDD:50G
CPU:1
MEM:1G(※1)
HDD:30G 
CPU:1
MEM:512M
HDD:50G 
CPU:1
MEM:1.1G
HDD:17G
設置場所日本日本/アメリカ日本/アメリカ アメリカ アメリカ
2015/6/3現在 価格は税別、一括払い時最安値ベース
(※1)2015/6 使えるネットメモリ2倍キャンペーン中

Hyper-V系のServerOS VPSの場合、MT4を動かそうと思うとリモートデスクトップライセンス(RDS)が必要になります。それを含めての価格となります。

それぞれ、特徴があります。
・サクラインターネット
 安定性で定評があります。またインターネットとの接続の太さ(バックボーン)が353Gbpsとレンタルサーバ業界では最強に近いです。日本国内で安定動作してほしいということならサクラインターネットをお勧めします。

・お名前.com
 早くからMT4むけVPSをうたって営業をしてきました。そのためFX業界でのVPSというと、まずお名前.comを思いつく方が多いのではないでしょうか?サクラインターネットとお名前.comでVPS業界を争っています。各MT4業者と連携しており、1か月無料体験などを入り口に使いやすくなっています。アメリカサーバを選択できるのも強みです。

・つかえるネット
 お名前.comと同様にMT4のVPS活用を売りにしてきました。アメリカデータセンターがニューヨークに近いところに設置していることを明言しているあたりも好感が持てます。MT4むけVPSとしては、お名前.comとつかえるネットが競争している形となり、サービスも似通っています。価格も限りなく近いです。2015/6はキャンペーンを行っており、スタンダードでもメモリ2Gとかなりお得になっています。

・1stレンタルサーバー
世界のVPS業界の大手です。カリフォルニアにデータセンターがありますが、日本語のページがあり海外業者としては契約がしやすいのが特徴です。大手ですので強力なバックボーンがあります。MT4業者の取引サーバーは通常ニューヨークにあることが多いため、カリフォルニアとは遠いですが、日本よりはるかにましです。あと最低価格が安いです。日本のサーバーと同じ値段を払うと2CPUになるところもよいです。

・Beeks FX
 番外編です。OANDA専用といってもいいVPS会社です。なんといってもOANDAデータセンターと同じビルに入っているのが強みで、OANDAで取引する場合もっとも注文が早い会社となります。

 私は、FXCMジャパンさんが行っている、お名前.com 1か月無料体験でまず契約してみました。つかえるネットさんの2Gメモリキャンペーンも結構ひかれます・・・。

■マシンスペック考察
 必要なマシンスペックですが、一つのMT4を起動して、3~4個EAを運用する場合、メモリ512M、1CPUで十分動作します。
メモリが1Gを超えていると、二つのMT4を起動しても十分動作します。HDDの空き容量もVPSではバックテストを行わないようにすれば10Gもあれば十分です。ただし、計算が重たいEAを複数動かす場合は、2CPUあるプランを検討したほうがよさそうです。

さて、次回はVPSを契約した後、やっておくべき設定を書きたいと思います。



2015年6月3日水曜日

MT4インジケータ 移動平均より滑らかに追従よく! ウェーブレット変換 その2

2015.6.10 このプログラムですが逆変換にバグがあります。正しく動作しません。

Gewinn2のOANDAデモ口座での運用を、みんなのMT4で公開始めました。ブログパーツにはっ付けてあります。
FXCMジャパンでのリアル運用も近いうちに公開予定です。

さて、昨日に引き続き、ウェーブレット変換です。
MT4インジケータ ウェーブレット変換を実装する。その1

引き続き日銀のレポートを参考に実装していきます。
http://www.imes.boj.or.jp/research/papers/japanese/kk23-1-1.pdf

離散ウェーブレット変換ですが、可逆性を保持しています。つまり変換結果から元データが再現可能です。
この特性を生かしたのがウェーブレット逆変換です。

ウェーブレット変換は、ローパスフィルタとハイパスフィルタで信号成分を分離する仕組みです。ハイパスで分離された成分を除去した上で、逆変換を行うと高周波成分が失われた値となります。これを利用してある特定の閾値を持って高周波成分を除去するとノイズフィルタが実装可能です。

ハイパス成分の標本標準偏差未満の値は除去する形で実装したのが、このインジケータとなります。
waveletSNFilter.PNG

EURUSD1時間足のチャートに7EMAとウェーブレットノイズフィルタの結果を表示した結果です。
クローズ値に対する値で、黄色が7EMA、赤がウェーブレットになっています。

水色の枠の中を見ていると、7EMAが暴れているのに対してウェーブレットは暴れていません。その上で全体的に7EMAよりも強く追従していることが判ります。

ウェーブレット変換によるノイズ除去効果が表れています。ちなみに極端に動作したときに逆にウェーブレットの方が暴れている箇所があります。これはウェーブレットフィルタ特性となります。ウェーブレットフィルタを変更することで軽減できます。

ウェーブレット変換の基本的な活用方法を示しましたが、変換結果を持って、類似波形を検索したり、エネルギーを算出して大きな影響を与えている波長を検出したりと活用方法がたくさんあるようです。色々研究してEAに活用したいと思います。

あ、ちなみにこのプログラム結構重たいので、チャートの要素数をそこそこ減らして実行してください。
//------------------------------------------------------------------
// ウェーブレット逆変換ノイズフィルタ

#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

#property indicator_chart_window

//バッファーを指定する。
#property indicator_buffers 5

//プロット数を指定する。
#property indicator_plots   1

#property indicator_label1  "reverseWavelet"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_type2   DRAW_NONE
#property indicator_type3   DRAW_NONE
#property indicator_type4   DRAW_NONE
#property indicator_type5   DRAW_NONE

//入力パラメータ ノイズ除去レベル
input double Noise = 1;

//バッファ
double waveletBuffer[];

//ディテールバッファ
double detail1[];
double detail2[];
double detail3[];
double detail4[];

// 解析レベル
const int level = 2;

//必要データ数
int dataCount = 0;

//------------------------------------------------------------------
//初期化
int OnInit()
{
dataCount = (int)MathPow(2, level);

SetIndexBuffer(0, waveletBuffer);
SetIndexBuffer(1, detail1);
SetIndexBuffer(2, detail2);
SetIndexBuffer(3, detail3);
SetIndexBuffer(4, detail4);

SetIndexDrawBegin(0, dataCount);

string short_name;
short_name = "WV("+ IntegerToString(level)  +")";
IndicatorShortName(short_name);

SetIndexDrawBegin(0, dataCount);

return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
//計算イベント
int OnCalculate(
const int rates_total,          //各レート要素数
const int prev_calculated,      //計算済み要素数
const datetime &time[],         //要素ごとの時間配列
const double &open[],           //オープン価格配列
const double &high[],           //高値配列
const double &low[],            //安値配列
const double &close[],          //クローズ価格配列
const long &tick_volume[],      //ティック数(要素の更新回数)
const long &volume[],           //実ボリューム(?)
const int &spread[])            //スプレット
{
// フィルタ
double wavebletFilter[];
double scalingFilter[];

ArrayResize(wavebletFilter, 2);
ArrayResize(scalingFilter, 2);

// ハールフィルタ
wavebletFilter[0] =-0.70710678;
wavebletFilter[1] =0.70710678;
scalingFilter[0] = 0.70710678;
scalingFilter[1] = 0.70710678;

//元となる値を計算する。
for( int i = (rates_total - prev_calculated - 1); i>=0 ; i-- )
{
if( i >= (rates_total - 256 - 1) )
{
continue;
}

// 最終的には一個の値に復元される。
double scalingValues[];
ArrayResize(scalingValues, 1);

CalulateWavelet(close, wavebletFilter, scalingFilter, dataCount , level, i, i, scalingValues);
waveletBuffer[i] = scalingValues[0];
}

return(rates_total - 1);
}

//------------------------------------------------------------------
//ウェーブレット値を計算する。
void CalulateWavelet(
const double &values[],          // 元値
const double &wavebletFilter[],  // ウェーブレットフィルタ
const double &scalingFilter[],   // スケーリングフィルタ
int count,                       // ウェーブレット計算サイズ
int targetLevel,                 // ターゲットレベル
int shift,                       // 起点インデックス
int bufferIndex,                 // 現在算出中のバッファインデックス
double &scalingValues[],         // 復元スケール値
int currentLevel = 1             // 現在のレベル
)
{
// count/2 の配列を用意する。
int total = count / 2;

double wavelet[];
double scaling[];
ArrayResize(wavelet, total);
ArrayResize(scaling, total);

int size = ArraySize(wavebletFilter);

for( int i = 0 ; i < total; i++)
{
wavelet[i] = 0;
scaling[i] = 0;
for( int k = 0 ; k < size; k++ )
{
wavelet[i] += wavebletFilter[k] * values[ i * 2 + k + shift];
scaling[i] += scalingFilter[k] * values[ i * 2 + k + shift];
}
}

// グローバルバッファに確保
switch( currentLevel )
{
case 1:
detail1[bufferIndex] = wavelet[0];
break;
case 2:
detail2[bufferIndex] = wavelet[0];
break;
case 3:
detail3[bufferIndex] = wavelet[0];
break;
case 4:
detail4[bufferIndex] = wavelet[0];
break;
}

double reverseScaling[];

if( currentLevel < targetLevel )
{
ArrayResize(reverseScaling, total / 2);
CalulateWavelet(scaling, wavebletFilter, scalingFilter, total, targetLevel, 0, bufferIndex, reverseScaling, ++currentLevel);
}
else
{
ArrayResize(reverseScaling, total);
//スケーリング値はそのまま使用する。
for( int i = 0 ; i < total; i++ )
{
reverseScaling[i] = scaling[i];
}
}

// 標準偏差以下の変動は切り捨てる。
double std = 0;

// 標準偏差用
switch( currentLevel )
{
case 1:
std = StdDev( detail1, 100, bufferIndex);
break;
case 2:
std = StdDev( detail2, 100, bufferIndex);
break;
case 3:
std = StdDev( detail3, 100, bufferIndex);
break;
case 4:
std = StdDev( detail4, 100, bufferIndex);
break;
}
std = std * Noise;

// 逆ウェーブレット変換を行う。
int scalingSize = ArraySize(scalingValues);
int reverseScalingSize = ArraySize(reverseScaling);
for( int i = 0 ; i < scalingSize; i++)
{
scalingValues[i] = 0;
for( int k = 0 ; k < size; k++ )
{
// アップサンプリングの為、偶数インデックスは飛ばす
int index = i + k;
if( index % 2 ==1 ) continue;
index = index / 2;

if( index >= reverseScalingSize ) continue;

scalingValues[i] += scalingFilter[k] * reverseScaling[index];

if( MathAbs( wavelet[index] ) > std )
{
//標準偏差以上のwavelet値だけ反映する。
scalingValues[i] += wavebletFilter[k] * wavelet[index];
}
}
}

}

//------------------------------------------------------------------
//標準偏差を求める。
// return 標準偏差
double StdDev(
const double &values[],      //元となる配列
int count,                   //計算対象数
int shift                    //シフト
)
{
if( (count + shift) > ArraySize(values) ) return 0;

double avg = 0 ;
double mu = 0 ;
for( int i = shift; i < shift + count; i++ )
{
avg += values[i];
}
avg = avg / count;

for( int i = shift; i < shift + count; i++ )
{
mu += MathPow(values[i] - avg, 2);
}

// 標本標準偏差
return MathSqrt(mu / (count - 1));
}


標準偏差は自力で求めています。iStdDevOnArrayは移動平均をとった上で標準偏差を求めるため、単純な移動平均をとるために自力で実装しました。
あと、速度向上の為、グローバルバッファに値を突っ込むなど、少々コードが汚いです・・・。

2015.6.8 プログラムを少し修正しました。ウェーブレット逆変換がうまくいっていない予感がしたので・・・。

2015年6月2日火曜日

MT4インジケータ ウェーブレット変換を実装する。その1

2015.6.9 このプログラム、逆変換に失敗しています。そのままでは使えませんのでご注意ください。

うーん。なかなかGewinn2君が動作してくれるような値動きになってくれません。
5/30夜のロンドンフィキシング後の動きでポジションをとるシグナルが発生していたのですが、週末フィルタでポジションを取りませんでした。

Gewinn2は1年ほど放置するつもりで設計しているため、気長に運用します。
今週から実運用を準備していますので、そちらも順次報告していきたいと思います。

さて、相場はノイズとトレンドを伴った値動きをすると仮定します。
ノイズ成分とトレンド成分はどうやって分離するのが良いのでしょうか?S/Nフィルタを実装するというインジケータも存在しますが、あれは正しい信号に対してのノイズ成分を抽出する為、正しい信号が決まらない為替相場には考え方が難しくなります。

フーリエ変換で周波数成分を分析して振幅が大きい成分だけで再構築する手法もよくとられているようですが時間成分が抜け落ちるという問題があります。ウィンドウフーリエ変換を行って詳細に解析していくことも可能ですがマシンパワーが必要になります。たぶん超大手のシステムトレード屋さんではミニスパコンクラスを使って行っている事でしょう。

もうちょっと簡単にPCのパワーでも解析可能な手法はないかということで、ウェーブレット変換を実装していきたいと思います。ウェーブレット変換は画像処理や信号解析で使われる手法ですが、これを金融に応用できないかを検討したレポートが日本銀行 金融研究所から公開されています。
http://www.imes.boj.or.jp/research/papers/japanese/kk23-1-1.pdf

このレポートを参考にプログラムを実装していきます。
このレポート、離散ウェーブレット入門から記載されているうえ、ウェーブレット変換した結果から原油やドル円に影響を与えている周波数期間を検討したりと実例に富んでいます。信号解析技術を使ったインジケータやEAを開発しようと目標にする方は一読することをお勧めします。

まず、ウェーブレット変換の基礎となる変換部分の関数実装とその内容をインジケータに表示しました。
ウェーブレットフィルタはハールフィルタを使用しています。
結果画像は次の通りです。

レベル1詳細成分
webletDetail.JPG

レベル1スケール成分
webletScale.JPG

//------------------------------------------------------------------
// ウェーブレットインジケータ(テスト用)

#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict

#property indicator_separate_window

//バッファーを指定する。
#property indicator_buffers 1

//プロット数を指定する。
#property indicator_plots   1

#property indicator_label1  "reverseWavelet"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrYellow
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//入力パラメータ 表示レベル
input int Level = 1;

//入力パラメータ 0ウェブレットディテール or 1スケール
input int DetailOrScale = 0;

//バッファ
double waveletBuffer[];

//必要データ数
int dataCount = 0;

//------------------------------------------------------------------
//初期化
int OnInit()
{
dataCount = (int)MathPow(2, Level);
SetIndexBuffer(0, waveletBuffer);
SetIndexDrawBegin(0, dataCount);

string short_name;
short_name = "WV("+ IntegerToString(Level)  +")";
IndicatorShortName(short_name);

SetIndexDrawBegin(0, dataCount);

return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
//計算イベント
int OnCalculate(
const int rates_total,          //各レート要素数
const int prev_calculated,      //計算済み要素数
const datetime &time[],         //要素ごとの時間配列
const double &open[],           //オープン価格配列
const double &high[],           //高値配列
const double &low[],            //安値配列
const double &close[],          //クローズ価格配列
const long &tick_volume[],      //ティック数(要素の更新回数)
const long &volume[],           //実ボリューム(?)
const int &spread[])            //スプレット
{
// フィルタ
double wavebletFilter[];
double scalingFilter[];

int filterSize = 2;

ArrayResize(wavebletFilter, filterSize);
ArrayResize(scalingFilter, filterSize);

// ハールフィルタ
wavebletFilter[0] = 0.70710678;
wavebletFilter[1] = -0.70710678;
scalingFilter[0] = 0.70710678;
scalingFilter[1] = 0.70710678;

//元となる値を計算する。
for( int i = (rates_total - prev_calculated - 1); i>=0 ; i-- )
{
if( i >= (rates_total - dataCount - filterSize -1) )
{
continue;
}

WaveletValues value = CalulateWavelet(close, wavebletFilter, scalingFilter, dataCount , Level, i);
waveletBuffer[i] = DetailOrScale == 0 ? value.wavelet : value.scaling;
}

return(rates_total - 1);
}

// ウェーブレット値構造体
struct WaveletValues
{
// ウェーブレット
double wavelet;
double scaling;
};

//------------------------------------------------------------------
//ウェーブレット値を計算する。
WaveletValues CalulateWavelet(
const double &values[],          // 元値
const double &wavebletFilter[],  // ウェーブレットフィルタ
const double &scalingFilter[],   // スケーリングフィルタ
int count,                       // ウェーブレット計算サイズ
int targetLevel,                 // ターゲットレベル
int shift                        // 起点インデックス
)
{
// count/2 の配列を用意する。
int total = count / 2;

double wavelet[];
double scaling[];
ArrayResize(wavelet, total);
ArrayResize(scaling, total);

int size = ArraySize(wavebletFilter);

for( int i = 0 ; i < total; i++)
{
wavelet[i] = 0;
scaling[i] = 0;
for( int k = 0 ; k < size; k++ )
{
wavelet[i] += wavebletFilter[k] * values[ i * 2 + k + shift];
scaling[i] += scalingFilter[k] * values[ i * 2 + k + shift];
}
}

targetLevel --;

if( targetLevel <= 0 )
{
WaveletValues result ;
result.wavelet = wavelet[0];
result.scaling = scaling[0];
return result ;
}
else
{
return CalulateWavelet(scaling, wavebletFilter, scalingFilter, total, targetLevel, 0);
}
}


実装は離散ウェーブレット変換となっています。
プログラムをよーく眺めると、ハールフィルタの場合、移動平均と差分をとっているだけです。

ウェーブレット変換はハイパスフィルタとローパスフィルタを元信号に対して適用して、これをダウンサンプリングしながら再帰的に行う事で周波数成分を抽出していく手法のようです。

現在進行形のデータに対してウェーブレット変換を行う為、対象となっているデータがウェーブレット変換値に影響を与える範囲だけ毎回計算しています。
そのため、本来ダウンサンプリングされて2^levelごとにしか値が生成されていないはずですが、毎足に値が生成されるようになっています。

次回の記事では、これがなんの役に立つの?ということを検証していきたいとおもいます。

※記事を書いている段階では、これ何の役にたつのか書いている本人も判っていません。やってみよう!ってかんじで作っています(^^;



2015年6月1日月曜日

Gewinn2 EURUSDデイトレード型EA 解説1 基本動作と5/10年間バックテスト

EURUSD デイトレード型EA Gewinn2
http://fx-on.com/systemtrade/detail/?id=7600

現在FX-ONでフォワードテスト中のGewinn2の解説をアップしていきたいと思います。

■最初に
 定価ですが、FX-ONのフォワードテストを通過後の販売開始時点で、初期購入者の方々に向けて大幅な割引を予定しています。購入可能になっても割引が開始されるまでお待ちください。

■Gewinn2動作紹介
EA動作の特徴としては、週に2~4回取引するEAです。デイトレードを基本として、最大24時間ポジションを保持します。
15分足を見て過去の価格帯から動いた方向に5分足で判断して順張りでポジションを取りに行きます。
時間足をみて強力なトレンドが出ているときは、その逆にはポジションを持たないようなフィルタをかけています。また各種、時間フィルタを内蔵しています。

思った場合と逆方向に戻ってきてしまった場合は、すぐに損切します。
S/Rオシレータを使って天井圏を判断し手じまいをします。ただし時間足で強いトレンドを確認した場合は、利益を伸ばすことにチャレンジします。

気配値を基準としたトレイリングは行っていません。目標とする価格帯へ移行するまではある程度の戻りを許容する設計となっています。
ただし、戻りが想定より大きくなった場合、シグナルで手じまいを行うようになっておりトレイリングの代わりとしています。

EAの思想として、とにかくマイナス利益のポジションを長時間保持しない事に注力しています。また勝てる時に勝つという考え方で勝率は30%を割っていますが、利益を大きくすることで対応しています。ちょこちょこ負けますが大きくとるという形になります。

■5年/10年 バックテスト比較
5年間/10年間との全ティックでのバックテスト結果です。FXDD社のデータを使用しています。

取引は5分毎の確定値で行われていますので、全ティックでも確定値でもそんなに変わりません。ただし、かなり浅めのロスカット値を設定しているため全ティックの方がより正確な値となっています。

5年と10年を比較すると、直近5年に対して10年に拡大しても取引回数が1.3倍ほどしか増えていません。これはEURUSDが、10年前と比較して、短期での値動きが大きくなっていると想定しています。想像ですが、プログラム取引が活発になって動き出すと順張り方向へポジションを持つ人が増えているのではないか?と仮定しています。
10年前~5年前の期間でも+を保っていますが、より直近5年の利益が大きいので最近の動きに適合したEAだと考えています。

■5年間バックテスト
通貨ペア EURUSD (EUR/USD)
期間 5分足(M5) 2010.01.04 00:00 - 2015.04.29 23:55 (2010.01.01 - 2015.04.30)
モデル 全ティック (利用可能な最小時間枠による最も正確な方法)
パラメーター MagicNumber=55351260; TestGmt=2; IsCountdong=false; SpreadFilter=2; Lot=0.1; MaxPosition=2; IsPermitBoth=true; PermitSlip=2; Limit=30; Profit=300; IsWeekEndFilter=true; IsChangeDayNonOpenFilter=true; IsChangeDayCloseFilter=false; IsEmploymentStatisticsFilter=true; MaxPositionHour=24; IsCompund=false; CompundLotUnit=0.01; CompundMaxLot=0.5; Comment="Gewinn2";

テストバー数 393914 モデルティック数 82261840 モデリング品質 90.00%
不整合チャートエラー 0

初期証拠金 10000.00 スプレッド 15
総損益 6927.20 総利益 22369.10 総損失 -15441.90
プロフィットファクター 1.45 期待利得 5.54
絶対ドローダウン 2.50 最大ドローダウン 455.60 (2.75%) 相対ドローダウン 4.00% (426.50)

総取引数 1250 ショートポジション(勝率%) 782 (29.41%) ロングポジション(勝率%) 468 (33.76%)
勝率(%) 388 (31.04%) 負率 (%) 862 (68.96%)
最大 勝トレード 300.00 負トレード -30.00
平均 勝トレード 57.65 負トレード -17.91
最大 連勝(金額) 5 (204.50) 連敗(金額) 11 (-79.30)
最大化 連勝(トレード数) 394.90 (2) 連敗(トレード数) -272.50 (10)
平均 連勝 1 連敗 3
2010全ティック.gif


■10年間バックテスト
※初期証拠金が5年の1/4になっていますので、ドローダウンの%は1/4にして判断してください。

通貨ペア EURUSD (Euro vs US Dollar)
期間 5分足(M5) 2005.01.10 10:50 - 2015.04.29 23:55 (2005.01.01 - 2015.04.30)

テストバー数 759643 モデルティック数 128130588 モデリング品質 89.99%
不整合チャートエラー 0

初期証拠金 2500.00 スプレッド 15
総損益 5536.00 総利益 29497.50 総損失 -23961.50
プロフィットファクター 1.23 期待利得 3.10
絶対ドローダウン 1089.00 最大ドローダウン 1228.10 (46.53%) 相対ドローダウン 46.53% (1228.10)

総取引数 1788 ショートポジション(勝率%) 1035 (28.41%) ロングポジション(勝率%) 753 (28.15%)
勝率(%) 506 (28.30%) 負率 (%) 1282 (71.70%)
最大 勝トレード 300.00 負トレード -30.00
平均 勝トレード 58.30 負トレード -18.69
最大 連勝(金額) 5 (112.20) 連敗(金額) 18 (-486.50)
最大化 連勝(トレード数) 514.20 (2) 連敗(トレード数) -486.50 (18)
平均 連勝 1 連敗 3
2005全ティック.gif

■今後について
会社の支援を受けて開発していますので、今後も継続的にアップデートをしていきます。
現在、取引回数を増やすための調査を行っています。またロスカットの値を固定ではなくてATRを参考に変動させた場合の期待利益の変化も調査しています。
継続的にアップデートしていき利益が伸ばせるよう頑張りますので皆様ぜひご愛用ください。

次回はプログラム的な特徴を説明したいと思います。