تبدیل کد اندیکاتور به کد اکسپرت. ساختار اندیکاتور

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

تبدیل کد اندیکاتور به کد اکسپرت. ساختار اندیکاتور

مقدمه

برای درک بهتر، نویسنده پیشنهاد می‌کند این مطالب را بخوانید:

  1. ساختن یک اندیکاتور سفارشی چه ویژگی‌ها و مزایایی دارد http://articles.mql4.com/en/articles/1497
  2. شمارش چندباره‌ی کندل‌های خالی (تشکیل‌نشده) در برخی اندیکاتورها http://articles.mql4.com/en/articles/1411

قبل از اینکه به بحث در مورد موضوع مقاله بپردازیم، این سوال را باید جواب داد:‌ چه نیازی به تبدیل کد اندیکاتور به کد اکسپرت است؟ آن‌هم، در اکثر موارد، یک اکسپرتی که با اندیکاتورهای سفارشی کار می‌کند، خیلی ساده‌تر از نظیر خودش به‌نظر می‌آید [حتی با اینکه] تمام موارد مورد نیاز برای عملکرد اندیکاتورهای سفارشی را درون کد خودش دارد. همچنین، به‌خصوص اگر این واقعیت را لحاظ کنیم که، یک کد، اگر درست نوشته شده باشد، در هر دو حالت، نتیجه کاملاً یکسان خواهد بود! [پس چرا این کار را بکنیم؟].

به‌نظر من، این کار به دو دلیل لازم است:

  1. اگر در محاسبات اکسپرت، مقادیر محاسبه‌شده روی کندل صفر، اصلاً استفاده نشوند، طبیعتاً، ترجیح خواهیم داد که محاسبات مجدد روی کندل‌ صفر و اولین کندل را، حذف کنیم، که البته غیرضروری هم هستند. این کار به ما اجازه می‌دهد، زمان بهینه‌سازی چنین اکسپرتی را یک‌سوم کنیم. و این کار در کدهای بسیار پیچیده و متمرکز بر منبع (resource-intensive)، بسیار ضروری و مناسب است.
  2. دلیل دیگر: استفاده‌ی تجاری از اکسپرت است، بطوریکه این اکسپرت حداکثر محافظت از کدهای خود را در برابر دِکامپایل شدن، دارد.

در مورد ۲، وضعیت کاملاً مشخص و تبدیل کد کاملاً منطقی است. و در مورد ۱، در اکثر مواقع، بسیار راحت‌تر هستیم که کدهای اندیکاتورهای سفارشی را دوباره نوشته و محاسبات غیرضروری را از آنها بیرون بیاوریم! طبیعتاً، چنین اندیکاتورهایی برای اکسپرت مناسب هستند و نه ترید! پس بیایید بحث خود را در مورد راه‌حل برای این مدل مشکل، آغاز کنیم.

مثالی از بهینه‌سازی اندیکاتور

ابتدا می‌خواهم توجه شما را به بخشی از کد این اندیکاتور سفارشی جلب کنم:

در این مورد، این خط، برای ما مناسب است:

تعبیر این بررسی که با کم شدن ۱ واحدی مقدار متغیر ‘counted_bars’ همراه است، این چنین می‌شود: اگر یک اندیکاتور سفارشی، این خط را نداشته باشد، مقادیر اشتباهی از بافر خود به اکسپرت می‌فرستد، آن‌هم زمانی که کندل صفر تغییر می‌کند. انحنای این اندیکاتور در اکسپرت کاملاً فرمی “مچاله” به‌خود می‌گیرد.

در اندیکاتورهای سفارشی، متغیرهای ‘limit’ و ‘counted_bars’ ممکن است نام‌های متفاوتی داشته باشند، اما کد برنامه باید این بررسی‌های کلی را داشته باشد. فکر می‌کنم این توضیح برای پاک‌سازی ادعاهای برخی از اکسپرت‌نویس‌ها کافی باشد که [ادعا می‌کنند] بین داده‌های متاتریدر که از بافر اندیکاتور می‌آیند و نیز داده‌هایی که از یک اندیکاتور سفارشی دریافت می‌شوند، شباهت مطلق نمی‌توان یافت. [درحالیکه] اگر کد اندیکاتور و کد اکسپرت را صحیح نوشته باشیم، فارغ از اینکه چقدر آن کد سخت هست، داده‌ها همیشه باید یکسان باشند!

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

در اینجا مثالی داریم که این روش بهینه‌سازی کد یک اندیکاتور را، برای سریع‌تر عمل کردن آن در اکسپرت، توضیح می‌دهد. در اندیکاتور اصلی، سیکل، صفر را به یک تغییر می‌دهد – بعد از آنکه اندیکاتور محاسبه‌ی مجددِ مقدارِ کندلِ صفر را قطع می‌کند.

در نتیجه، سورس کد ما این شکلی خواهد شد:

تکرار می‌کنم که روش بالا فقط برای اکسپرت‌هایی است که روی کندل‌های کامل‌شده کار می‌کنند. برای مثال، تمام کندل‌ها غیر از کندل صفر!

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

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

و باید توجه داشت که مقادیر سود و ضرر تست‌شده را نباید بعد از تغییر کد اکسپرت، اصلاح کرد!

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

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

طرح کلی ساختار یک اندیکاتور

قبل از شروع کار روی موضوع اصلی مقاله، بیایید ساختار یک اندیکاتور را از دیدگاه یک برنامه‌نویس تحلیل کنیم. این برنامه‌نویس به اندیکاتور به‌عنوان بخشی از کدِ اکسپرت آینده، نگاه می‌کند:

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

حال بیایید چیزهایی را از عناصر طرح حذف کنیم که برای ما در حال ‌حاضر جالب نیستند و در اکسپرت نیز غیرضروری می‌باشند:

این کد را می‌توان به‌سادگی داخل یک کد اکسپرت قرار داد، به‌شرطی که چند خطای کوچک وجود نداشته باشد:

  1. اول از همه اینکه نباید فراموش کنیم تابع IndicatorCounted() در اکسپرت‌ها کار نمی‌کند!
  2. همچنین در بلوک اول، نمی‌توان آرایه‌های سفارشی را به اندیکاتوری تغییر داد!

بنابراین برای صیانت کامل از کد اندیکاتور اول از همه نیاز داریم یک مشابه برای تابع IndicatorCounted() را توسعه دهیم و به‌نوعی مشابه‌های (analogues) بافرهای اندیکاتور را در اکسپرت شبیه‌سازی کنیم. متاسفانه، شبیه‌سازی مستقیم بافرهای اندیکاتور در اکسپرت، با استفاده از توابع استاندارد، غیرممکن است. اکنون نظیر SetIndexBuffer() و  IndicatorBuffers()را برای اکسپرت‌ها نداریم! بنابراین باید این مسئله را با راه‌های دیگری حل کنیم. خوب است که در MQL4 آپشن‌های زیادی موجود داریم.

شبیه‌سازی بافرهای اندیکاتور در اکسپرت

اول بیایید با جزئیات فرآیندهایی که در بافرهای اندیکاتور رخ می‌دهند را ببینیم.

  1. مقادیری که به متغیرهای بافر اندیکاتور نسبت داده شده‌اند، وقتی که اندیکاتور به نمودار متصل است و نرم‌افزار کار می‌کند، بین سیکل‌ها گُم نمی‌شوند.
  2. اگر یک کندل صفر (آخرین کندل) روی نمودار تغییر کند، تمام عناصر بافر اندیکاتور جا‌به‌جا می‌شوند.
  3. اگر یک کندل جدید بیاید، مقدار متغیر limit، از ۱ به ۲ در اندیکاتور تغییر کرده و تمامی عناصر بافر یک واحد جابه‌جا می‌شوند. برای مثال کندل صفر کندل اول می‌شود، اولی، دو می‌شود و الی آخر.
  4. طبیعتاً ابعاد بافر اندیکاتور نیز تغییر می‌کنند. اگر در تیک بعدی، کندل تغییر نکند، تمامی عناصر بافر سرجای خود می‌مانند.

اکنون به شبیه‌سازی بافرهای اندیکاتور می‌رسیم. برای این کار باید از این توابع استاندارد MQL4 استفاده کنیم: ArraySize()، ArrayResize()، و ArraySetAsSeries(). کد شبیه‌سازی بافرهای اندیکاتور کاملاً ساده است و اصول عملکرد آن را می‌توان اینگونه توصیف کرد: وقتی کندل صفر تغییر می‌کند، دستور مستقیم تعریف عناصر، در بافرها، بازسازی می‌شود؛ سلول‌های جدید از کندل‌های جدید در بافرها با استفاده از تابع ArrayResize() اضافه شده، و بعد از آن دستور معکوس تعریف عناصر در بافرها تنظیم می‌شود، و به‌نظر می‌رسد سلول‌های خالی در بافر اندیکاتور شبیه‌سازی‌شده، بین اولی‌ها باشند.

راستی اینکه، این روش شبیه‌سازی بافرهای اندیکاتور را همچنین می‌توان در اندیکاتورها، وقتی که بافرهای هشت اندیکاتور برای محاسبات واسطه کافی نیستند، استفاده کرد. مثال را می‌توانید در فایل‌های پیوست  SMI.mq4و SMI_New.mq4 ببینید.

تبدیل کد اندیکاتور به کد اکسپرت

جایگزینی تابع IndicatorCounted()

حال می‌خواهیم به تحلیل شبیه‌سازی تابع IndicatorCounted() بپردازیم. این تابع مقدار کندل‌های آن نموداری که فعال است و بعد از آخرین فراخوان اندیکاتور تغییر نکرده، را بازمی‌گرداند. به بیان دیگری بگوییم. این تابع مقدار کندل‌های آن نموداری که فعال است و در نرم‌افزار هنگام تیک قبلی، در دسترس بوده را بازمی‌گرداند.

برای اینکه مقادیر کاملاً یکسان باشند، ما باید از مقدار حاصل از کندل‌ها، یک واحد کم کنیم ([منهای ۱ کنیم]). بنابراین این تابع به‌راحتی می‌تواند جایگزین یک متغیر استاتیک عددصحیح شود، که با مقدار یک متغیر از پیش تعیین شده‌ی Bars-1، پس از دریافت مقدار[۱] از آن، شروع می‌شود. بعد از آن، طرح اندیکاتور این چنین خواهد بود:

تبدیل‌های دیگری از کد اندیکاتور و طرح نهایی ساختار آن

البته، می‌توانیم به‌سادگی کد اندیکاتور را در چند بخش به کد اکسپرت تبدیل کنیم، با فرض اینکه این اندیکاتور را در اکسپرت فقط یک‌بار برای کار روی نمودار فعال، نیاز داریم! اگر اندیکاتور دو بار استفاده شود، می‌شود به‌سادگی در مرتبه‌ی دوم نام‌های تمام متغیرهای اندیکاتور را تغییر داد و کد را یک ‌بارِ دیگر اضافه کرد. اما در این ‌صورت، اکسپرت پیچیده‌تر خواهد شد.

وظیفه‌ی پردازش داده‌ها در دیگر تایم‌فریم‌ها به‌راحتی حل شده‌است. متغیرهای از پیش تعیین شده از نوع Bars را جایگزین سری‌های زمانی از این نوع کنید:

Null – ‌برای نماد رشته;, ۰ (در سری‌های زمانی)  –  تایم‌فریم‌ int برای;, Close[bar] – برای

و الی آخر.

اکنون به تحلیل خطوط اندیکاتور می‌پردازیم:

به‌مانند جایگزینی پیشنهادی تابع IndicatorCounted() در ساختار اندیکاتور ما، اندیکاتور counted_bars [هم] هیچ‌گاه کمتر از صفر نخواهد بود. بنابراین، خطوط:

در کد اکسپرت را می‌توان حذف کرد. [کار] با دو خطِ بعدی:

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

اینجا باید یک مطلب را لحاظ کنیم. اندیکاتورهایی وجود دارند که در چند بار محاسبه‌ی مجدد کندل صفر، در ابتدای سیکل‌های محاسباتی، مقادیر برخی از متغیرها را برای بازگرداندن کد به وضعیت اولیه‌اش (مقاله)، روی اولین کندل ذخیره می‌کنند. در اکسپرت پس از حذف کردن دو خط آخر، این ذخیره‌سازی باید روی کندل صفر انجام شود، و نه اولی. معمولاً چنین اندیکاتورهایی در ابتدای سیکل‌های محاسباتی، این قطعه کد را دارند:

این بخش از کد که در بالا مشاهده می‌کنید را برای ذخیره‌سازی مقادیر متغیرها، باید دستکاری کرد، تا ذخیره‌سازی به‌جای کندل اول، روی کندل صفر انجام شود. تمام “۱”ها را باید “۰” کرد و “۲”ها را “۱”. و اگر می‌خواهید از این کد برای کار روی نمودارهایی غیر از نمودار فعالی کنونی، استفاده کنید، رفرنس به آرایه سری‌های زمانی را هم باید تغییر داد [بدین صورت که]

به

[تغییر کند].

در نتیجه چنین چیزی خواهیم داشت:

کد اندیکاتور ممکن است توابع هموارسازی مانند XXXSeries() را داشته باشد، که خودم آن را توسعه داده‌ام. برای استفاده کردن از قطعه کدهایی با چنین توابعی در کد اکسپرت، می‌بایستی این توابع را با نظیر اکسپرت‌شان، جایگزین کرد.

نتیجه‌گیری

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

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

امینی

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

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

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

کاربرد عملی سِرور خصوصی مجازی (VPS) برای ترید خودکار

نوشتن نظر شما

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