LoginSignup
0
1

More than 1 year has passed since last update.

MT5のトレードパネル

Posted at

目的

MetaTrader5でいい感じのトレードパネルを作る。

開発環境

  • Microsoft Visual Studio Community 2019
  • Version 16.11.9
  • Microsoft .NET Framework Version 4.8.09032

データのやりとりの仕組み

ここからの 引用です。

image.png

MQL側と.net側のコントローラ(DLL)を介して,
WinFormsとヨロシクやってくれるいい感じのやつです。

一つ問題があるとすると,コントローラで定義されているものしか,
WinFormsのコントローラとして使えないところでしょうか。

なので,DataGridViewみたいのは,コントローラ側(真ん中)で定義されていないので,
自分でDLLを改良する必要があります。

C#側

こんな感じのレイアウトで行きます。
image.png

ボタンを配置したり,TextBoxやらComboBoxを適当に配置するだけです。
イベントの割り当ては,コントローラ(DLL)で勝手にやってくれます。
余計なものもありますが,とりあえずコードを全部・・・。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TradePanel
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }



        private void txtProfit_TextChanged(object sender, EventArgs e)
        {
            if (double.Parse(txtProfit.Text) > 0.0)
            {
                allSettlement.BackColor = Color.FromArgb(192, 192, 255);
            }
            else if (double.Parse(txtProfit.Text) < 0.0)
            {
                allSettlement.BackColor = Color.FromArgb(255, 128, 128);
            }
            else
            {
                allSettlement.BackColor = btnSelClose.BackColor;
            }

            txtProfit.Text = String.Format("{0:#,0}", double.Parse(txtProfit.Text));
        }

        private void txtBox_TextChanged(object sender, EventArgs e)
        {
            TextBox text = (TextBox)sender;
            text.Text = String.Format("{0:#,0}", double.Parse(text.Text));
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox checkBox = (CheckBox)sender;
            if (checkBox.Checked)
            {
                this.TopMost = true;
            }
            else
            {
                this.TopMost = false;
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            CheckBox checkBox = checkBox1;
            if (checkBox.Checked)
            {
                this.TopMost = true;
            }
            else
            {
                this.TopMost = false;
            }
        }

        private void txtChangeBox_TextChanged(object sender, EventArgs e)
        {
            // DataGridView初期化(データクリア)
            //dataGridView1.Columns.Clear();
            //dataGridView1.Rows.Clear();


            //string[] items1 = cbxTicket.Items.OfType<string>().ToArray();
            //string[] items2 = cbxType.Items.OfType<string>().ToArray();
            //string[] items3 = cbxSymbol.Items.OfType<string>().ToArray();
            //string[] items4 = cbxOpenPrice.Items.OfType<string>().ToArray();
            //string[] items5 = cbxTP.Items.OfType<string>().ToArray();
            //string[] items6 = cbxSL.Items.OfType<string>().ToArray();
            //string[] items7 = cbxPrice.Items.OfType<string>().ToArray();
            //string[] items8 = cbxSwap.Items.OfType<string>().ToArray();
            //string[] items9 = cbxCommission.Items.OfType<string>().ToArray();
            //string[] items10 = cbxProfit.Items.OfType<string>().ToArray();

            //DataGridViewRow[] rows = new DataGridViewRow[items1.Length * 10];

            //for (int i = 0; i < items1.Length; i++)
            //{
            //    string[] data = new string[10];
            //    data[0] = items1[i];
            //    data[1] = items2[i];
            //    data[2] = items3[i];
            //    data[3] = items4[i];
            //    data[4] = items5[i];
            //    data[5] = items6[i];
            //    data[6] = items7[i];
            //    data[7] = items8[i];
            //    data[8] = items9[i];
            //    data[9] = items10[i];

            //    DataGridViewRow row = new DataGridViewRow();
            //    row.CreateCells(this.dataGridView1);
            //    row.SetValues(data);
            //    rows[i] = row;
            //}

            //this.dataGridView1.Rows.AddRange(rows);
        }

        private void textBox2_TextChanged(object sender, EventArgs e)
        {

        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {

        }
    }
}

MQL側

続いてMQL側です。
MetaTrader5にしか対応してないです。

OnTickで,TickDataを受け取る度にイベントが発生し,
WinForms側のコントローラとつながるイメージです。

//+------------------------------------------------------------------+
//|                                              GuiMtController.mq5 |
//|                                   Copyright 2020, IINEbotXXX7963 |
//|                               https://twitter.com/IINEbotXXX7963 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, IINEbotXXX7963"
#property link      "https://twitter.com/IINEbotXXX7963"
#property version   "1.00"
//https://www.mql5.com/ja/articles/5563
//https://www.mql5.com/ja/articles/6549
//https://github.com/PublicMqlProjects/GuiControllerElementsDemo
//https://github.com/PublicMqlProjects/MtGuiController/blob/master/MtGuiController/IController.cs

#include <Trade\Trade.mqh>
string FormName = "Form1";
#import  "MtGuiController.dll"
input string assembly = "C:\\MT5\\TradePanel\\TradePanel\\bin\\Debug\\TradePanel.exe"; //実行ファイルの保存場所
input string dummy1 =""; //-------------------------------
input int msl = 10;//初期のsl pips数
input int mtp = 20;//初期のtp pips数

int gsl;
int gtp;

input double mcurrent_volume = 0.01; //初期のlot数
input string dummy2 =""; //-------------------------------
//-- 注文を実行するための取引モジュール
CTrade Trade;
double current_volume;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//--- タイマーを作成する
   EventSetMillisecondTimer(150);
   GuiController::ShowForm(assembly, FormName);

   current_volume = mcurrent_volume;

   GuiController::SendEvent("currentVolume", MtGuiController::GuiEventType::TextChange, 0, 0.0, DoubleToString(current_volume, 2));


   for(int i = 0; i < SymbolsTotal(true); i++)
     {
      GuiController::SendEvent("symbolCbx", MtGuiController::GuiEventType::AddItem, 0, 0, SymbolName(i, true));
     }


//---値をゲット
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   GuiController::SendEvent("askValue", MtGuiController::GuiEventType::TextChange, 0, 0.0, DoubleToString(ask, Digits()));
   GuiController::SendEvent("bidValue", MtGuiController::GuiEventType::TextChange, 0, 0.0, DoubleToString(bid, Digits()));

   GuiController::SendEvent("symbolCbx", MtGuiController::GuiEventType::TextChange, 0, 0.0, Symbol());

   gsl = msl;
   gtp = mtp;
   GuiController::SendEvent("txtSL", MtGuiController::GuiEventType::TextChange, 0, 0.0,IntegerToString(msl));
   GuiController::SendEvent("txtTP", MtGuiController::GuiEventType::TextChange, 0, 0.0,IntegerToString(mtp));

   GuiController::SendEvent("txtBalance", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE),0));//残高
   GuiController::SendEvent("txtCredi", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_CREDIT),0));//クレジット
   GuiController::SendEvent("txtEquity", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),0));//有効証拠金
   GuiController::SendEvent("txtMargin", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN),0)); //必要証拠金
   GuiController::SendEvent("txtMarginFree", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_FREE),0)); //余剰証拠金
   GuiController::SendEvent("txtMarginLevel", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL),2) +"%"); //証拠金維持率
   GuiController::SendEvent("txtProfit", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_PROFIT),0));//全損益
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   GuiController::HideForm(assembly, FormName);
   EventKillTimer();

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   GuiController::SendEvent("askValue", MtGuiController::GuiEventType::TextChange, 0, 0.0, DoubleToString(ask, Digits()));
   GuiController::SendEvent("bidValue", MtGuiController::GuiEventType::TextChange, 0, 0.0, DoubleToString(bid, Digits()));


   GuiController::SendEvent("txtBalance", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE),0));//残高
   GuiController::SendEvent("txtCredi", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_CREDIT),0));//クレジット
   GuiController::SendEvent("txtEquity", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY),0));//有効証拠金
   GuiController::SendEvent("txtMargin", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN),0)); //必要証拠金
   GuiController::SendEvent("txtMarginFree", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_FREE),0)); //余剰証拠金
   GuiController::SendEvent("txtMarginLevel", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL),2) +"%"); //証拠金維持率
   GuiController::SendEvent("txtProfit", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(AccountInfoDouble(ACCOUNT_PROFIT),0));//全損益



  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---






//--- タイマーで新しいイベントを取得する
   for(static int i = 0; i < GuiController::EventsTotal(); i++)
     {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      if(id ==  MtGuiController::GuiEventType::TextChange)
        {
         if(el_name == "currentVolume")
           {
            TrySetNewVolume(sparam);
           }

         if(el_name == "txtSL")
           {
            Print("SL->" + sparam);
            gsl = (int)StringToInteger(sparam);
           }
         if(el_name == "txtTP")
           {
            Print("TP->" + sparam);
            gtp = (int)StringToInteger(sparam);
           }
        }
      else
         if(id ==  MtGuiController::GuiEventType::ScrollChange && el_name == "incrementVol")
            OnIncrementVolume(lparam, dparam, sparam);
         else
            if(id ==  MtGuiController::GuiEventType::ClickOnElement)
               TryTradeOnClick(el_name);
            else
               if(id ==  MtGuiController::GuiEventType::ComboBoxChange && el_name == "symbolCbx")
                 {
                  ChartSetSymbolPeriod(0,sparam,0);
                 }

     }

   double totalPips = 0.0;
   int pt = PositionsTotal();
   for(int j = 0; j < PositionsTotal(); j++)
     {
      string symbol=PositionGetSymbol(j); // Get the symbol name
      if(PositionSelect(symbol))
        {
         double  value = 0.0;
         int digits ;
         digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
         double pips = 0.0;
         // 3桁・5桁のFXブローカーの場合
         if(digits == 3 || digits == 5)
           {
            pips =  MathPow(10, digits) / 10;
           }
         // 2桁・4桁のFXブローカーの場合
         if(digits == 2 || digits == 4)
           {
            pips =  MathPow(10, digits);
           }

         int positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
         string str="";
         //---
         if(positionType==0)
           {
            str="buy";
            //POSITION_PRICE_CURRENT -> このポジションの現在価格
            //POSITION_PRICE_OPEN -> このポジションの価格
            value = PositionGetDouble(POSITION_PRICE_CURRENT) - PositionGetDouble(POSITION_PRICE_OPEN);

           }
         if(positionType==1)
           {
            str="sell";
            value = PositionGetDouble(POSITION_PRICE_OPEN) - PositionGetDouble(POSITION_PRICE_CURRENT);
           }

         totalPips += value * pips;
         //Print("TOTAL_PIPS-> " + DoubleToString(totalPips));

        }


     }


   GuiController::SendEvent("txtTotalPips", MtGuiController::GuiEventType::TextChange, 0, 0.0,DoubleToString(totalPips,1));//全損益
   
//   
//      datetime tm=TimeCurrent();
//   int position =PositionsTotal();
//
//   for(int p = 0; p < position; p++)
//     {
//      string pos_symbol = PositionGetSymbol(p); // Get the symbol name
//      int digits ;
//      digits = (int)SymbolInfoInteger(pos_symbol, SYMBOL_DIGITS);
//      if(PositionSelect(pos_symbol))
//        {
//
//
//         //GuiController::SendEvent("cbxTicket", MtGuiController::GuiEventType::AddItem, 0, 0, IntegerToString(PositionGetInteger(POSITION_IDENTIFIER)));
//         //GuiController::SendEvent("cbxType", MtGuiController::GuiEventType::AddItem, 0, 0, PositionTypeToString((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)));
//         //GuiController::SendEvent("cbxSymbol", MtGuiController::GuiEventType::AddItem, 0, 0, PositionGetString(POSITION_SYMBOL));
//         //GuiController::SendEvent("cbxOpenPrice", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits));
//         //GuiController::SendEvent("cbxTP", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_TP),digits));
//         //GuiController::SendEvent("cbxSL", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_SL),digits));
//         //GuiController::SendEvent("cbxPrice", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_PRICE_CURRENT),digits));
//         //GuiController::SendEvent("cbxSwap", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_SWAP),0));
//         //GuiController::SendEvent("cbxCommission", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_COMMISSION),0));
//         //GuiController::SendEvent("cbxProfit", MtGuiController::GuiEventType::AddItem, 0, 0, DoubleToString(PositionGetDouble(POSITION_PROFIT),0));
//        }
//     }

   //GuiController::SendEvent("txtChangeBox", MtGuiController::GuiEventType::TextChange, 0, 0.0,TimeToString(tm)+TimeToString(tm,TIME_SECONDS));


  }
//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
//---

  }
//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
//---

  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---

  }
//+------------------------------------------------------------------+
//| BookEvent function                                               |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
  {
//---

  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//| 数量を検証する                                                   |
//+------------------------------------------------------------------+
double ValidateVolume(double n_vol)
  {
   double min_vol = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   double max_vol = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
//-- 下限を確認する
   if(n_vol < min_vol)
      return min_vol;
//-- 上限を確認する
   if(n_vol > max_vol)
      return max_vol;
//-- 数量を正規化する
   double vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   double steps = MathRound(n_vol / vol_step);
   double corr_vol = NormalizeDouble(vol_step * steps, 2);
   return corr_vol;
  }
//+------------------------------------------------------------------+
//| 与えられたテキストからの新しい現在の数量を設定する                  |
//+------------------------------------------------------------------+
bool TrySetNewVolume(string nstr_vol)
  {
   double n_vol = StringToDouble(nstr_vol);
   current_volume = ValidateVolume(n_vol);
   string corr_vol = DoubleToString(current_volume, 2);
   GuiController::SendEvent("currentVolume", MtGuiController::GuiEventType::TextChange, 0, 0.0, corr_vol);
   return true;
  }
//+------------------------------------------------------------------+
//| 取引注文を実行する                                              |
//+------------------------------------------------------------------+
bool TryTradeOnClick(string el_name)
  {

//double tsl,ttp;

// 現在の通貨ペアの小数点以下の桁数を取得
   int digits ;
   digits = (int)SymbolInfoInteger(Symbol(), SYMBOL_DIGITS);
   double pips = 0.0;
// 3桁・5桁のFXブローカーの場合
   if(digits == 3 || digits == 5)
     {
      pips =  MathPow(10, digits) / 10;
     }
// 2桁・4桁のFXブローカーの場合
   if(digits == 2 || digits == 4)
     {
      pips =  MathPow(10, digits);
     }


   if(el_name == "btnBuy")
     {
      double dvalue = 0.0;
      double avalue = SymbolInfoDouble(Symbol(), SYMBOL_BID);
      double limitRate,stopRate;
      dvalue = SymbolInfoDouble(Symbol(), SYMBOL_ASK); //buy時基準
      limitRate = NormalizeDouble(dvalue + gtp * 1/pips, digits);        // リミット価格    を正規化
      stopRate  = NormalizeDouble(avalue - gsl * 1/pips, digits);         // ストップロス価格を正規化
      ////---
      //  if(avalue > stopRate)
      //      stopRate = avalue;

      //Print("OpenPrice-> " + dvalue + " SL-> " + stopRate + " TP-> "+ limitRate);
      if(gsl == 0.0 && gtp == 0.0)
        {
         return Trade.Buy(current_volume);
        }
      if(gsl == 0.0)
        {
         return Trade.Buy(current_volume,NULL,0,NULL,limitRate);
        }
      if(gtp == 0.0)
        {
         return Trade.Buy(current_volume,NULL,0,stopRate,NULL);
        }

      return Trade.Buy(current_volume,NULL,0,stopRate,limitRate);
     }
   if(el_name == "btnSell")
     {
      double dvalue = 0.0;
      double avalue = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
      double limitRate,stopRate;
      dvalue = SymbolInfoDouble(Symbol(), SYMBOL_BID); //buy時基準
      limitRate = NormalizeDouble(dvalue - gtp * 1/pips, digits);        // リミット価格    を正規化
      stopRate  = NormalizeDouble(avalue + gsl * 1/pips, digits);         // ストップロス価格を正規化

      if(gsl == 0.0 && gtp == 0.0)
        {
         return Trade.Sell(current_volume);
        }
      if(gsl == 0.0)
        {
         return Trade.Sell(current_volume,NULL,0,NULL,limitRate);
        }
      if(gtp == 0.0)
        {
         return Trade.Sell(current_volume,NULL,0,stopRate,NULL);
        }

      return Trade.Sell(current_volume,NULL,0,stopRate,limitRate);
     }
   if(el_name == "allSettlement")
     {
      positionAllClose();
      return true;
     }
   if(el_name == "btnSelClose")
     {
      positionSelectClose();
      return true;
     }//
   return false;
  }

//+------------------------------------------------------------------+
//|        すべてのポジションを決済                                        |
//+------------------------------------------------------------------+
void positionAllClose()
  {
   CPositionInfo cpo;
   int position =PositionsTotal();
   for(int p = 0; p < position; p++)
     {
      string pos_symbol = PositionGetSymbol(p); // Get the symbol name
      if(PositionSelect(pos_symbol))
        {
         int ti = (int)PositionGetInteger(POSITION_IDENTIFIER);
         Print("pos_symbol:" + pos_symbol + " tiket:" + IntegerToString(ti));
         if(!Trade.PositionClose(ti,0))
           {
            Sleep(20);
            Trade.PositionClose(ti,0);
           }
        }


     }

   Sleep(20);

   position = PositionsTotal();
   if(position > 0)
     {
      positionAllClose();
     }
  }

//+------------------------------------------------------------------+
//|        現在表示されているシンボルのみ決済                               |
//+------------------------------------------------------------------+
void positionSelectClose()
  {
   int position =PositionsTotal();
   for(int p = 0; p < position; p++)
     {
      if(PositionSelect(_Symbol))
        {
         int ti = (int)PositionGetInteger(POSITION_IDENTIFIER);
         Trade.PositionClose(ti,0);
        }
     }
  }



//+------------------------------------------------------------------+
//| 現在の数量を増減する                                        |
//+------------------------------------------------------------------+
void OnIncrementVolume(long lparam, double dparam, string sparam)
  {
   double vol_step = 0.0;
//-- インクリメントプレスを検出する
   if(dparam > lparam)
      vol_step = (-1.0) * SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
//-- デクリメントプレスを検出する
   else
      if(dparam < lparam)
         vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
      //-- インクリメントプレスを再検出する
      else
         if(lparam == 0)
            vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
         //-- デクリメントプレスを再検出する
         else
            vol_step = (-1.0) * SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   double n_vol = current_volume + vol_step;
   current_volume = ValidateVolume(n_vol);
   string nstr_vol = DoubleToString(current_volume, 2);
   GuiController::SendEvent("currentVolume", MtGuiController::GuiEventType::TextChange, 0, 0.0, nstr_vol);
  }

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| CONVERTING POSITION TYPE TO A STRING                             |
//+------------------------------------------------------------------+
string PositionTypeToString(int position_type)
  {
   string str="";
//---
   if(position_type==0) { str="buy";  }
   if(position_type==1) { str="sell"; }
//---
   return(str);
  }
//+------------------------------------------------------------------+

このMQL5をExpertsフォルダに配置して,
Librariesフォルダには,ここから落としてくるか,
GitHubページからソースを落としてビルドしてください!

完成品

完成品はこちらにあります!

REFERENCEs

さいごに

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

Have a good MQL Life!!!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1