MT4のトレードパネル

# 目的
C#とMetaTraderでデータのやり取りをして、トレードパネルを作る。

# 前提条件

https://qiita.com/InvestorX/items/e609254f8581de14d971

# （おさらい）データのやり取りに関する実現方法について
データのやり取りに関する実現方法については，主に3種類あると考えられます。

1. 共有メモリを使用する方法
1. ソケット通信による方法
1. ファイルによる方法

恐らく３番目が一番実装しやすいのではないでしょうか。
ただし，３番目の場合，ファイルアクセスのルールを決めないといけないため，少しコードの工夫が必要です。

共有メモリを使ってトレードパネルを実装していきたいと思います！！！

# 画面構成

こんな感じで作りこんでいきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/53af5020-22e1-4889-9405-b9ca343ab5ed.png)

Lotにカーソル合わせると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/e69efba8-a462-aefa-7563-140423d73070.png)

あとはどんだけ作りこむかだけなので、実装次第でなんとでもなります。

GitHubに置いてあるのを一番下のリンクに貼りますが、MQL4ファイルがどっからか持ってきたやつのままで、このままだとC#と全然つながりません。（だれもpull requestとかissue上げてくれなかったのはこのせい？と勝手に思っている！！！）


# C#

前提条件の記事にあるように

```C#
//共有メモリにアクセスするためのメンバ
MemoryMappedFile share_mem;
MemoryMappedFile shareMemOrder;
//MT4->C#/C#->MT4の区別をつけるためのタグ
String pullTAG = "mtexe";
String pushTAG = "exemt";
```

が大事だと思ってます。

このほかThreadingTimerを使って、200msec毎にMT4から新しいメッセージが来ていないかチェックしています。

Windowがロードされたタイミングで、MT4が起動しているか確認します。

一応、MT4側からは、自分の口座情報的（通貨ペアなど）なものを共有メモリに既に書き込んで置いてあるイメージです。

C#側からこの口座情報的なものを最初に読みます。

```C#
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    orderFlag = false;
    mt4Status.Text = "未連携";
    Trace.WriteLine("mtexe:" + getMemorryString(pullTAG) + ":");
    Trace.WriteLine(getMemorryString(pullTAG+".array"));
    String getSymbol = getMemorryString("mtexe").Trim().Replace("\0", "");

    share_mem = MemoryMappedFile.CreateNew(pushTAG+".symbol", 256, MemoryMappedFileAccess.ReadWriteExecute);

    shareMemOrder 
       = MemoryMappedFile.CreateOrOpen(pushTAG+".order", 256, MemoryMappedFileAccess.ReadWriteExecute);

    //シンボルがあればMT4との連携ができているものとし、
    //定期実行用のThreadingTimerを動かす。
    if (getSymbol != null && getSymbol.Length > 0)
    {
        timerDelegate = new TimerCallback(requestMTStatus);
        timer = new Timer(timerDelegate, null, 0, 200);
        statusBorder.Background = Brushes.LightGreen;
        mt4Status.Text = "連携済み";
    }
    if (getMemorryString(pullTAG+ ".array") != null && getMemorryString(pullTAG +".array").Length > 0)
    {
        int selectedItem = 0;
        int i = 0;
        String[] sep = { "," };
        String[] f = getMemorryString(pullTAG+".array").Split(sep, StringSplitOptions.RemoveEmptyEntries);
        Trace.WriteLine("\r\nMT4通貨:" + getSymbol);
        foreach (String str in f)
        {
            if (str.IndexOf(getSymbol) > -1)
            {
                selectedItem = i;
            }
            else
            {
                i++;
            }
            comboBox.Items.Add(str);
        }

        //Trace.WriteLine("MT4側の選択されている通貨は？" + selectedItem);
        comboBox.SelectedItem = selectedItem;
        comboBox.Text = getSymbol;
    }
}
```

次に定期実行されるrequestMTStatusですが、requestMTStatus内では、MT4側から流れてくるであろうBidとAskの価格を表示させるようにします。

スプレッドもついでに表示させます。

```C#

private void requestMTStatus(object state)
{

    this.Dispatcher.Invoke(() =>
    {
        try
        {
            //
            String orderEnable = getMemorryString(pullTAG + ".order");
            orderEnable = orderEnable.Replace("\0", "");
            if (orderEnable.Length > 0)
            {
                if (orderFlag)
                {
                    Trace.WriteLine("o:" + orderEnable);
                    MemoryMappedViewAccessor accessor = shareMemOrder.CreateViewAccessor();
                    // sellは1
                    // Write data to shared memory
                    string orderStr = "";
                    char[] data = orderStr.ToCharArray();
                    accessor.Write(0, data.Length);
                    accessor.WriteArray<char>(0, data, 0, data.Length);

                    // Dispose accessor
                    accessor.Dispose();
                    orderFlag = !orderFlag;
                }
            }


            String val = getMemorryString(pullTAG+".value");
            //Trace.WriteLine(val);
            textBlock.Text = val;
            String[] sep = { "," };
            String[] sep2 = { "." };
            String[] str = val.Split(sep, StringSplitOptions.RemoveEmptyEntries);

            //Trace.WriteLine(str[0]+","+str[1]);
            String[] bid = str[0].Split(sep2, StringSplitOptions.RemoveEmptyEntries);
            String[] ask = str[1].Split(sep2, StringSplitOptions.RemoveEmptyEntries);

            int digits = int.Parse(str[2].Replace("\0", ""));
            digits = digits - 3 < 0 ? 0 : digits - 3;
            int index = str[0].IndexOf(".");

            selltextBlock.Text = "";
            Underline underline = new Underline();
            Run run = new Run();
            run.Text = "SELL\n";
            run.Foreground = Brushes.White;
            run.FontSize = 16;
            underline.Inlines.Add(run);
            underline.Foreground = Brushes.White;

            this.selltextBlock.Inlines.Add(underline);
            selltextBlock.TextAlignment = TextAlignment.Right;
            this.selltextBlock.Inlines.Add(bid[0] + "." + bid[1].Substring(0, digits));
            run = new Run();
            run.Text = bid[1].Substring(digits, 2);
            run.FontSize = 24;
            this.selltextBlock.Inlines.Add(run);
            //int qr = digits - 2;
            this.selltextBlock.Inlines.Add(bid[1].Substring(digits + 2, 1));
            sellBtn.Content = selltextBlock;

            //buyの処理
            buytextBlock.Text = "";
            Underline underline2 = new Underline();
            Run run2 = new Run();
            run2.Text = "BUY\n";
            run2.Foreground = Brushes.White;
            run2.FontSize = 16;
            underline2.Inlines.Add(run2);
            underline2.Foreground = Brushes.White;

            this.buytextBlock.Inlines.Add(underline2);
            buytextBlock.TextAlignment = TextAlignment.Left;

            this.buytextBlock.Inlines.Add(ask[0] + "." + ask[1].Substring(0, digits));
            run2 = new Run();
            run2.Text = ask[1].Substring(digits, 2);
            run2.FontSize = 24;
            this.buytextBlock.Inlines.Add(run2);
            this.buytextBlock.Inlines.Add(ask[1].Substring(digits + 2, 1));
            buyBtn.Content = buytextBlock;

            spreadTbk.Text = "";
            Run run3 = new Run();
            run3.Text = "Spread\n" + str[3];
            run3.FontSize = 18;
            this.spreadTbk.Inlines.Add(run3);
            this.spreadTbk.TextAlignment = TextAlignment.Center;



        }
        catch (Exception ex)
        {
            Trace.WriteLine(ex.ToString());
        }

    });

}
```

あとは、BUYボタン押したときやSELLボタン押したときの処理をMT4側に流してあげれば良いです。

# MQL側

>ソースコード探してます。

```C
///ソース見つかったら更新します！！！
```


# 完成品

GitHubにトレードパネルがあるので、みんなで改良していきましょう！

https://github.com/InvestorX/MQL4TradePanelW

# REFERENCEs

https://www.mql5.com/ja/articles/115

https://www.ipentec.com/document/csharp-use-memory-mapped-file

https://www.mql5.com/en/articles/1540

https://www.mql5.com/en/code/8577

# さいごに

投資は自己責任！

カレンダー参加者募集してます！

https://qiita.com/advent-calendar/2022/mql

Have a good MQL Life!!!