「システムトレードの自動化」の記事を楽しみにされている皆さん、お久しぶりです。
気がつけば4月も終わりですね。
すでにゴールデンウィークの計画を立てられ、新入生や新入社員が少しずつ新しい環境に慣れ始めているころと思います。
ご存知のように、ここ3ヶ月ほど「システムトレードの自動化」を進めていませんでした。
え? 1日数十人しかこないようなクソブログ、誰も久しぶりじゃないし、どうでもいいって?
まあそれは否定しませんが、取り合えず久しぶりってことにしておいてください。
ここ最近は順調に「システムトレード」に関する記事のアクセス数が上がっています。
ようやく世間的にも「システムトレードと言えば『ねほり』」と認識されつつあるようで、シストレ戦略の代弁者になれる日が近づきつつあるようです。
目的なんだっけ……?
以前マルチストラテジーを作成し、日々のシグナルを確認していました。
完全自動化の前に、時間が許される時に売買してましたが、どういう訳か勝てません。
今回は閑話の会として、その辺りの考察とProtraの実装修正やパッチを作成します。
日銀のETF購入見送りが続いている
日経平均 日中は584円安
ギャアアアァァァーーーーーーーーーーーーーー!!!!!
定番のやりとりですが、最近の主な原因は
日銀のETF購入見送りが続いていること
らしく、次のグラフを見ると一目瞭然です。
日米で株価に乖離が出ても日銀のETF購入が行われず日経平均株価は上昇していません。
ただし、システムトレードでは、株が上がろうが下がろうが、動きが無かろうが「負けない」取引を過去数十年のバックテストで作り上げて実践しています。
このような株の動きに一喜一憂してはいけません。
マルチストラテジーの弊害
システムトレードでは、特徴が異なるアルゴリズムを組み合わせてリスク分散を図るのが定番です。
- 異なるコンセプト(順張り、逆張り、空売り)
- 市場分散(東証、マザーズ)
- ロングショートの組み合わせ
しかし、経験上 順張りで単調増加するストラテジーを作るのは容易ではありません。
以前「グランビルの法則」を使った「じんぱち氏」の順張りを作成しました。
そこから自分なりにストラテジーを改造し続け、次のようなバックテスト結果を得る事に成功。
もはや勝ったも同然!
と、この順張りを含んだ「マルチストラテジー」を2020年の秋から稼働させ、時々株を購入してました。
で、結果
毎月、負け続けた……
昨年の秋から運用を開始したこのストラテジーは、残念ながら市場でフルボッコされた挙句、既にご臨終されてます。
なんだコレ?ネタにしては洒落にならんぞ?
先週末、ようやく時間をとってマルチストラテジーを分解してバックテストを行うことが出来た。
昨年秋から曲がってるじゃねーか!!
なんで急に利益曲線が曲がるのよ?
日経平均株価は上がってるのに順張りで負ける要因って何よ?
もう、何が答えなのか分かりません。
Protraに対する独自修正部分の再整理
損失状況の確認が遅くなった理由があります。
今まで日記で紹介してきた実装以外にも、様々な改造を試み続けてました。
- Protraの月足表示
- 銘柄と同数のCSVの取り込み
- Ptsimの売買結果を勝ち負けで色変更
- Ptsimの売買結果をクリックで、Protraの銘柄該当箇所に飛ぶ
どれも失敗に終わったけどね。
さらに、その結果
C#知らないのに修正を入れすぎてGUIが壊れ、何も確認できなくなった。
更にはRevertも行わずに公式サイトによるアップデートも部分的に取り込んでいたので、もはや本線と差分が大きくて意味が分からぬ状況が続いてました。
今回は重い腰をあげ差分パッチを作ります。
因みに差分パッチに含む修正は次の通り。
次の修正は、折角投稿したので公式にマージして欲しい……。
自動起動&終了を動的に切り替える
パッチ作成の前にもう一つ改造を施してみます。
自動化処理のために
- Protra.cli.exe
- Ptsim.cli.exe
という実行ファイル名でソースコードを書き換えてはビルドを行っていました。
今回は「conf/protra.xml」「conf/ptsim.xml」を書き換える事で、ビルド無しで動的に自動実行実施を切り替える修正を行います。
具体的には次のようにXMLに書き込めば自動処理を行うアプリケーションになります(「true」で「自動実行&自動終了」適用。デフォルトfalse)。
1 2 3 4 5 6 7 |
<ChartConfig> <Height>231</Height> <DailyFile>charge3.pt</DailyFile> </ChartConfig> </ChartList> + <Autoclose>false</Autoclose> </ProtraConfig> |
修正内容を抜粋すると次のとおり。
ページ下のパッチの中に含まれます。
1 2 3 4 5 6 7 8 9 10 11 12 |
/// /// アプリを強制終了するかを取得または設定する。 /// public bool Autoclose { get; set; } ... var config = GlobalEnv.PtSimConfig if (config.Autoclose) { Application.Exit(); } |
独自拡張が増えてきたのでパッチ化&パッチ適用方法
Cygwin上でパッチ作って、適用しようとすると次のようなエラーが出て弾かれてました。
1 2 3 4 5 6 7 8 9 |
patching file ./Protra/Controls/ChartBox.cs Hunk #1 FAILED at 256 (different line endings). 1 out of 1 hunk FAILED -- saving rejects to file ./Protra/Controls/ChartBox.cs.rej patching file ./Protra/Dialogs/PriceUpdateDialog.cs Hunk #1 FAILED at 22 (different line endings). Hunk #2 FAILED at 94 (different line endings). Hunk #3 FAILED at 107 (different line endings). 3 out of 3 hunks FAILED -- saving rejects to file ./Protra/Dialogs/PriceUpdateDialog.cs.rej ... |
色々とググって試行錯誤した結果、確実にパッチ適用する方法は次のとおり。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ unix2dos.exe protra.patch $ patch -p0 --binary < hoge.patch patching file ./Protra/Controls/ChartBox.cs patching file ./Protra/Dialogs/PriceUpdateDialog.cs patching file ./Protra/MainForm.cs Hunk #4 succeeded at 408 with fuzz 2. patching file ./Protra/MainForm.designer.cs patching file ./Protra.Lib/Config/ProtraConfig.cs patching file ./Protra.Lib/Config/PtSimConfig.cs patching file ./Protra.Lib/Lang/Builtins/DrawBuiltins.cs patching file ./Protra.Lib/Lang/Builtins/SimulateBuiltins.cs patching file ./PtSim/MainForm.cs Hunk #8 succeeded at 414 with fuzz 2. patching file ./PtSim/MainForm.Designer.cs |
これで、実装が壊れても戻せる。
まとめ
ストラテジーを失い、お金も失い、時間も失い、何も進んでない。
この順張りアルゴリズムは、2012年頃の手法だよ?
これが実践で使い始めたと同時に負け始めるって……。
完全自動化に向けて、動かすストラテジーが一つ無くなった……。
差分コード
今回作った差分コード。
「Performance.cs」に関しては、修正が多く そのまま差し替えた方が良いのでパッチには入れてない。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
diff -dur ./Protra/Controls/ChartBox.cs ./Protra/Controls/ChartBox.cs --- ./Protra/Controls/ChartBox.cs 2014-02-10 15:10:07.000000000 +0900 +++ ./Protra/Controls/ChartBox.cs 2021-04-26 02:19:54.805716500 +0900 @@ -256,8 +256,8 @@ { // グローバル変数の設定 Interpreter.GlobalVariableTable.Clear(); - Interpreter.GlobalVariableTable["$Names"] = new Value(new Value[6]); - Interpreter.GlobalVariableTable["$Colors"] = new Value(new Value[6]); + Interpreter.GlobalVariableTable["$Names"] = new Value(new Value[7]); + Interpreter.GlobalVariableTable["$Colors"] = new Value(new Value[7]); Interpreter.GlobalVariableTable["$IsTimeSeries"] = new Value(true); // 組み込み関数の設定 diff -dur ./Protra/Dialogs/PriceUpdateDialog.cs ./Protra/Dialogs/PriceUpdateDialog.cs --- ./Protra/Dialogs/PriceUpdateDialog.cs 2016-09-06 21:21:04.000000000 +0900 +++ ./Protra/Dialogs/PriceUpdateDialog.cs 2021-04-26 19:37:20.793367000 +0900 @@ -22,6 +22,7 @@ using System.ComponentModel; using System.Windows.Forms; using Protra.Lib; +using Protra.Lib.Config; using Protra.Lib.Data; using Protra.Lib.Dialogs; using Protra.Lib.Update; @@ -94,6 +95,7 @@ private void backgroundWorkerUpdate_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { + var config = GlobalEnv.ProtraConfig; if (e.Error != null) { using (new CenteredDialogHelper()) @@ -107,6 +109,11 @@ else { labelInfo.Text = "更新が完了しました。"; + if (config.Autoclose) + { + this.Close(); + Application.Exit(); + } } Cursor = Cursors.Arrow; buttonCancel.Enabled = true; diff -dur ./Protra/MainForm.cs ./Protra/MainForm.cs --- ./Protra/MainForm.cs 2014-02-10 15:10:07.000000000 +0900 +++ ./Protra/MainForm.cs 2021-04-26 19:43:20.742177200 +0900 @@ -214,7 +214,7 @@ private void SetBrandInfo(Brand brand) { labelCode.Text = brand.Code + " " + brand.MarketName; - labelBrandName.Text = brand.Name; + labelBrandName.Text = brand.Name.Replace("&", "&&"); } /// @@ -251,7 +251,7 @@ var contextMenu = new ContextMenu(); foreach (var brand in brands) { - var menuItem = new MenuItem {Text = brand.ToString(), Tag = brand}; + var menuItem = new MenuItem {Text = brand.ToString().Replace("&", "&&"), Tag = brand}; menuItem.Click += menuItemSearchedBrand_Click; contextMenu.MenuItems.Add(menuItem); } @@ -391,7 +391,6 @@ bl.List.Add(code); GlobalEnv.BrandListConfig.Save(); } - private void listDelToolStripMenuItem_Click(object sender, EventArgs e) { var brandListIndex = comboBoxBrandList.SelectedIndex; @@ -409,5 +408,12 @@ listBoxBrandList.SelectedIndex = brandIndex == listBoxBrandList.Items.Count ? brandIndex - 1 : brandIndex; GlobalEnv.BrandListConfig.Save(); } + + private void MainForm_Shown(object sender, EventArgs e) + { + var config = GlobalEnv.ProtraConfig; + if (config.Autoclose) + priceUpdateToolStripMenuItem.PerformClick(); + } } } \ No newline at end of file diff -dur ./Protra/MainForm.designer.cs ./Protra/MainForm.designer.cs --- ./Protra/MainForm.designer.cs 2014-01-20 14:30:57.000000000 +0900 +++ ./Protra/MainForm.designer.cs 2021-04-26 19:44:55.689645700 +0900 @@ -605,6 +605,7 @@ this.Text = "Protra"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.Load += new System.EventHandler(this.MainForm_Load); + this.Shown += new System.EventHandler(this.MainForm_Shown); this.mainMenuStrip.ResumeLayout(false); this.mainMenuStrip.PerformLayout(); this.mainToolStrip.ResumeLayout(false); diff -dur ./Protra.Lib/Config/ProtraConfig.cs ./Protra.Lib/Config/ProtraConfig.cs --- ./Protra.Lib/Config/ProtraConfig.cs 2014-02-11 18:49:09.000000000 +0900 +++ ./Protra.Lib/Config/ProtraConfig.cs 2021-04-26 19:14:15.948793000 +0900 @@ -142,6 +142,11 @@ } /// + /// アプリを強制終了するかを取得または設定する。 + /// + public bool Autoclose { get; set; } + + /// /// コンストラクタ。 /// public ProtraConfig() diff -dur ./Protra.Lib/Config/PtSimConfig.cs ./Protra.Lib/Config/PtSimConfig.cs --- ./Protra.Lib/Config/PtSimConfig.cs 2014-02-11 18:49:09.000000000 +0900 +++ ./Protra.Lib/Config/PtSimConfig.cs 2021-04-26 18:50:03.229797300 +0900 @@ -68,6 +68,11 @@ public string SystemFile { get; set; } /// + /// アプリを強制終了するかを取得または設定する。 + /// + public bool Autoclose { get; set; } + + /// /// 設定ファイルの名前を取得する。 /// protected override string ConfigName diff -dur ./Protra.Lib/Lang/Builtins/DrawBuiltins.cs ./Protra.Lib/Lang/Builtins/DrawBuiltins.cs --- ./Protra.Lib/Lang/Builtins/DrawBuiltins.cs 2013-06-14 11:19:13.000000000 +0900 +++ ./Protra.Lib/Lang/Builtins/DrawBuiltins.cs 2021-04-26 02:24:00.538840600 +0900 @@ -156,8 +156,8 @@ _functionList = new List<DrawFunctionRecord>(); MinY = double.MaxValue; MaxY = double.MinValue; - Indicators = new Dictionary<int, double>[6]; - for (var i = 0; i < 6; i++) + Indicators = new Dictionary<int, double>[7]; + for (var i = 0; i < 7; i++) Indicators[i] = new Dictionary<int, double>(); } diff -dur ./Protra.Lib/Lang/Builtins/SimulateBuiltins.cs ./Protra.Lib/Lang/Builtins/SimulateBuiltins.cs --- ./Protra.Lib/Lang/Builtins/SimulateBuiltins.cs 2013-07-08 12:32:30.000000000 +0900 +++ ./Protra.Lib/Lang/Builtins/SimulateBuiltins.cs 2021-04-26 02:26:28.070261300 +0900 @@ -128,14 +128,16 @@ { case "Buy": log.Order = Order.Buy; - if (!LogData.Add(log)) - throw new RuntimeException("同日の売買があります。", null); +// if (!LogData.Add(log)) +// throw new RuntimeException("同日の売買があります。", null); + LogData.Add(log); msg += "買\r\n"; break; case "Sell": log.Order = Order.Sell; - if (!LogData.Add(log)) - throw new RuntimeException("同日の売買があります。", null); +// if (!LogData.Add(log)) +// throw new RuntimeException("同日の売買があります。", null); + LogData.Add(log); msg += "売\r\n"; break; default: diff -dur ./PtSim/MainForm.cs ./PtSim/MainForm.cs --- ./PtSim/MainForm.cs 2014-02-10 15:10:07.000000000 +0900 +++ ./PtSim/MainForm.cs 2021-04-27 11:05:21.965735300 +0900 @@ -25,6 +25,8 @@ using System.Drawing; using System.Globalization; using System.Windows.Forms; +using System.IO; +using System.Text; using Protra.Lib; using Protra.Lib.Config; using Protra.Lib.Data; @@ -39,6 +41,8 @@ /// public partial class MainForm : Form { + private string _name; + /// /// アプリケーションのメインエントリポイント /// @@ -247,6 +251,7 @@ { var worker = (BackgroundWorker)sender; var args = (Object[])e.Argument; + _name = (string)args[0]; var executor = new SystemExecutor((string)args[0], (BrandList)args[1], (TimeFrame)args[2]); executor.Execute(worker, WrapInvoke(textBoxExecute.AppendText)); if (worker.CancellationPending) @@ -272,6 +277,7 @@ private void backgroundWorkerExecute_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { + var config = GlobalEnv.PtSimConfig; if (e.Error != null) textBoxExecute.AppendText(e.Error.Message + "\r\nエラーが発生したので実行を中断します。"); else if (e.Cancelled) @@ -283,6 +289,13 @@ EndInvoke(_asyncResult); // 最後のBeginInvokeの後始末をする。 _asyncResult = null; } + { + // ファイルに書き出しをする + File.WriteAllText(@".\data\log\lasttrading.txt", textBoxExecute.Text); + DateTime dt = DateTime.Now; + String filePath = @".\data\log\trading_" + _name + "_" + dt.ToString("yyyyMMddHHmmss") + ".txt"; + File.WriteAllText(filePath, textBoxExecute.Text); + } Cursor = Cursors.Arrow; buttonExecute.Text = "実行"; progressBarExecute.Value = 0; @@ -290,6 +303,10 @@ SetEnabled(EnableFlags.All); else SetEnabled(EnableFlags.DeleteLogAll | EnableFlags.EditBrandList); + if (config.Autoclose) + { + Application.Exit(); + } } private void buttonPerformance_Click(object sender, EventArgs e) @@ -314,6 +331,7 @@ { var worker = (BackgroundWorker)sender; var args = (Object[])e.Argument; + _name = (string)args[0]; var performance = new Performance((string)args[0], (BrandList)args[1], (TimeFrame)args[2]); var profits = performance.Calculate(worker, WrapInvoke(richTextBoxPerformance.AppendText)); if (worker.CancellationPending) @@ -396,5 +414,33 @@ return; Clipboard.SetDataObject(string.Concat(rows)); } + + private void MainForm_Shown(object sender, EventArgs e) + { + var config = GlobalEnv.PtSimConfig; + if (config.Autoclose) + { + if (backgroundWorkerExecute.IsBusy) + { + backgroundWorkerExecute.CancelAsync(); + Cursor = Cursors.WaitCursor; + buttonExecute.Enabled = false; + return; + } + var system = ptFileTreeView.SelectedFile; + var brandList = comboBoxBrandList.SelectedItem; + if (system == null || brandList == null) + return; + buttonExecute.Text = "中断"; + SetEnabled(EnableFlags.Execute); + textBoxExecute.Clear(); + backgroundWorkerExecute.RunWorkerAsync(new[] { system, brandList, TimeFrame }); + } + } + + private void ExitToolStripMenuItem_Click(object sender, EventArgs e) + { + + } } } \ No newline at end of file diff -dur ./PtSim/MainForm.Designer.cs ./PtSim/MainForm.Designer.cs --- ./PtSim/MainForm.Designer.cs 2014-01-20 14:30:57.000000000 +0900 +++ ./PtSim/MainForm.Designer.cs 2021-04-27 11:22:00.712502800 +0900 @@ -168,6 +168,7 @@ this.ExitToolStripMenuItem.Name = "ExitToolStripMenuItem"; this.ExitToolStripMenuItem.Size = new System.Drawing.Size(254, 22); this.ExitToolStripMenuItem.Text = "終了(&X)"; + this.ExitToolStripMenuItem.Click += new System.EventHandler(this.ExitToolStripMenuItem_Click); // // brandListToolStripMenuItem // @@ -624,6 +625,7 @@ this.Text = "PtSim"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); this.Load += new System.EventHandler(this.MainForm_Load); + this.Shown += new System.EventHandler(this.MainForm_Shown); this.menuStripMain.ResumeLayout(false); this.menuStripMain.PerformLayout(); this.contextMenuStripHistory.ResumeLayout(false); |