تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

مقدمه

در مقاله‌ی قبلی (یعنیTransferring an Indicator Code into an Expert Advisor Code. Indicator Structure) ساختار کلی یک اندیکاتور و کدی که برای تبدیل شدن به کد اکسپرت مدنظر است را بررسی کردیم، و ایده‌های اصلی در تغییرات ابتدایی کد اندیکاتور را توضیح دادیم. اکنون می‌خواهیم کد بدست‌آمده را به یک تابع سفارشی تبدیل کنیم، چراکه این راه شاید مناسب‌ترین راه برای ارائه‌ی یک کد اندیکاتوری در یک کد اکسپرتی باشد. تابع سفارشی را در قالب فایل mqh ارائه می‌دهیم و عرضه‌ی آن در اکسپرت با استفاده از دستور #include، جای بسیار کمی خواهد گرفت و فراخوانی این تابع نسبت به یک اندیکاتور سفارشی خیلی سخت نیست. آنچه اهمیت دارد این است که چنین توابع سفارشی‌ایی را می‌توان بعداً به‌شکل جهانی مورد استفاده قرار داد زیرا برای هر اکسپرتی مناسب هستند.

قبل از نوشتن این تابع، بیایید این موضوع را تحلیل کنیم که چگونه این تابع، صرف‌نظر از ساختار داخلی‌اَش، با دیگر بخش‌های اکسپرت تعامل می‌کند.

ساختار اکسپرت با فراخوانی اندیکاتور سفارشی

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

در این طرح، ما روی هر تیک (tick) از بافر صفر اندیکاتور سفارشیIndicatorPlan.mq4 ، مقادیر شمارش‌شده‌ را در دو فراخوان، می‌گیریم، و آنها را در آرایه‌های مرسوم Ind_Buffer1[] و Ind_Buffer2[] جای می‌دهیم. طرح فراخوانی اندیکاتور اینگونه طراحی‌ شده‌‌است که به‌منظور محاسبات بیشتر فقط به مقادیر پنج اندیکاتور آخر، به‌استثنای اندیکاتور صفر، نیاز داریم.

ساختار اکسپرت با فراخوان تابع سفارشی

درحالیکه ما مشغول توسعه‌ی یک تابع اندیکاتور جهانی هستیم، که برای هر اکسپرتی مناسب است، [این تابع] باید مقادیر دریافت‌شده را به بافر اندیکاتور/اندیکاتورهای مشابه بفرستد، که [در آنجا] مقادیر اندیکاتور برای تمام کندل‌ها در نمودار، ذخیره می‌شوند. البته، می‌توانیم تابع اندیکاتوری را توسعه دهیم که فراخواندن آن کاملاً مشابه با فراخواندن یک اندیکاتور سفارشی باشد، اما نوشتن چنین چیزی علاوه بر طولانی بودن کد، بسیار زمان‌بر هم خواهد بود.

می‌شود این کار را ساده‌تر انجام داد. این تابع باید پارامترهای ورودی را، [به‌صورت] پارامترهای یک اندیکاتور سفارشی و بافر دریافت کند، و باید همان بافر را با حالت شبیه‌سازی اندیکاتور بازگرداند – آن‌هم در نقطه‌ای که سلول‌ها (خانه‌ها) با مقادیرِ اندیکاتورِ محاسبه‌شده، پُر شده‌اند. این کار را می‌توان به‌سادگی با آوردن یک متغیر اکسترنال مرتبط-با-مرجع (linked-by-reference) برای تابع مربوط به بافر اندیکاتور، انجام داد. در زبان MQL4، این مورد اینطور دیده خواهد شد: double& InputBuffer. تابع این اندیکاتور باید به‌عنوان یک تابع منطقی ثبت شود، که اگر محاسبات موفقیت‌آمیز بودند، به “صحیح” (true)، و اگر محاسبات، به‌دلیل عدم‌ وجودِ تعداد مناسبی از کندل‌ها روی نمودار، موفقیت‌آمیز نبودند، به “غلط” (false) بازگردد. بعد از همه‌ی این توضیحات، تابع اندیکاتور (که از روی طرح اندیکاتور ساخته شده و در مقاله‌ی قبلی به آن پرداخته شد)، قرار است چنین فرمی داشته باشد:

این تابع اندیکاتور یک متغیر اکسترنال اضافی دیگر به‌نام Number دارد که مقدار شماره‌ی فراخوان این تابع اندیکاتور را می‌پذیرد.

طبیعی است که بغیر از بافر اندیکاتور InputBuffer0، متغیرهای اکسترنال همچنین شامل بافرهایی برای محاسبات واسطه‌ای InputBuffer1 و InputBuffer2 باشند، زیرا ساختن این بافرها درون تابع بسیار مشکل‌ساز است. بهتر است حالت اندیکاتور عملکرد این بافرها را درون تابع، شبیه‌سازی کنیم. این کار مشکلی پیش نخواهد آورد. اکنون بیایید به معنی متغیر اکسترنال NullBarRecount بپردازیم. درواقع اکثر اکسپرت‌ها نیاز به محاسبات، روی کندل صفر، ندارند، و درحالیکه ما مشغول نوشتن کد این تابع اندیکاتور جهانی هستیم، به‌صورت طبیعی مقادیر اندیکاتور روی کندل صفر محاسبه‌ی مجدد خواهند شد، که این ممکن است زمان اجرا را به‌طور قابل‌توجهی افزایش دهد. با “false” نشان دادن پارامتر اکسترنال تابع NullBarRecount، در واقع داریم محاسبات تابع را روی کندل صفر به‌نوعی ممنوع می‌کنیم – البته اگر این محاسبات را نیاز نداشته باشیم.

اکنون می‌توانیم طرح ساختار اکسپرت را برای فراخواندن اندیکاتورها ارائه کنیم. در این طرح حالت فراخوانی توابع رعایت شده‌است:

طرح کلی تبدیل یک کد اندیکاتور به یک تابع سفارشی

بعد از این کار مقدماتی، اکنون می‌توانیم به‌سراغ ساختن یک طرح کلی از ساختار داخلی یک تابع اندیکاتور برویم. بیایید مبنا را طرح اندیکاتور مقاله‌ی قبلی قرار دهیم. بعید است کار سختی در پیش باشد:

  • فقط محتوی تابع int start()را بردارید؛
  • اعلام تابع Get_IndSeries()را اضافه کنید:

  • نام‌های بافرهای اندیکاتور داخل کد را (Ind_Buffer) طبق نام‌های بافر (InputBuffer) متغیرهای اکسترنالِ تابع Get_IndSeries() تغییر دهید؛
  • اعلام متغیر LastCountBar را اضافه کنید؛
  • صحیح بودن متغیر NullBarRecount را بررسی کنید:

  • در تمام سیکل‌های محاسباتی اندیکاتور، صفر را به ‌ LastCountBarتغییر دهید؛
  • تغییرات را در همان آغاز کد صورت دهید – وقتی که دارید بررسی می‌کنید آیا عدد کندل‌ها برای محاسبات بیشتر کافی هست یا خیر: return(0) به return(false)؛
  • در آخر کد، return(0) را به return(true) تغییر دهید؛

تابع اندیکاتور آماده است:

به‌نظرم اگر خواننده از MQL4 به‌خوبی استفاده کند، بعد از خواندن کارهایی که در بالا گفته شد، مشکلی در نوشتن توابع اندیکاتور با توجه به طرح داده شده، نخواهد داشت.

مثالی از نوشتن یک تابع اندیکاتور سفارشی

اکنون بیایید یک تابع اندیکاتور بنویسیم. بیایید یک اندیکاتور بی‌نهایت ساده را انتخاب کنیم:

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

تثبیت الگوریتم

  1. از شر تمامی عناصر غیرضروری در کد اندیکاتور، خود را خلاص کنید؛
  2. کد شبیه‌سازی بافر اندیکاتور را برای یک تک بافر[۱] ExtBuffer[] بنویسید؛
  3. تابع IndicatorCounted() را جایگزین متغیر IndCounted کنید؛
  4. متغییرIndCounted  را با مقدار کندل‌های نمودار منهای ۱، شروع کنید؛
  5. کندل‌های متغیر از پیش‌ تعیین‌شده را به فراخوانی iBars (نماد اَرز، تایم‌فریم) سری‌های زمانی، تغییر دهید؛
  6. چک کردن‌های غیرضروری را برای counted_bars حذف کنید:

  1. فقط محتوی تابع int start() را باقی بگذارید؛
  2. اعلام تابع Get_RAVISeries()را اضافه کنید:

  1. نام‌های بافر اندیکاتور داخل کد را (ExtBuffer) عیناً مطابق با نام‌های بافر (InputBuffer) متغیرهای اکسترنالِ تابع Get_RAVISeries() جایگزین کنید؛
  2. اعلام متغیر LastCountBar را اضافه کنید؛
  3. متغیر استاتیکIndCounted  را به یک صف/آرایه IndCounted[Number] تبدیل کرده و یک کد برای تغییرات حجم متغیرها، بسته به تعداد فراخوان‌های تابع mqh، اضافه کنید:

  1. صحیح بودن متغیر NullBarRecount را بررسی کنید:

  1. در تمام سیکل‌های محاسبات اندیکاتور، صفر را به LastCountBar تغییر دهید:

  1. تغییرات را در همان آغاز کد صورت دهید – وقتی که دارید بررسی می‌کنید آیا عدد کندل‌ها برای محاسبات بیشتر کافی هست یا خیر: return(0) به return(false)؛
  2. در آخر return(0) را با return(true) جایگزین کنید.

بعد از تغییر تمامی کدها، تابع اندیکاتور Get_RAVISeries() را خواهیم داشت:

مسلماً کار به‌خوبی پیش رفته است! شاید خیلی آسان نبود… اما یک سوال مطرح است. آیا این تابع اندیکاتور همانند یک تابع سفارشی محاسبات را انجام می‌دهد؟

تست کردن تابع اندیکاتور سفارشی جهت دقت در محاسبات

نیاز داریم بررسی کنیم آیا نتایج محاسبات این تابع برابر با نتایج محاسبات یک اندیکاتور سفارشی است یا خیر. به همین جهت، بهترین و مناسب‌ترین اکسپرت آن است که ترید نمی‌کند و فقط مقادیر را از اندیکاتور سفارشی  RAVI.mq4و تابع سفارشی Get_RAVISeries() دریافت، و تفاوت را پیدا کرده و بعد از آن مقادیر اندیکاتور، مقادیر تابع سفارشی، و اختلاف بین آنها را درون یک فایل گزارش می‌ریزد. تمامی کاری که باید بکنیم، تحلیل محتوای آن فایل گزارش است؛ تا بتوانیم نتیجه‌گیری نهایی در مورد تطبیق الگوریتم‌مان از تابع سفارشی Get_RAVISeries() و اندیکاتور RAVI.mq4 را اتخاذ کنیم:

اکسپرت Get_RAVISeriesTest را در استراتژی‌تستر اجرا کنید. طبیعتاً فایل کامپایل‌شده‌ی  RAVI.ex4باید درون پوشه‌ی  \expert\indicatorsباشد، و نیز فایل Get_RAVISeries.mqh در پوشه‌ی \expert \include از نرم‌افزار متاتریدر، باشد. در ژورنال استراتژی‌تستر و در یک فایل گزارش، دو ستون با مقادیر اندیکاتور و آنالوگ آن را به‌شکل یک تابع می‌بینیم؛ ستون سوم تفاوت این مقادیر را نشان می‌دهد. تمام مقادیر ستون آخر برابر با صفر هستند و این یعنی اینکه این مقادیر در هر دو مدل، کاملاً یکسان هستند. نتیجه اینکه نوشتن تابع سفارشی اندیکاتور با موفقیت انجام شده‌است!

تبدیل کد اندیکاتور به کد اکسپرت. طرح‌های کلی ساختاری یک اکسپرت و توابع اندیکاتور

نتیجه‌گیری

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

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

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

۱) Single Buffer

امینی

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

بهینه‌سازی ربات معاملاتی در ترید واقعی

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

رویـــکــــرد شــــیء‌گرا در MQL

نوشتن نظر شما

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