عبور از محدودیت‌های استراتژی‌تستر در تست اکسپرت هج

عبور از محدودیت‌های استراتژی‌تستر در تست اکسپرت هج

عبور از محدودیت‌های استراتژی‌تستر در تست اکسپرت هج

در این مقاله به ایده‌ی تست اکسپرت هج در استراتژی‌تستر می‌پردازیم. همانطور که می‌دانید استراتژی‌تستر محدودیت‌های خاص خودش را دارد و برای یک نمادِ (symbol) دیگر، نمی‌تواند هیچ معامله‌ای باز کند. هر کاربری که بخواهد اکسپرت/اکسپرت‌های هج خود را تست کند باید فقط در حالت واقعی (live) این کار را انجام دهد. اما آیا این موضوع توانایی‌های ما را محدود می‌سازد؟ مطمئن هستم که هر تریدر هج، نیاز دارد که اکسپرت خود را قبل از رفتن به حالت واقعی، تست کند. بنابراین، می‌خواهم این ایده را بدهم که بیاییم یک‌نوع وضعیت مجازی جهت تست استراتژی (تستر-مانند) ایجاد کنیم، و امیدوارم این کار به ما کمک کند محدودیت‌های استراتژی‌تستر متاتریدر ۴ را شکسته و بتوانیم بعداً استفاده‌های دیگری نیز داشته باشیم.

مفهوم تستر مجازی

وقتی داشتم در mq4 با تابع “Files” کار می‌کردم، ایده‌ی تستر مجازی به‌ ذهنم خطور کرد. و در ادامه این فکر به ذهنم رسید که برخی از داده‌های مهم را از یک فایل بگیرم تا به‌نوعی طرح ترید مجازی را آماده کنم. “آیا این پاسخ سوال من درباره‌ی تست اکسپرت هج است؟” بیایید امتحان کنیم.

تستر مجازی من به هیچ برنامه یا نرم‌افزار خارجی نیاز ندارد. همه‌چیز را می‌توان توسط پارامتر‌های mq4 انجام داد. مفهوم این تستر مجازی این است که اجازه دهیم پارامترهای مشخص‌شده برای باز کردن یا بستن معاملات هج به ما بگویند که داده‌های مورد نیاز را جمع‌آوری کنیم؛ مثل قیمت بازشدن، زمان بازشدن، قیمت بسته‌شدن، زمان بسته‌شدن، و تمام دیگر داده‌های مهم. بعد از اینکه داده‌های مورد نیاز تعیین و جمع‌آوری شدند، از آن‌ها برای مقایسه با مقدار آخرین تیکِ هر نوع داده‌ی همسان، مانند قیمت بازشدن و آخرین قیمت پیشنهادی برای فروش (Bid)، یا قیمت بازشدن و آخرین خرید (Ask)، استفاده می‌کنیم. چنین مقادیری ما را به‌سمت روش محاسبه‌ی سود هدایت می‌کنند و این کار ما را راهنمایی می‌کند گروه جدیدی از داده‌ها را بعد از برآورده‌ شدن شرایط بسته‌‌شدن هج، جمع‌آوری کنیم.

گروه‌های داده، به‌شکل یک فایل برای استفاده‌های بیشتر درمی‌آیند. بعد از اینکه تست گرفتن انجام شد و انواع داده‌ها در فایل یا فایل‌هایی ذخیره شدند، قادر خواهیم بود ببینیم “چگونه اکسپرت هج اجرا شده‌است”. داده‌ها را می‌دهیم تا آن‌ها را به‌شکل اندیکاتور منحنی عملکرد ترسیم کنند، و پس از آن تست مجازی اکسپرت هج خود را می‌توانیم تمام کنیم.

با این مفهوم، فکر می‌کتنم بتوانیم نتایج تست را که مشابه با نتایج‌ استراتژی‌تستر واقعی هستند، داشته باشیم. باید این را بگویم که این فقط یک ایده برای ایجاد یک تستر برای اکسپرت‌های هج است و ضمانت نمی‌کنم که دقیقاً عین تستر واقعی به ما نتیجه دهد. اما امیدوارم حداقل مرجع خوبی برای بحث‌های آینده باشد.

بیایید شروع کنیم.

معنای سادهی معاملهی هج

قبل از اینکه شروع کنیم، بیایید کمی دربا‌ره‌ی “HEDGE” صحبت کنیم. ساده‌ترین راه برای تعریف هجینگ این است که دو معامله‌ در جهت مخالف [یکدیگر] در دو جفت‌اَرز، همزمان باز کنیم. این کار برای کم کردن ریسک معامله انجام می‌شود، اگر یکی از معاملات به‌سمت بالا برود، آن یکی قطعاً به‌سمت‌ پایین حرکت می‌کند، اما هیچ جای نگرانی نیست زیرا همزمان هم معامله‌ی Sell داریم، هم معامله‌ی Buy، و اگر یکی را از دست بدهیم، دیگری را بدست آورده‌ایم، و به همین دلیل است که این را “کم کردن ریسک” می‌نامند. در دنیای فارکس، انواع و اقسام سبک‌های معامله در جهت مخالف، داریم.

  • برای دو جفت‌اَرز که همیشه مثل هم حرکت می‌کنند، مانند EURUSD و GBPUSD، Buy برای EURUSD و Sell برای GBPUSD، به‌صورت همزمان، می‌شود هجینگ.
  • برای دو جفت‌اَرز که همیشه مخالف هم حرکت می‌کنند، مانند EURUSD و USDCHF، Buy برای EURUSD و Buy برای USDCHF می‌شود هج کردن آن‌ها.
  • و حتی Buy و Sell گرفتن همزمان برای EURUSD نیز هج کردن به‌حساب می‌آید، اما بعضی‌وقت‌ها به این کار “arbitrate” می‌گویند.

در معاملات هج، حقایقی هست که نمی‌توان آن‌ها را زیر سوال برد.

  1. همبستگی. یک ابزار آماری است برای ارتباط بین دو واحد پولی. ضریب همبستگی، محدوده‌ای از ۱- تا ۱+ دارد. ۱+ یعنی دو جفت‌اَرز صد در صد مواقع در یک جهت حرکت می‌کنند. ۱- یعنی دو جفت‌اَرز صد در صد مواقع در جهت مخالف [یکدیگر] حرکت می‌کنند. و در صورتیکه ۰ باشد، بدان معناست که ارتباط بین دو جفت‌اَرز مدنظر، کاملاً تصادفی است (بیشتر بخوانید).
  2. نسبت لاتیج. برای کار کردن با دو جفت‌اَرزی که نه در جهت یکدیگرند و نه مخالف یکدیگر حرکت می‌کنند، “نسبت لاتیج” کاملاً مهم و ضروری است زیرا نوسانات و قابلیت حرکت این دو جفت‌اَرز، متفاوت و همانند لاک‌پشت و خرگوش است، و اگر یکی از این‌ها لاک‌پشت باشد، دیگری خرگوش است. نسبت لاتیج می‌تواند ریسک را کمتر کند، حرکتی که قوی‌تر است، یا همان جفت‌اَرزی که خرگوش است، تاثیر خود را گذاشته و با درج لاتیج بالاتر برای جفت‌اَرز لاک‌پشت، ضررِ کمتر، هنگامیکه جفت‌اَرز خرگوش ما در جهت منفی حرکت می‌کند، تضمین‌شده‌ است. آنگاه می‌توانید از لاک‌پشتی که در جهت مثبت است، بیشتر سود بگیرد، و یا به‌عبارت دیگر، لاک‌پشت سودده، ضرر خرگوش را جبران می‌کند. بنابراین، تکنیک هج این خاطرجمعی را به شما می‌دهد که بیشتر از باز کردن فقط یک معامله‌ در سَمتِ منفی، ضرر نمی‌کنید.

راستی، آیا تابحال از خود پرسیده‌اید که یک معامله‌گر هجر چگونه با این سبک معامله‌ کردن، سود بدست می‌آورد؟ نگران نباشید، همیشه بین دو جفت‌اَرز، نقاط مشترک و به‌نوعی روی‌هم‌اُفتادن (overlap) داریم، و همبستگی مطلق و دائمی نیست، مکرر تاخیر از سوی یک جفت‌اَرز را شاهد خواهیم بود، یکی شروع به حرکت می‌کند و دیگری بعد از آن شروع می‌کند؛ و دوباره و دوباره، درست مثل لاک‌پشت و خرگوش – خرگوش وسط راه استراحت می‌کند و منتظر لاک‌پشت است تا شکستش دهد و برنده شود. به همین دلیل است که معامله‌گرهای هج سودهای خوبی می‌گیرند. و امروزه بسیاری از افراد از این روش برای کسب درآمد در فارکس استفاده می‌کنند و جای نگرانی هم نیست. هج کنید، صبر کنید و وقتی سود مثبت دیدید، معامله را ببندید، و همین!

مفهوم هج

قبل از اینکه کدنویسی تستر مجازی را آغاز کنیم، بیایید مفهوم هج را در یک ازمایش بفهمیم. بدون مفهوم هج هیچ‌گاه نخواهیم دانست کدام نوع داده را باید خروجی بگیریم، ثبت و محاسبه کنیم. این داده‌ها به ما نشان می‌دهند چه نوع معامله‌ای را باید به‌صورت مجازی ایجاد کرد. در این آزمایش قوانین هج را اینگونه تعریف می‌کنم.

  • به‌صورت روزانه و در آغاز روز، [معامله‌ی] هج باز کنید
  • وقتی معامله به ۱۰۰ دلار رسید آن را ببندید (لاتیج، ۱ و ۲ لات باشد)
  • داده‌های تیک قیمت را ساعتی جمع‌آوری کنید***
  • در آغاز روز جدید، حتی اگر به سود مورد نظر نرسیدید، داده‌ها را پاک کنید
  • فقط، ۲ لات EURJPY بخرید، و ۱ لات GBPJPY بفروشید.

بر اساس این قوانین، معاملات مجازی نیاز به قیمت‌های بازشدن روزانه‌ی (هر دو جفت‌اَرز) دارند تا از آن‌ها به‌عنوان قیمت بازشدن معامله استفاده کنیم. برای محاسبه‌ی سود در طول روز، قیمت هر روز و در هر ساعت، به‌عنوان تیک قیمت، باید به‌عنوان داده برای قیمت بسته‌شدن معامله ثبت شود (ask برای sell، و bid برای buy) و باید همراه با زمان آن تیک، ثبت شدن انجام شود (تا مطمئن شویم قیمت تیک از مقدارِ زمانی مشابه می‌آید). و با درنظر گرفتن مفهوم معاملات هج به‌صورت روزانه، تمام داده‌های مورد نیاز را در دو نوع فایل تقسیم می‌کنم، که بازشدن روزانه و مقدار تیک هر دو جفت‌اَرز هستند. هر دو نوع داده را در قالب فایل‌های رشته‌ای، همراه با نام‌های متفاوت، مانند GBPJPYD1.csv و GBPJPYTick.csv، خروجی می‌گیریم.

به‌خاطر داده‌های تیک، و از آنجاییکه می‌خواهیم تستر مجازی ما تا سرحد امکان شبیه به استراتژی‌تستر واقعی باشد، باید این دو مرحله را انجام دهیم.

  • اسکریپتی بنویسید که قیمت بازشدن روزانه GBPJPY را در قالب یک فایل خروجی بگیرد.
  • اسکریپتی بنویسید که قیمت تیک روزانه GBPJPY را در قالب یک فایل خروجی بگیرد.

و هر دو مرحله را باید برای EURJPY نیز انجام دهید.

اما فکر می‌کنم می‌توانیم همه‌ی این‌ها را در قالب یک اکسپرت ترکیب کنیم، و این اکسپرت باید هر دو نوع داده را در قالب دو فایل مجزا خروجی بدهد. و بعد از اینکه این اکسپرت فرآیند ثبت داده‌ها را تمام کرد، یک اکسپرت جدید برای ایجاد معامله‌ی مجازی، تمام داده‌های GBPJPY و EURJPY را از فایل‌های خروجی‌گرفته‌‌شده می‌گیرد تا تست مجازی روی آن‌ها انجام شود.

۳ گام برای شکستن محدودیت تست

با ایده‌ای که دادم، می‌توانیم رویای شکستن محدودیت تست را فقط در ۳ گام به حقیقت تبدیل کنیم.

  1. داده‌های قیمت را گرفته و آن‌ها را در قالب فایل‌هایی با استفاده از اکسپرت، خروجی بگیرید.
  2. با یک اکسپرت دیگر شرایط تست مجازی را ایجاد کرده، بطوریکه این اکسپرت هم نتایج را به‌شکل یک فایل خروجی دهد.
  3. در پنجره‌ای جداگانه نتایج را به‌شکل یک اندیکاتور بازبینی کنید.

پس بیایید گام اول را برداریم.

گام ۱. خروجی گرفتن از داده‌های قیمت

در پایین اکسپرتی را می‌بینید که برای خروجی گرفتن از قیمت بازشدن روزانه‌ی اَرز مدنظر در قالب یک فایل با نام “GBPJPYD1.csv” برای GBPJPY و “EURJPYD1.csv” برای EURJPY طراحی شده‌است. و در عین حال، قیمت تیک را هم به‌شکل یک فایل خروجی می‌دهد، و نام فایل چیزی مانند این است:       “symbolT.csv” (درست مانند فایل D1). برای یادگیری نحوه‌ی کارکرد اکسپرت، کامنت‌ها را بخوانید.

توجه: تمام فایل‌هایی که این اکسپرت می‌سازد، در مسیر “MetaTrader 4/tester/files” هستند.

//+------------------------------------------------------------------+
//|                                                    symbol-D1.mq4 |
//|                                                    A Sexy Trader |
//|                                  http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
#include <stdlib.mqh>
 
extern string StartDate = "2007.03.17"   //set the starting date to get the same time data
             ,StopDate  = "2007.06.28";  //set the testing time limit to protect the over 
                                         //data recording process
 
extern bool For_OP_SELL = true;/*This is to guide for the right type of data to be collected
                                 ->if For_OP_SELL = true 
                                   the daily Open will be collected as an order open price
                                 ->if For_OP_SELL = false , means for OP_BUY,
                                   the daily Open+SPREAD will be collected instate.
                               */
                               
string name,tname;
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
 
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//-------------------------------------------------------------------+
//| Some Essential Parameters In This EA                             |
//-------------------------------------------------------------------+
   int day                   // a variable to mark that today Open has been collected 
      ,ho                    // handle of the file recording the Open price 
      ,ht                    // handle of the file recording the tick price
      ,x=1                   /* the order of D1 file increasing every time D1 data is 
                             equal to 4086 characters and generate the new recording file*/
      ,xt=1                  // same as x but for tick data
      ,bartime               // a variable to mark that current bar's Open has been collected 
      ; 
   double ot                 // latest Open Time
         ,op                 // latest Open price
         ,lt                 // latest tick time
         ,ltk                // latest tick price
         ;
   string OStr               // the string to collect the daily open
         ,TStr               // the string to collect the tick value
         ;
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {   
    
    /*---------------------Only collect the data in the specific time period.----------------------*/  
    if(TimeToStr(TimeCurrent(),TIME_DATE)>=StartDate && TimeToStr(TimeCurrent(),TIME_DATE)<=StopDate)  
    {
       name=Symbol()+x+"D1.csv";       // setup the name of daily open price file
       tname=Symbol()+xt+"T.csv"       // the name of tick price file
            ;
//----       
      if(day!=TimeDay(Time[0]))        // the new day has come
       {
         ot=Time[0];                   // get the new bar time
         if(For_OP_SELL)op=Open[0];    // get the new order open price for SELL Symbol 
                                                 
         else           op=Open[0]+MarketInfo(Symbol(),MODE_SPREAD)*Point;
                                       // else if collecting for the BUY Symbol
         
 
         OStr=OStr+TimeToStr(Time[0],TIME_DATE)+",";//collect the new time data separate each data with ","
         OStr=OStr+DoubleToStr(op,Digits)+",";      //now the new open price
       
       //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~now it's time to export as a file 
       
       ho=FileOpen(name ,FILE_CSV|FILE_WRITE);  // open a file to record the daily open
       if(ho>0)                                 // if the file Symbol()+x+"D1.csv" exist
       {
        FileWrite(ho,OStr);                     // write the collected data
        FileClose(ho);                          // close the file every time your file process done
        if(StringLen(OStr)==4086){x++;OStr="";} /* if the data contains 4086 characters set new x to*/
       }                                        /*   create the new file and prepare the new string for 
                                                   the new file 
                                                */            
        
       Print(TimeToStr(Time[0],TIME_DATE));     // print the collected day time 
       
       int thex=FileOpen(Symbol()+"x.csv",FILE_CSV|FILE_WRITE);
                                                // create a file to record "how many x?" for D1 file for further usage
       if(thex>0)                               // if the file exist
       {
        string xs=DoubleToStr(x,0);             // transform x into string 
        FileWrite(thex,xs);                     // write the x value
        FileClose(thex);                        // close file (every time you finish)
       } 
        
        
        day=TimeDay(Time[0]);                   // now mark today as collected 
       }
 
//--------------------------------FOR TICK VALUE
       /*Because of the decision of collecting the tick data hourly*/
       if(bartime!=Time[0])                     // the new hourly bar has come
                                                // and to make it more flexible when you decided 
                                                      // to collect data in another time frame
       {
         lt=TimeCurrent();                      // get the tick time 
         
         if(!For_OP_SELL) ltk=Bid;              // the tick price for sell order 
         else             ltk=Ask;              // in case for buy order
         
         TStr=TStr+TimeToStr(lt,TIME_DATE|TIME_MINUTES)+",";
                                                // insert the data into the collected string 
         TStr=TStr+DoubleToStr(ltk,Digits)+","; //
         
       //~~~~~~~~                               // now export the data 
       ht=FileOpen(tname,FILE_CSV|FILE_WRITE);  // open a file to record the tick value
       if(ht>0)                                 // if the file Symbol+xt+"T.csv" exist
       {
        FileWrite(ht,TStr);                     // write the collected tick with time 
        FileClose(ht);                          // finish.
        if(StringLen(TStr)==4080){xt++;TStr="";}// prepare for new file if this file reached 4080 character
       }
       
       int thext=FileOpen(Symbol()+"xt.csv",FILE_CSV|FILE_WRITE);
                                                // record the xt value .. same as D1 file 
                                                
       if(thext>0)                              // if the file exist
       {
        string xts=DoubleToStr(xt,0);           // transform into string 
        FileWrite(thext,xts);                   // write xt
        FileClose(thext);                       // done
       } 
     bartime=Time[0];                           // mark as current hourly bar Open price has been collected
     }  
   }    
   else if(TimeToStr(TimeCurrent(),TIME_DATE)>StopDate)  // if out of specified period  
   Print("Done.");                                       // let us know it all done.
//----
   return(0);
  }
//+------------------------------------------------------------------+

گام ۲. ایجاد معاملهی مجازی

این یکی از جذاب‌ترین مراحل است. در اینجا می‌خواهیم اکسپرت هج را توسط استراتژی‌تستر، به‌نوعی تست‌پذیر کنیم. اسکریپت پایین را نگاه کنید تا ببینید این کار چگونه انجام می‌شود. و خواندن کامنت‌ها را نیز فراموش نکنید. و درست همانند اکسپرت اول، فایل نتایج در مسیر “MetaTrader 4/tester/files” ذخیره خواهد شد.

//|                                                 VirtualHedge.mq4 |
//|                                                    A Sexy Trader |
//|                                  http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
#include <stdlib.mqh>
 
extern string  StartDate         = "2007.03.17";    // specify the testing period the same as collected data
extern string  StopDate          = "2007.06.27";
extern string  BaseSymbol        = "GBPJPY";        // the base symbol          
extern string  HedgeSymbol       = "EURJPY";        // the hedge symbol
 
extern int     Base_OP           = OP_SELL;         // by the rules always sell GBPJPY
extern int     Hedge_OP          = OP_BUY;          // and always buy EURJPY
 
extern double  BaseLotSize       = 1.0;             // take the lot size of 1 for GBPJPY
extern double  HedgeLotSize      = 2.0;             // and take 2 lots for EURJPY
 
extern double  ExpectProfit$     = 100;             // always close when reach $100
 
extern bool    PrintDetails      = true;            // print the order opening and closing detail ?
 
int BSP                                      // the spread of GBPJPY 
   ,HSP                                      // the spread of EURJPY
   ,BOP=-1                                   // OrderType of GBPJPY
   ,HOP=-1                                   // OrderType of EURJPY
   ,day=0                                    // for marking that today hedge was executed
   ,hr                                       // handle of the file for exporting the hedge result
   ,p=1
   ,BC                                       // the base-symbol's contract size
   ,HC                                       // the hedge-symbol's contract size
   ,floating=0                               // for marking as there are some orders currently floating
   ,Pointer=0                                // the Pointer of each string data
   ,AL                                       // Account Leverage
   ,on                                       // order number
   ;
double BOpen                                 // open price of base symbol
      ,HOpen                                 // open price of hedge symbol
      ,BLots                                 // base symbol lotsize
      ,HLots                                 // base symbol lotsize
      ,lastTick                              // to mark the last tick for calculating the current profit
      ,BPF                                   // base symbol order profit
      ,HPF                                   // hedge symbol order profit
      ,TPF                                   // the total profit
      ,CurBalance                            // the array to collect the hedge results
      ,CurB=0                                // the current balance
      ,BTick                                 // Base tick
      ,HTick                                 // Hedge tick
      ,BD1Time                               // Base daily open time 
      ,HD1Time                               // Hedge daily open time 
      ,BTTime                                // Base tick time 
      ,HTTime                                // Hedge tick time 
;
 
string CurTrade                              // a string to show the current performance as a comment 
      ,BORD                                  // a string to specify the type of arrows and text of base symbol
      ,HORD                                  // same thing but for hedge symbol                   
      ,hobstr                                // the whole string of the base symbol open price data
      ,bstr                                  // the string of all daily data of base Symbol 
      ,hstr                                  // the string of all daily data of hedge Symbol
      ,btstr                                 // the string of tick data of base Symbol
      ,htstr                                 // the string of tick data of hedge Symbol
      ,pstr                                  // the string for exporting result data
      ;                   
 
color SELLCL=DeepSkyBlue                     // the color of arrows to mark that sell order was executed
     ,BUYCL=HotPink                          // the color of arrows to mark that buy order was executed
     ,BCL                                    // the color of base symbol, varies by the Base_OP parameter
     ,HCL                                    // the color of hedge symbol, varies by the Hedge_OP parameter
     ;
     
bool closed=true                             // for marking as all trades were closed
     ,trimb=true                             // get rid of taken data of base Symbol or not?
     ,trimh=true                             // get rid of taken data of hedge Symbol or not?
     ,trimbd1=true                           // get rid of taken daily data of base Symbol or not?
     ,trimhd1=true                           // get rid of taken daily data of hedge Symbol or not?
     ;     
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//----
   CurBalance=AccountBalance();                      //mark the initial deposit
   CurB=AccountBalance();                            //mark the current balance
   pstr=pstr+DoubleToStr(CurBalance,2)+",";          //collect the performance string
   
   AL=AccountLeverage();                             //get the Account Leverage
   
   BSP=MarketInfo(BaseSymbol  ,MODE_SPREAD);         //get the spread
   HSP=MarketInfo(HedgeSymbol ,MODE_SPREAD);
   
   BC =MarketInfo(BaseSymbol  ,MODE_LOTSIZE);        //get the contract size for calculating profit
   HC =MarketInfo(HedgeSymbol ,MODE_LOTSIZE);
   
   BOP=Base_OP;                                      //get the OrderType 
   HOP=Hedge_OP;
   
   BLots=BaseLotSize;                                //get the lot size
   HLots=HedgeLotSize;
   
   string RName=BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv";    
                                                     //name the performance file
                                                     //this file is needed to copy to 
                                                     /* Program files/MetaTrader 4 /Experts/files */
   
   hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);         //open the file to export the initial deposit
 
   if(hr>0)                                          //if the file exist
   {
    FileWrite(hr,pstr);                              //export the initial deposit
    FileClose(hr);                                   //close file
   }
   
   if(Base_OP==OP_SELL){BCL=SELLCL;BORD="sell";}     //specify the parameter leading to create 
   else                {BCL=BUYCL; BORD="buy";}      // the trading arrow and text
   if(Hedge_OP==OP_BUY){HCL=BUYCL; HORD="buy";}
   else                {HCL=SELLCL;HORD="sell";}
   
   getdata(BaseSymbol);                              //get all data of BaseSymbol
   getdata(HedgeSymbol);                             //all data of HedgeSymbol
                                                     //the function located at the bottom 
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
string RName=BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv"; //name the performance file
 
//--------------------------Only perform the show of specified period--------------------------// 
if(TimeToStr(TimeCurrent(),TIME_DATE)>=StartDate && TimeToStr(TimeCurrent(),TIME_DATE)<=StopDate)
                                                                               //mark as no any order now
{
 if(day!=TimeDay(Time[0]))                                  //the new day has come
 {
  
  {
   if(BOpen!=0 && HOpen!=0)                                 //check if yesterday hedge still floating
   {
 
    if(Base_OP==OP_BUY)                                     //the base-symbol's current profit
     {
      BPF=((BTick-BOpen)*BLots*BC)/BOpen;                   
     }
    else
    {
     BPF=((BOpen-BTick)*BLots*BC)/BOpen;
    }
    
    if(Hedge_OP==OP_BUY)                                    //the hedge-symbol's current profit
    {  
     HPF=((HTick-HOpen)*HLots*HC)/HOpen;
    }
    else
    {
     HPF=((HOpen-HTick)*HLots*HC)/HOpen;
    }
    
    TPF=BPF+HPF;                                            //the total current profit
    
    CurB+=TPF;
    
    CurBalance=CurB;                                        //get the latest AccountBalance
    
    pstr=pstr+DoubleToStr(CurBalance,2)+",";                //insert into the performance string
    
    floating=0;                                             //mark as no any order now
 
    BOpen=0;                                                // block the profit calculation process
    HOpen=0;
    
    if(BOpen==0 && HOpen==0)                                //make sure there is no any order now
    {
     closed=true;                                           //mark all orders were closed
     
     CreateObject("R : "+on,OBJ_TEXT,Time[0],Close[0],0,0,DarkViolet,"","Cleared With Profit Of : "+DoubleToStr(TPF,2));
                                                            //create the cleared all orders text
     
     if(PrintDetails)Print("Cleared Hedge With Profit : "+DoubleToStr(TPF,2));
                                                            //print latest action if PrintDetails allowed
                                                            
      hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);             //open the result file to prepare for file writing
      if(hr>0)
      { 
       FileWrite(hr,pstr);                                  //export the data
       FileClose(hr);
      }
      if(StringLen(pstr)>4086){p++;pstr="";}                //set the new file name if pstr is larger than 4086 characters
    
                                                            
                                                            
      int thep=FileOpen("p.csv",FILE_CSV|FILE_WRITE);       //this file is needed to copy to 
                                                            /* Program files/MetaTrader 4 /Experts/files */  
                                                            //too
                                                            
                                                            // record the p value as a file 
                                                
       if(thep>0)                                           // if the file exist
       {
        string ps=DoubleToStr(p,0);                         // transform into string 
        FileWrite(thep,ps);                                 // write p
        FileClose(thep);                                    // done
       } 
    
    }  
   }   
   
   if(floating==0)                                          //now all yesterday hedge were cleared 
   {
    trimb=true; trimh=true;                                 //allow getting tick data for today to work
       
            
    //----------GETTING TODAY ORDER OPEN PRICE AND TIME----------//  
         if(trimbd1)                                              //getting the daily base price allowed
         {
             Pointer=StringFind(bstr,",",0);                      //find the nearest "," from the beginning of string
             BD1Time=StrToTime(StringSubstr(bstr,0,Pointer));     //get the time value located before the "," syntax
             bstr=StringSubstr(bstr,Pointer+1,0);                 //trim off the taken data
             
             Pointer=StringFind(bstr,",",0);                      //find the nearest "," from the trimmed data string
             BOpen=StrToDouble(StringSubstr(bstr,0,Pointer));     //get the PRICE value located before the "," syntax
             bstr=StringSubstr(bstr,Pointer+1,0);                 //trim off the taken data again to prepare for next time 
         }
         if(trimhd1)                                              //getting the daily hedge price allowed
         {
             Pointer=StringFind(hstr,",",0);                      //all processes are the same as bstr above
             HD1Time=StrToTime(StringSubstr(hstr,0,Pointer));     //...
             hstr=StringSubstr(hstr,Pointer+1,0);                 //...   
             
             Pointer=StringFind(hstr,",",0);                      //...
             HOpen=StrToDouble(StringSubstr(hstr,0,Pointer));     //...
             hstr=StringSubstr(hstr,Pointer+1,0);                 //...
         }
    //--------GETTING TODAY ORDER OPEN PRICE AND TIME ENDED--------//  
 
       
       if(BOpen!=0 && HOpen!=0 && CurBalance>(BLots+HLots)*BC/AL)     
               //make sure the data taken is not zero and margin balance still available    
       {
       floating=1;            //mark as hedge sent
       
       closed=false;          //mark as all orders opened
       
       on++;                  //new hedge orders have opened 
        
        if(PrintDetails)      //if PrintDetails allowed
        {
         Print(on+" Opened : "+BaseSymbol+" "+DoubleToStr(BLots,2)+" lots @ "+DoubleToStr(BOpen,Digits)+".");
         Print(on+" Opened : "+HedgeSymbol+" "+DoubleToStr(HLots,2)+" lots @ "+DoubleToStr(HOpen,Digits)+".");
        }
       }
       else                                                 //in case can not send hedge
       {
        Comment("Can Not Open The Trade : No Margin Available");
       }                                                    //let us know 
       
       
       if(closed==false)                                    //hedge sent
       {
                                                            //create the buy and sell arrow and text
                                                            //this function is located at the bottom of the EA
       CreateObject("B : "+on,OBJ_ARROW,Time[0],Open[0]-20*Point,0,0,BCL,BORD,"");
 
       CreateObject("H : "+on,OBJ_ARROW,Time[0],Open[0]+30*Point,0,0,HCL,HORD,"");
       }
   }   
  } 
  
  day=TimeDay(Time[0]);                                     //mark as proceeded
 }  
 
//-------------------------For Each Tick----------------------------// 
 
if(lastTick!=Hour())                 //the new hour has come
{                                                    
  
   if(trimb && StringFind(btstr,",",0)>0)                         //getting base tick allowed
   {
             Pointer=StringFind(btstr,",",0);                     //same process as getting daily data above
             BTTime=StrToTime(StringSubstr(btstr,0,Pointer));
             btstr=StringSubstr(btstr,Pointer+1,0);
             
             Pointer=StringFind(btstr,",",0);
             BTick=StrToDouble(StringSubstr(btstr,0,Pointer));
             btstr=StringSubstr(btstr,Pointer+1,0);  
   }
   if(trimh && StringFind(htstr,",",0)>0)                         //if getting hedge tick allowed
   {    
             Pointer=StringFind(htstr,",",0);
             HTTime=StrToTime(StringSubstr(htstr,0,Pointer));
             htstr=StringSubstr(htstr,Pointer+1,0);
             
             Pointer=StringFind(htstr,",",0);
             HTick=StrToDouble(StringSubstr(htstr,0,Pointer));
             htstr=StringSubstr(htstr,Pointer+1,0);  
   }
  
  
  
  if(TimeDay(BD1Time)==TimeDay(BTTime) && TimeDay(HD1Time)==TimeDay(HTTime))
                                                            //only if the tick is form the same day
  {    
   trimbd1=true; trimhd1=true;                              //allow to get the next day value
   
   if(TimeHour(BTTime)==TimeHour(HTTime))                   //and same time hour
   {                                             
     
     trimb=true; trimh=true;                                //allow to get the next tick value
 
     if(BOpen!=0 && HOpen!=0)                               //if the calculation process allowed
     {
      
      if(Base_OP==OP_BUY)
       {
        BPF=((BTick-BOpen)*BLots*BC)/BOpen;                 //the base-symbol's current profit
       }
      else
      {
       BPF=((BOpen-BTick)*BLots*BC)/BOpen;
      }
    
      if(Hedge_OP==OP_BUY)
      {  
       HPF=((HTick-HOpen)*HLots*HC)/HOpen;                  //the hedge-symbol's current profit
      }
      else
      {
       HPF=((HOpen-HTick)*HLots*HC)/HOpen;
      }
     
      TPF=BPF+HPF;                                          //the total current profit
      
      CurTrade=DoubleToStr(TPF,2);                          //show the current profit
     
      if(TPF > ExpectProfit$)                               //when they need to close  
      {                                                 
       
       BOpen=0;                                             //set the new value of order open price 
       HOpen=0;                                             //and block the profit calculation process
      
       CurTrade="No Any Hedge Order Now.";                  //Hedge was closed 
       
       
       floating=0;                                          //mark as hedge was closed
       CurB+=TPF;
       
       CurBalance=CurB;                                     //get the last balance equity
       
       pstr=pstr+DoubleToStr(CurBalance,2)+",";             //insert into performance string
       
       CreateObject("R : "+on,OBJ_TEXT,Time[0],Close[0],0,0,YellowGreen,"",
                                 "Close With Profit Of : "+DoubleToStr(TPF,2));
                                                            //create the closed text
                                                            
       if(PrintDetails)                                     //Print the last Close detail
       {                                                    
         Print(on+" Closed "+BaseSymbol+" @ "+DoubleToStr(BTick,Digits));
         Print(on+" Closed "+HedgeSymbol+" @ "+DoubleToStr(HTick,Digits));
         Print(on+" Closed Hedge With Profit : "+DoubleToStr(TPF,2));  
       }                                                   
       hr =FileOpen(RName ,FILE_CSV|FILE_WRITE);    //open it again to prepair for file writing
       
       if(hr>0)
       { 
        FileWrite(hr,pstr);                         //export the data
        FileClose(hr);
       }
      
      if(StringLen(pstr)>4086){p++;pstr="";}        //set the new file name if pstr is larger than 4086 characters
       
      
       thep=FileOpen("p.csv",FILE_CSV|FILE_WRITE);
                                                    // record the p value again
                                                
       if(thep>0)                                   // if the file exist
       {
        ps=DoubleToStr(p,0);                        // transform into string 
        FileWrite(thep,ps);                         // write p
        FileClose(thep);                            // done
       } 
      
      }
     }
     
   }
   else                                             //in case the data values are not from the same tick time  
   {
     if(BTTime>HTTime){trimb=false;}                //freeze the offside data to can not be trim off
     
     else             {trimh=false;}
   }
  }
  else                                              //in case tick data is offside from today
  {
     if(BTTime>BD1Time){trimb=false;}               //freeze the offside data to can not be trim off
     else
     if(BTTime<BD1Time){trimbd1=false;}
      
     if(HTTime>HD1Time){trimh=false;}
     else 
     if(HTTime<HD1Time){trimhd1=false;}
  } 
}
 
 
 
lastTick=Hour();                                     //mark as latest tick proceeded
}   
   
   
   Comment("\nBOpen : "+DoubleToStr(BOpen,Digits)    //show the current situation
          ,"\nHOpen : "+DoubleToStr(HOpen,Digits) 
          ,"\nBOT : "+TimeToStr(BD1Time,TIME_DATE)
          ,"\nHOT : "+TimeToStr(HD1Time,TIME_DATE)
          ,"\nBTick : "+DoubleToStr(BTick,Digits)
          ,"\nHTick : "+DoubleToStr(HTick,Digits)
          ,"\nBTT : "+TimeToStr(BTTime,TIME_DATE|TIME_MINUTES)
          ,"\nHTT : "+TimeToStr(HTTime,TIME_DATE|TIME_MINUTES)
          ,"\nfloating : "+floating
          ,"\nclosed : "+closed
          ,"\ntrimb : "+trimb
          ,"\ntrimh : "+trimh
          ,"\n"
          ,"\nCurOrderNo. : "+on
          ,"\nCurProfit : "+CurTrade
          ,"\nCurBalance : "+DoubleToStr(CurBalance,2)
          );
//----
   return(0);                                               //ALL DONE.
  }
//+------------------------------------------------------------------+
//| A Function To Make This Virtual Tester Looks Like The Real One   |
//+------------------------------------------------------------------+
void CreateObject(string name,int type, int time1, double price1, int time2, 
                             double price2, color cl,string ordtype,string txt)
{
    
    if(type==OBJ_TREND)
    {
    ObjectCreate(name,type,0,time1,price1,time2,price2);
    ObjectSet(name,OBJPROP_COLOR,HotPink);
    }
    
    if(type==OBJ_ARROW)
    {
    ObjectCreate(name,type,0,time1,price1);
    ObjectSet(name,OBJPROP_COLOR,cl);
     if(ordtype=="sell")ObjectSet(name,OBJPROP_ARROWCODE,221);
     else              ObjectSet(name,OBJPROP_ARROWCODE,222);
    }
 
    if(type==OBJ_TEXT)
    {
     ObjectCreate(name,type,0,time1,price1);
     ObjectSetText(name,txt,8,"Comic Sans MS",cl);
    }
}
//+------------------------------------------------------------------+
//| GETTING ALL DATA FROM FILES FUNCTION                             |
//+------------------------------------------------------------------+
void getdata(string sym)
{
Comment("Collecting Data.","\n\nPlease Wait........");  //let us know that getting data still in process
      
      int x =FileOpen(sym+"x.csv",FILE_CSV|FILE_READ)   //get how many files of D1 and tick
         ,xt=FileOpen(sym+"xt.csv",FILE_CSV|FILE_READ)
         ,pter=0,s=0,v=0
         ,lastME=0,t=0
         ;
      double ME,U;
      string str,str2;
      int xa=StrToInteger(FileReadString(x))
         ,xta=StrToInteger(FileReadString(xt))
         ,xtc=1
         ;
      FileClose(x);
      FileClose(xt);   
   
      if(xta>xa)xtc=xta;                                     //make it run only in one for loop
      else      xtc=xa;
      
      pter=0;s=0;
      for(int i=1;i<=xtc;i++)                                //the loop to get all string data
      {
       string name=sym+i+"T.csv"                             //set the data file name
             ,d1  =sym+i+"D1.csv"
             ;
       int h=FileOpen(name,FILE_CSV|FILE_READ)               //open all files to read
          ,d=FileOpen(d1  ,FILE_CSV|FILE_READ);
       
       //------------------------------------------------------------
       string source=FileReadString(h);                      //read the tick string
       FileClose(h);
       
        if(sym==BaseSymbol)                                  //if get the data of base symbol 
        {
         btstr=btstr+source;
        }
        else                                                 //if hedge symbol 
        {
         htstr=htstr+source;
        }
 
       //------------------------------------------------------------
       if(d>0)                                               //read the daily data 
       {
         string d1s  =FileReadString(d);FileClose(d);
         
         if(sym==BaseSymbol)                                 //if get base daily data
         {
          bstr=bstr+d1s;
         }
         else                                                //if get hedge data
         {
          hstr=hstr+d1s;
         }
       }
      }
}
//------------------------------------ALL DONE---------------------------------------------//

گام ۳. بازبینی نتیجه

بعد از اینکه معاملات مجازی انجام و نتایج هج ثبت شدند، می‌توانیم آن داده‌ها را برداشته تا مفهوم هجینگ‌مان را نشان دهیم. برای انجام این کار، تصمیم گرفتم تمام داده‌های ثبت‌شده را به‌عنوان یک اندیکاتور برای ترسیم منحنی عملکرد در یک پنجره‌ی جدا، همانند بسیاری از اندیکاتورها مثل CCI، RSI، و ‌ATR و غیره، خروجی بگیرم. تمام فایل‌های اکسپرت دوم نیز باید در مسیر “MetaTrader 4/experts/files” کپی شوند.

برای اتمام این منحنی، اندیکاتور زیر نیاز است.

//+------------------------------------------------------------------+
//|                                                  performance.mq4 |
//|                                                    A Sexy Trader |
//|                                         http://pipsmaker.wordpress.com/ |
//+------------------------------------------------------------------+
#property copyright "A Sexy Trader"
#property link      "http://pipsmaker.wordpress.com/"
 
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_color1 Goldenrod
//---- input parameters
extern string    BaseSymbol="GBPJPY";
extern string    HedgeSymbol="EURJPY";
//---- buffers
double ExtMapBuffer1[]                 //this is the indicator buffer
      ,curve[8888888]                  //this array is for collecting the result from the performance file
      ;
 
int handle;                            //maybe no need to explain anymore
string data;
int len=0
   ,i=0
   ,j=0
   ,p
   ,pv
   ,pter=0
   ;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,ExtMapBuffer1);
   
   IndicatorShortName(BaseSymbol+"~"+HedgeSymbol+" ");
//----
   
   p =FileOpen("p.csv",FILE_CSV|FILE_READ);                       //get how many result files were exported
   pv=StrToInteger(FileReadString(p));
   FileClose(p);
   
   for(int i=1;i<=pv;i++)                                         //the loop to get all exported result as a string
   {
      string name = BaseSymbol+"_"+HedgeSymbol+"_result"+p+".csv";//get the name of the performance file
      handle=FileOpen(name,FILE_CSV|FILE_READ);                   //search for the file to open to read
      if(handle>0)                                                //if the file is exist
      {   
          data=data+FileReadString(handle);                       //get the whole data
          FileClose(handle);                                      //close it
      }
   
   }
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int    counted_bars=IndicatorCounted(),i=0,s=-1;
   
//----
   len=StringLen(data);                              //get the lenght of the data string
  
   pter=0;                                           //set the pointer which use for searching the ","
                                                     /*please be informed that the file csv was collected 
                                                     the result as a string like this 
                                                     ۱۰۰۰.۵۴,۱۱۰۰.۵۴,۱۲۰۰.۵۴,۱۳۰۰.۵۴,۱۴۰۰.۵۴
                                                     but the fact is we want only the real data is like this
                                                     ۱۰۰۰.۵۴
                                                     ۱۱۰۰.۵۴
                                                     ۱۲۰۰.۵۴
                                                     ۱۳۰۰.۵۴
                                                     ۱۴۰۰.۵۴
                                                     so the "," is the key to get the data as above
                                                     and it can be done by finding out the data befor ","
                                                     and get rid off it from the whole data 
                                                     to shift the next data into the front
                                                     and insert each taken off data into the curve array 
                                                     */
   
   for(i=len;i>=0;i--)                               /*the loop to define how many room this array should build
   {                                                   to contain the performance data
                                                     */
    if(StringFind(data,",",0)>0)                     //if there is a nearest "," from the front
    {
     s++;                                            //indicate the room number  
     pter=StringFind(data,",",0);                    //get the point where the first "," exist
     curve[s]=StrToDouble(StringSubstr(data,0,pter));//insert the first data of the whole string
     data=StringSubstr(data,pter+1,0);               //cut the inserted data off
    }                           
    else          break;                             //no data to count anymore , break the loop
 
   ArrayResize(curve,s+1);                           //resize the curve array for furthur usage
//----
   for(i=0,j=s;i<=s;i++,j--)                         //the plotting process bigin
   {
    if(curve[j]>0)ExtMapBuffer1[i]=curve[j];         //plot the performance curve
   }
//----                                               //all the things done.
 
   return(0);
  }
//+------------------------------------------------------------------+

چگونه از آنها استفاده کنیم

قبل از اینکه یک کپی از کد من را دانلود کنید، بیایید کوتاه به “چگونگی استفاده از آن‌ها” بپردازیم و به آن به‌‌چشم یک مینی‌دستتورالعمل نگاه کنید.

برای اینکه انتظارات‌مان را واقعاً برآورده کنیم، ۵ قدم ساده باید برداریم که نمی‌شود از آن‌ها صرف‌نظر کرد.

  1. در تستر (نیازی به حالت ویژوال نیست)، در منوی “Expert Advisor:”، symbol-D1.mq4 را انتخاب کنید، و در “Symbol:”، اولین نماد هجِ جفت‌اَرز هج مورد علاقه‌ی خود را انتخاب کنید، دوره‌ی تاریخ و زمان را تعیین کنید و اگر این نماد جهت معامله‌ی Sell است، مقدار “For_OP_SELL” را true تعیین کنید، و اگر برای معامله‌ی Buy است، آن را false تعیین کنید، و برای منوی “Period:”، دوره‌ی hourly (ساعتی) را انتخاب کرده و “Start” را بزنید تا فرآیند ثبت، شروع شود.
  2. برای نماد دوم هج، همان مراحل شماره‌ی ۱ را انجام دهید، ***فراموش نکنید که پارامتر “For_OP_SELL” را تغییر دهید*** تا متناسب با نوع معامله‌ی این نماد باشد.
  3. mq4 را انتخاب کنید، تمام متغیرها را تعیین کنید، و نماد مورد نظر برای تست را انتخاب کنید (هر نمادی که دوست دارید). اما این کار نیاز به حالت ویژوال دارد تا بتوان عملکرد هج را دید.
  4. تمام فایل‌های مربوط به نشان دادن کارایی هج را از “program files/metatrader 4/tester/files” به “program files/metatrader 4/experts/files” کپی کنید (csv و p.csv. اگر بیش از یک فایل نتیجه داشتید، می‌بایستی همه را کپی کنید).
  5. mq4 را به هر نموداری که در حال حاضر فعال است، متصل کنید تا عملکرد هج را در حالت تقریباً واقعی ببینید.

و این منحنی عملکرد، حاصل قوانین تجربی من است.

عبور از محدودیت‌های استراتژی‌تست
عبور از محدودیت‌های استراتژی‌تستر در تست اکسپرت هج

اوه! خیلی زشت است اما مطمئناً منحنی شما بهتر خواهد بود.

نتیجه‌گیری

بسیار خوشحالم که اکنون قدم به دنیای جدید تست اکسپرت‌های هج گذاشته‌ایم. دیگر مشکل محدودیت نداریم. اما به‌یاد داشته باشید که مفهوم هجینگ که در این مقاله مطرح شد، فقط مثال است و برای کوتاه کردن زمان تست گرفتن ایجاد شده‌است. اگر می‌خواهید تستر مجازی با استراتژی هج شما کار کند، می‌بایستی داده‌های خاص خود، مانند بازشدن و بسته‌شدن هر روز، high، low، یا هر نوع دیگری را لیست کنید. و اگر می‌خواهید با همبستگی ترید کنید، تمام مقادیر همبستگی برای هر زمان خاصی باید خروجی گرفته شوند. با این لیست شما می‌دانید که چه داده‌ای باید ثبت شود، چه داده‌ای باید محاسبه شود، و چه داده‌ای را باید به‌عنوان نتیجه، خروجی گرفت. و برای کوتاه‌تر شدن زمان پردازش داده‌های خود، پیشنهاد می‌کنم دوره‌ی تست خود را چند قسمت کنید – که بهتر از این است که همه را در یک زمان پردازش کنید. برای مثال اگر می‌خواهید اکسپرت خود را برای ۱ سال تست کنید، می‌توانید ۴ بخش سه ماهه درنظر بگیرید. امیدوارم منحنی عملکرد شما شکلی دلفریب داشته باشد و امیدوارم این مقاله به شما معامله‌گران هج کمک کند، یا حداقل بخشی از این مقاله برای شما مفید بوده باشد و یا از این مقاله الهام گرفته و نتایج عالی کسب کنید.

این مقاله دارای فایل پیوست است.

فایل پیوست را از اینجا میتوانید دانلود کنید . 

 

مقالات پیشنهادی :

M23admin

→ خواندن مطلب قبلی

چگونه معیارهای بهینه‌سازی خود را پیاده‌سازی کنیم؟

خواندن مطلب بعدی ←

مــارتینگل چـــیست؟

یک مورد نظر

نوشتن نظر شما

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *