[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
前回までの改造でOmegaChartで10年分のデータをダウンロードできるようになったのですが
2つほど気に入らない部分があったのでさらに改造しました。
データファイルが存在しない時に10年分の株価データをダウンロードしようとすると
「配列のオフセットおよび長さが範囲を超えているか、
カウンターがソース コレクションのインデックスから最後までの要素の数より大きい値です。」
というエラーが発生します。
これは配列を200日分しか確保していないのに
10年分のデータを追加しようとしたからだと思われます。
UpdateDataFarm()の初期化部を以下に変更しました。20年分くらいは格納できると思います。
_farm = new byte[RECORD_LENGTH * 5000]; //この上限はどこかで取得すべきだが
もうひとつが全銘柄の株価データをダウンロードした後で
全銘柄のファイルを更新するようになっているのを改造して
個々の銘柄をダウンロードするたびに更新するようにしました。
私の環境では10年分のデータをダウンロードするのに1銘柄に3分以上かかることもあります。
100銘柄だと何時間もかかりそうです。
100銘柄の株価データをダウンロードした後でdataフォルダの個々のファイルを更新するように
なっていると途中で何か起きた時にそれまでダウンロードしたデータが使用されず
時間が無駄になってしまいます。
個々の銘柄の株価データをダウンロードする度にdataフォルダのファイルを更新すれば
もし途中でエラーが発生したりしてもそれまでにダウンロードしたデータは適用済みとなります。
単純に処理を移動すれば実現できました。
以下の処理がdataファイルを更新する処理です。
DownloadOldDate(br, startDate, endDate)もしくは DownloadCurrentDate(br) の
直後に実行するようにコピペするだけでした。
                Hashtable tr = (Hashtable)newdata[br.Code];
                if (tr == null)
                {
                    continue;
                }
                ArrayList dates = new ArrayList(tr.Keys);
                dates.Sort();
                bool trace_flag = false;
                using (DailyDataFarm f = (DailyDataFarm)br.CreateDailyFarm(dates.Count))
                {
                    foreach (int day in dates)
                    {
                        NewDailyData td = (NewDailyData)tr[day];
                        if (td == null)
                        {
                            if (!trace_flag)
                            {
                                trace_flag = true;
                                Debug.WriteLine("Data not found(yahoo) : code=" + br.Code + " market=" + br.Market.ToString());
                            }
                        }
                        else
                            f.UpdateDataFarm(day, td);
                    }
                    f.Save(Util.GetDailyDataFileName(br.Code));
                }
こうすることで途中でキャンセルしても
ダウンロードの終わった銘柄はindex.txtから削除して
次の日に続きの銘柄からダウンロードするということが可能となります。
ダウンロードの終わった銘柄をindex.txtから勝手に削除する処理を追加すれば
楽かもしれませんが1度全銘柄の10年分のデータが揃ってしまえば
index.txtを元に戻して月に1度くらい最新データをダウンロードをするだけで維持できるので
データが揃うまではちまちまと手作業でindex.txtから削除します。
以上の処理は10年分の株価データをOmegaChartでダウンロードするための改造で
他の方法で過去の株価データを用意すればいいだけなので真似しなくていいです。
これまでの改造は
OmegaChartのコードを利用して長期の株価データをダウンロードするだけの専用ツールを作成できないか
を考えた結果です。index.txtを手作業で変更する必要はありますが自分用としては使えそうです。
ただのプログラミングの勉強です。
Yahooからデータのダウンロードをするように改造しました。
その1、その2の改造と合わせることで
OmegaChartで10年分の過去データをダウンロードするのが目的です。
OmegaYahoo.zip、OmegaYahoo.lzh、UpdateButton2.lzh
というものにソースが含まれているらしいのですが入手できませんでした。
ですがOmegaChartの改造ソースコードをネットで公開されているかたのソースを見ると
Yahooからダウンロードするようになっていたのでその部分だけを移植しました。
その他いろいろな機能も改造されているのですがそのまま全機能をパクルと
勉強にならないので今回はその部分だけパクリました。
2か所変更しただけです。
DownloadOrder.cs の以下の部分を
Specialized.MujinzouDataSource m = new Specialized.MujinzouDataSource(_dateArray); 
m.IncludesDomesticIndices = true; //最近の銘柄で国内指数はカバーする
以下に置き換えて
Specialized.YahooDataSource m = new Specialized.YahooDataSource(_dateArray);
 
本当は別のファイルとするのがいいと思いますが
Mujinzou.cs の
internal class MujinzouDataSource : DailyDataSource {
・・・
}
の下に以下のYahoo用のクラスを並べて追加しました。
 
    internal class YahooDataSource : DailyDataSource
    {
        //上場廃止銘柄のダウンロードを行わない
        public bool IsIgnoreObsolete = true;
        public YahooDataSource(int[] dates)
            : base(dates)
        {
        }
        public override void Run()
        {
            //ダウンロード開始日と終了日
            int startDate = GetStartDate(this._dates);
            int endDate = GetEndDate(this._dates);
Hashtable newdata = new Hashtable();
            //データをダウンロード
            IDictionaryEnumerator ie = Env.BrandCollection.GetEnumerator();
            while (ie.MoveNext())
            {
                AbstractBrand br = (AbstractBrand)ie.Value;
                if (br.Market == MarketType.Custom) continue;
                if (this.IsIgnoreObsolete)
                {//上場廃止は無視する設定の場合
                    if (br is BasicBrand && ((BasicBrand)br).Obsolete == true)
                    {
                        continue;
                    }
                }
                if (endDate - startDate > 20)
                {//あんまり間が空くとこっちで縦断爆撃しないといけない...
                    newdata[br.Code] = DownloadOldDate(br, startDate, endDate);
                }
                else
                {//そんなに間が空いていないなら、Yahoo!に負荷がかからないと思われる方法で
                    newdata[br.Code] = DownloadCurrentDate(br);
                }
                SendMessage(AsyncConst.WM_ASYNCPROCESS, (startDate & DataSourceBase.DATE_MASK), AsyncConst.LPARAM_PROGRESS_SUCCESSFUL);
            }
//データをインポート
            ie = Env.BrandCollection.GetEnumerator();
            while (ie.MoveNext())
            {
                AbstractBrand br = (AbstractBrand)ie.Value;
                if (br.Market == MarketType.Custom) continue;
                Hashtable tr = (Hashtable)newdata[br.Code];
                if (tr == null)
                {
                    continue;
                }
                ArrayList dates = new ArrayList(tr.Keys);
                dates.Sort();
                bool trace_flag = false;
                using (DailyDataFarm f = (DailyDataFarm)br.CreateDailyFarm(dates.Count))
                {
                    foreach (int day in dates)
                    {
                        NewDailyData td = (NewDailyData)tr[day];
                        if (td == null)
                        {
                            if (!trace_flag)
                            {
                                trace_flag = true;
                                Debug.WriteLine("Data not found(yahoo) : code=" + br.Code + " market=" + br.Market.ToString());
                            }
                        }
                        else
                            f.UpdateDataFarm(day, td);
                    }
                    f.Save(Util.GetDailyDataFileName(br.Code));
                }
                SendMessage(AsyncConst.WM_ASYNCPROCESS, br.Code, AsyncConst.LPARAM_PROGRESS_SUCCESSFUL);
            }
}
        //開始日検索
        int GetStartDate(int[] inDateArray)
        {
            Debug.Assert(inDateArray.Length >= 1);
int r = inDateArray[0];
            for (int i = 1; i < inDateArray.Length; i++)
            {
                if (r > inDateArray[i])
                {
                    r = inDateArray[i];
                }
            }
            return r;
        }
        //終了日検索
        int GetEndDate(int[] inDateArray)
        {
            Debug.Assert(inDateArray.Length >= 1);
int r = inDateArray[0];
            for (int i = 1; i < inDateArray.Length; i++)
            {
                if (r < inDateArray[i])
                {
                    r = inDateArray[i];
                }
            }
            return r;
        }
        //コードをyahoo形式で取得する  5401 -> 5401.t
        //未対応の場合 空白になる
        string GetYahooCode(int inCode, MarketType inType)
        {
            string codeString = inCode + ".";
            switch (inType)
            {
                case MarketType.T1:
                case MarketType.T2:
                    codeString += "t";
                    break;
                case MarketType.O1:
                case MarketType.O2:
                    codeString += "o";
                    break;
                case MarketType.J:
                    codeString += "q";
                    break;
                case MarketType.M:
                    codeString += "t";
                    break;
                case MarketType.H:
                    codeString += "j";
                    break;
                case MarketType.B:
                    switch (inCode)
                    {
                        case (int)BuiltInIndex.Nikkei225:   //日経平均
                            codeString = "998407.O";
                            break;
                        case (int)BuiltInIndex.TOPIX:   //TOPIX
                            codeString = "998405.T";
                            break;
                        case (int)BuiltInIndex.JASDAQ:   //JASDAQ
                            codeString = "23337.Q";
                            break;
                        case (int)BuiltInIndex.Nikkei225_F:   //日経平均先物
                            codeString = "5040469.O";
                            break;
                        default:
                            //未対応
                            codeString = "";
                            break;
                    }
                    break;
                default:
                    //未対応
                    codeString = "";
                    break;
            }
            return codeString;
        }
        //古いデータをダウンロード
        //Hashtable[日時] = NewDailyData 形式
        Hashtable DownloadOldDate(AbstractBrand inBR, int inStartDate, int inEndDate)
        {
            DateTime d2 = Util.IntToDate(inStartDate);
            DateTime e2 = Util.IntToDate(inEndDate);
            string codeString = GetYahooCode(inBR.Code, inBR.Market);
            int count = 0;
            Hashtable ret = new Hashtable();
            if (codeString == "")
            {
                return ret; //未対応
            }
            while (true)
            {
                string url = String.Format("http://table.yahoo.co.jp/t?c={0}&a={1}&b={2}&f={3}&d={4}&e={5}&g=d&s={6}&y={7}&z={6}", d2.Year, d2.Month, d2.Day, e2.Year, e2.Month, e2.Day, codeString, count);
                string html = Download(url);
                //ダウンロードした htmlの解析
                if (!ParseHTML(ret, inBR.Code, html))
                {
                    break;
                }
                //次のデータがある?
                if (html.IndexOf("次の") < 0)
                {
                    break;  //ないなら終わる
                }
                count += 50;
            }
            return ret;
        }
        //数日の新しいデータをダウンロード
        //Hashtable[日時] = NewDailyData 形式
        Hashtable DownloadCurrentDate(AbstractBrand inBR)
        {
            string codeString = GetYahooCode(inBR.Code, inBR.Market);
            Hashtable ret = new Hashtable();
            if (codeString == "")
            {
                return ret; //未対応
            }
            string url = String.Format("http://table.yahoo.co.jp/t?s={0}&g=d", codeString);
            string html = Download(url);
            //ダウンロードした htmlの解析
            ParseHTML(ret, inBR.Code, html);
            return ret;
        }
        string Download(string inUrl)
        {
            try
            {
                MemoryStream ms = Util.HttpDownload(inUrl);
                ms.Close();
                return Encoding.GetEncoding("euc-jp").GetString(ms.ToArray());
            }
            catch (Exception e)
            {
                throw new Exception("URL(" + inUrl + ")をダウンロード中にエラーが発生しました。\r\n例外メッセージ:" + e.Message, e);
            }
        }
        bool ParseHTML(Hashtable ioReslut, int inCode, string inHTML)
        {
            int kaishine = inHTML.IndexOf("始値");
            if (kaishine < 0)
            {
                return false;
            }
            int tableStart = inHTML.LastIndexOf("<table ", kaishine);
            if (tableStart < 0)
            {
                return false;
            }
            double vv = 1, pv = 1;
            //倍率調整
            if (IsDomesticIndex(inCode))
            {
                vv = 0.001; //DreamVisorのものにあわせる格好で。1000株単位かな
                pv = 100;
            }
            if (inCode == (int)BuiltInIndex.TOPIX_F)
            { //TOPIX先物は整数単位で記録されている
                pv = 10;
            }
            //テーブルの取り込み
            ArrayList y = TableToCsv(inHTML.Substring(tableStart));
            for (int yCount = 1; yCount < y.Count; yCount++)
            {
                NewDailyData td = new NewDailyData();
                ArrayList x = (ArrayList)y[yCount];
                if (((string)x[1]).IndexOf("分割") >= 0)
                {
                    //分割?
                    continue;
                }
                int date = StringDateToInt((string)x[0]);
                td.open = (int)(double.Parse((string)x[1]) * pv);
                td.high = (int)(double.Parse((string)x[2]) * pv);
                td.low = (int)(double.Parse((string)x[3]) * pv);
                td.close = (int)(double.Parse((string)x[4]) * pv);
                if (x.Count > 5)
                {
                    td.volume = (int)(double.Parse((string)x[5]) * vv);
                }
                else
                {
                    td.volume = 0;
                }
                ioReslut[date] = td;
            }
            return true;
        }
        private static bool IsDomesticIndex(int code)
        {
            return code == (int)BuiltInIndex.Nikkei225 || code == (int)BuiltInIndex.TOPIX || code == (int)BuiltInIndex.JASDAQ;
        }
        //table タグを csv に変換
        //複雑なテーブルには未対応です。
        //ArrayList[y] = {ArrayList[x]} 形式です.
        ArrayList TableToCsv(string inHTML)
        {
            ArrayList y = new ArrayList();
            //枝刈り
            inHTML = inHTML.Replace("<TABLE", "<table");
            inHTML = inHTML.Replace("</TABLE", "</table");
            inHTML = inHTML.Replace("<TR", "<tr");
            inHTML = inHTML.Replace("</TR", "</tr");
            inHTML = inHTML.Replace("<TD", "<td");
            inHTML = inHTML.Replace("</TD", "</td");
            inHTML = inHTML.Replace("<TH", "<td");   //TD扱いにする
            inHTML = inHTML.Replace("</TH", "</td"); //TD扱いにする
            inHTML = inHTML.Replace("<th", "<td");   //TD扱いにする
            inHTML = inHTML.Replace("</th", "</td"); //TD扱いにする
            int tableStart = inHTML.IndexOf("<table");
            if (tableStart < 0)
            {
                return y;
            }
            int tableEnd = inHTML.IndexOf("</table>", tableStart);
            if (tableEnd < 0)
            {
                return y;
            }
            int trLoop = tableStart;
            while (true)
            {
                //TRタグ 行の取得
                int trStart = inHTML.IndexOf("<tr", trLoop, tableEnd - trLoop);
                if (trStart < 0)
                {
                    break;
                }
                int trEnd = inHTML.IndexOf("</tr>", trStart, tableEnd - trStart);
                if (trEnd < 0)
                {
                    break;
                }
                trLoop = trEnd + 1;
                //TDタグ 列の取得
                ArrayList x = new ArrayList();
                int tdLoop = trStart;
                while (true)
                {
                    int tdStart = inHTML.IndexOf("<td", tdLoop, trEnd - tdLoop);
                    if (tdStart < 0)
                    {
                        break;
                    }
                    int tdEnd = inHTML.IndexOf("</td>", tdStart, trEnd - tdStart);
                    if (tdEnd < 0)
                    {
                        break;
                    }
                    tdLoop = tdEnd + 1;
                    //中身を保存.
                    string node = inHTML.Substring(tdStart, tdEnd - tdStart);
                    x.Add(KillTag(node));
                }
                y.Add(x);
            }
            return y;
        }
        string KillTag(string inHTML)
        {
            string retString = "";
            int tagLoop = 0;
            while (true)
            {
                int tagStart = inHTML.IndexOf('<', tagLoop);
                if (tagStart < 0)
                {
                    break;
                }
                //タグが始まるまでのテキストの取り込み
                if (tagStart - tagLoop > 0)
                {
                    retString += inHTML.Substring(tagLoop, tagStart - tagLoop);
                }
                int tagEnd = inHTML.IndexOf('>', tagStart);
                if (tagEnd < 0)
                {
                    break;  //タグを閉じていない??
                }
                tagLoop = tagEnd + 1;
            }
            return retString;
        }
        static int StringDateToInt(string str)
        {
            DateTime d;
            if (!DateTime.TryParse(str, out d))
            {
                return 0;
            }
            return Util.DateToInt(d);
        }
}
 
 
 それだけの変更でコンパイルが成功しました。
index.txt の中を
9766,コナミ,T1,・・・・
の1行だけにしてダウンロードを実行してみました。
ダウンロード開始日を2001年1月29日にするとなんと10年分の時系列データがダウンロードできました。
まだindex.txtが約4335行の完全な状態では試していません。
4000銘柄の10年分のデータのダウンロードにどれくらい時間がかかるかわからないし
途中で止まるとdataフォルダの中は1銘柄も更新されない恐れがあります。
データのダウンロードの仕様として
全てのデータをダウンロードし終わってからdataフォルダのデータを更新するようです。
 
1度にダウンロードする銘柄数を500銘柄とかにして
その銘柄だけをindex.txtに書いてダウンロードしてというのを
9回繰り返せば10年分の全銘柄をダウンロードできるかな。
まだ1銘柄しか成功していないのでデバッグが必要な銘柄もあるかもしれません。
改造ソースコードを公開されている方ありがとうございました。
 
10年前に指定できるように改造しました。
デバッグしてデータを取得するアドレスは以下のフォーマットになっていることがわかりました。
例)
2010年11月29日 ttp://souba-data.com/data_day/2010d/10_11d/T101129.lzh
2011年1月28日 ttp://souba-data.com/data_day/2011d/11_01d/T110128.lzh
IEでアクセスしてみると現時点で2010年8月2日のファイルは存在しました。
それでOmegaChartで2010年8月2日以降のデータをダウンロードしようとしましたが
動きが怪しかったのでデバッグしてみました。
Data.csに以下のような部分がありました
offset = _byteLength - RECORD_LENGTH;
do {
int t = GetInt(offset);
if(t==date)
break;
else if(t < date) {
offset += RECORD_LENGTH;
break;
}
else
offset -= RECORD_LENGTH;
} while(true);
offsetがマイナスになってはいけないはずなので
アンダーフローしたときはbreakするようにしました。
if (offset < 0) {
offset = 0;
break;
}
そうすると偶然ダウンロード成功しました。
めでたし、めでたし。
Yahooからデータをダウンロードする改造版が出回っているので
Yahooからダウンロードするように挑戦してみます。
範囲を変更できないか解析してみました。
Download.cs:614: private static int[] ListDates() {
関数の中に
DateTime start = ld.AddMonths(-2);
という処理があってそれにより2か月前から指定できるようになっていました。
以下に変更して10年前から指定できるようにしてみました。
DateTime start = ld.AddMonths(-120);
初期では2か月前までしか指定できません。
変更することで10年前の2001年01月29日まで指定できるようになりました。
指定できてもサーバーにデータがなければあまり意味がありません。
スクリーニングでエラーになる件ですが
日付がMaxを越えているのが原因らしいので
AutoTradingDialog.cs:254:   this._startDatePicker.MaxDate = new System.DateTime(2010, 12, 31, 0, 0, 0, 0);
AutoTradingDialog.cs:272:   this._endDatePicker.MaxDate = new System.DateTime(2010, 12, 31, 0, 0, 0, 0);
ScreeningDialog.cs:271:   this._datePicker.MaxDate = new System.DateTime(2010, 12, 31, 0, 0, 0, 0);
以上の3か所をとりあえず2010から2100に変更してみました。
スクリーニングは成功しました。
 
今さらですが急にOmegaChartを使いたくなったのでダウンロードして実行してみたら
「スキーマのロード中にエラーが発生しました。」というエラーが発生して正常に動作しませんでした。
ほかにも
「内部エラーが発生しました。error.logファイルにエラー位置が記録されました。
保護されているメモリに読み取りまたは書き込み操作を行おうとしました。
他のメモリが壊れていることが考えられます。」
などのエラーが発生しました。
OmegaChartのバージョンは 1.8.5 でOSはWindows7 x64です。
ソースコードをコンパイルし直したら正常になるらしいのでコンパイルに挑戦しました。
開発環境
ゲイツは太っ腹だぜ!なんて感動しながら
Microsoft Visual C# Expressをインストールしました。
そのほかに準備したものは
ソースコード OmegaChart184_src.tar.gz
インストール済みの OmegaChart 1.8.5
くらいです。
index.txt は2011/01/04のを使用しています。
手準
1.ソースコードを適当な場所に解凍
2.zanetti.slnをダブルクリックしてVisual C# Express 2010を起動
3.Visual Studio 変換ウィザードが起動するので[次へ]、[次へ]、[完了]と適当に済ませる
4.ツールバーのDebugをReleaseに変更
5.Any CPUになっているので構成マネージャーからx86を新規作成してx86に変更
6.参照の追加でOmegaChart 1.8.5 の中の "grammatica-1.4.dll" "DotNetMagic2005.dll" を指定
7.ビルド
警告 CS0618: 'System.Net.WebProxy.GetDefaultProxy()' は古い形式です: 'This method has been deprecated. Please use the proxy selected for you by default. http://go.microsoft.com/fwlink/?linkid=14202'
は無視します。
bin\x86\Release\OmegaChart.exe というファイルができているので
置き換えて終了
実行してみるととりあえずは起動可能でした。エラーは表示されません。
データのダウンロードを実行すると2011/01/05のデータは取得できています。
どこかで改造しないとデータのダウンロードができないと見た気がするのですが
ダウンロードできちゃいました。
スクリーニングをしようとすると
「内部エラーが発生しました。error.logファイルにエラー位置が記録されました。
'2011/01/05 0:00:00' の値は 'Value' に対して有効ではありません。
'Value' は 'MinDate' と 'MaxDate' の間でなければなりません。パラメーター名: Value」
というエラーが発生して実行できませんでした。
⇒ forextester.o-oi.net/Entry/60/ こちらの変更をして再コンパイルで回避できました。
他にもいろいろ問題がありそうなので少しずつ改造していきたいです。
StdDev.mq4 みたいなのをForexTester2用に移植してみました。
こちらで販売中です。 
 
 
ForexTesterとMetaTraderで同じヒストリーデータを使って表示してみました。
数点の値を確認してみましたが一致していました。
このインディケータに似ているのが
BBandWidthRatio と Bollinger Bandwidth です。
StdDevを価格データで割っている感じです。
販売中のインジケーター一覧はこちらです。
| 10 | 2025/11 | 12 | 
| 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 | 
自分で売買ルールを作成してテストして自信をもってリアルトレードしたいです。


