C#とMetaTrader（MQL4）の連携方法その1

# 目的
C#とMetaTraderでデータのやり取りをする。

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

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

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

本稿では，ソケット通信による実装方法について検討したいと思います。

# ソケット通信によるC#とMetaTraderの連携方法について
ソケット通信による方法では，C#に限らすPythonなどでも実装可能なので，少し応用の幅が広いと思います。

データのやり取りに関しては次のように考えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531802/d0e046e7-c931-dfe7-6e76-7f71e86a7bcc.png)

C#からの要求に対しては，MetaTraderでは常に決まったポートで待ち受けるようにします。
MetaTraderからの要求に対しても，常に決まったポートで待ち受けるようにします。

これを行うことで，C#からの要求がMetaTraderからの要求とぶつからないようにすることができます。

実装時は，データをやりとりできることをしか検証していないため同じポートでやりました。

# コード
## C#側

```C#
        private void connect_ClickAsync(object sender, EventArgs e)//ボタン割り当て
        {
            ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());

            foreach (IPAddress ip in ipHostInfo.AddressList)
            {
                Console.WriteLine(ip.ToString());
                if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {

                    ipAddress = ip;
                    //break;
                }
            }
            //ipAddress = ipHostInfo.AddressList[0];
            ipAddress = IPAddress.Parse("127.0.0.1");
            Task task = Task.Run(async () => { await SocketServer(); });

            //接続が成功したら
            //disconnect.Enabled = true;
            //connect.Enabled = false;
        }



        public async Task SocketServer()//受信用のサーバを立ち上げる
        {
            //ここからIPアドレスやポートの設定
            // 着信データ用のデータバッファー。
            byte[] bytes = new byte[1024];

            await Task.Run(() =>
            {
                try
                {

                    Regex reg = new Regex("\0");
                    if (server == null)
                    {
                        //指定IP 指定ポートでサーバを起動
                        server = new TcpListener(new IPEndPoint(ipAddress, port));
                    }
                    server.Start();
                    while (startFlg)
                    {
                        if (!startFlg)
                        {
                            server.Stop();
                            break;
                        }

                        using (TcpClient tcpClient = server.AcceptTcpClient())
                        using (Stream stream = tcpClient.GetStream())
                        {
                            int i = 0;
                            while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                string data1 = reg.Replace(Encoding.UTF8.GetString(bytes), "");



                                // 任意の処理
                                //データの受取をReceiveで行う。
                                //int bytesRec = handler.Receive(bytes);
                                //string data1 = Encoding.UTF8.GetString(bytes, 0, bytesRec);
                                if (data1.Length > 0)
                                {
                                    Console.WriteLine("data1の数:" + data1.Length);
                                    Console.WriteLine(data1);

                                    String[] sep = { "," };
                                    String[] sba = data1.Split(sep, StringSplitOptions.RemoveEmptyEntries);

                                    if (this.InvokeRequired)
                                    {
                                        //別スレッドによるUI操作
                                        this.Invoke((MethodInvoker)(() =>
                                        {
                                            bidTbx.Text = sba[1];
                                            askTbx.Text = sba[2];
                                        }));
                                    }
                                    else
                                    {
                                        //UIスレッドからのUI操作
                                        bidTbx.Text = sba[1];
                                        askTbx.Text = sba[2];
                                    }




                                    //大文字に変更
                                    data1 = data1.ToUpper();

                                    //クライアントにSendで返す。
                                    byte[] msg = Encoding.UTF8.GetBytes(data1);
                                    stream.Write(msg, 0, msg.Length);
                                    Array.Clear(bytes, 0, bytes.Length);
                                }
                            }
                        }

                        // break;
                    }
                }
                catch (Exception ex)

                {
                    Console.WriteLine(ex.ToString());
                }

            });

        }
```

```C#
        public static void SocketClient(string st)//クライアント側（C#からMetaTraderにアクセスするとき)
        {
            //IPアドレスやポートを設定している。
            IPHostEntry ipHostInfo1 = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipAddress1 = ipHostInfo1.AddressList[0];

            foreach (IPAddress ip in ipHostInfo1.AddressList)
            {
                if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    ipAddress1 = ip;
                    break;
                }
            }

            ipAddress1 = IPAddress.Parse("127.0.0.1");

            IPEndPoint remoteEP = new IPEndPoint(ipAddress1, 4545);

            //ソケットを作成
            Socket socket = new Socket(ipAddress1.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            //接続する。失敗するとエラーで落ちる。
            socket.Connect(remoteEP);

            //Sendで送信している。
            byte[] msg = Encoding.UTF8.GetBytes(st + "<EOF>");
            socket.Send(msg);

            //Receiveで受信している。
            byte[] bytes = new byte[1024];
            int bytesRec = socket.Receive(bytes);
            string data1 = Encoding.UTF8.GetString(bytes, 0, bytesRec);
            Console.WriteLine(data1);

            //ソケットを終了している。
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
        }
```

MetaTrader側でどう受け取るかにもよりますが，
```C#

   SocketClient("buy,141.25,,,OK");

```
といった具合に要求することを考えています。

## MQL4

MQL4側は，[ここ](https://www.mql5.com/en/blogs/post/706665)を参考にしながら実装していければと思います。

# 完成品

一応置いておきますが，共有メモリでの実装に移行している関係で，正しく動きません！

https://github.com/InvestorX/MQL4TradePanel

# REFERENCEs

https://www.mql5.com/en/blogs/post/706665

https://qiita.com/_T_E_T_R_A_/items/bdd310714fea63474a7b

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

# さいごに

投資は自己責任！

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

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

Have a good MQL Life!!!