🚀 بهترین برنامه نویس و طراح ربات معامله گر فارکس و سفارش ربات و اکسپرت معامله گر متاتریدر به زبان MQL4 و MQL5 | متااکسپرت

تابع OnDeinit در Expert Advisor

تابع OnDeinit در Expert Advisor: ضامن بقاء و پاکیزگی سیستم معاملاتی

در دنیای پیچیده برنامه‌نویسی اکسپرت متاتریدر (MetaTrader Expert Programming)، توجه اغلب توسعه‌دهندگان تازه‌کار معطوف به هسته اصلی عملکرد یک اکسپرت ادوایزر (Expert Advisor) یعنی تابع OnTick (OnTick Function) است؛ جایی که منطق اصلی ورود و خروج معاملات شکل می‌گیرد. با این حال، این تمرکز صرف، منجر به نادیده گرفتن یکی از حیاتی‌ترین اجزای ساختار یک ربات معاملاتی می‌شود: تابع OnDeinit (OnDeinit Function). این تابع، که در حقیقت فرصت نهایی اکسپرت ادوایزر (Expert Advisor) برای خداحافظی مرتب و کامل با محیط اجرای خود است، نقشی محوری در مدیریت منابع (Resource Management)، پایداری بلندمدت و رفتار حرفه‌ای سیستم ایفا می‌کند. عدم پیاده‌سازی صحیح یا کامل این تابع، می‌تواند سرآغاز مجموعه‌ای از مشکلات پنهان، از نشت‌های حافظه (Memory Leaks) و باقی ماندن اشیای ناخواسته روی نمودار گرفته تا اختلال در اجرای سایر اسکریپت‌ها و اکسپرت‌ها باشد. درک عمیق تابع OnDeinit (OnDeinit Function) نه تنها برای نوشتن کد تمیز، بلکه برای تضمین این است که یک ربات معاملاتی پس از حذف شدن یا تغییر تنظیمات، هیچ اثر مخرب یا باقیمانده‌ای از خود در محیط متاتریدر ۴ (MetaTrader 4) یا متاتریدر ۵ (MetaTrader 5) باقی نگذارد. این تابع نقطه پایان منطقی است که اگر به‌درستی مدیریت نشود، می‌تواند تمام تلاش‌های انجام شده در فاز فعال‌سازی را تحت‌الشعاع قرار دهد و امنیت عملکرد کلی پلتفرم معاملاتی را به خطر اندازد.

چرخه حیات اکسپرت ادوایزر: جایگاه محوری OnDeinit

برای درک کامل اهمیت تابع OnDeinit (OnDeinit Function)، ابتدا باید معماری و توابع چرخه حیات اکسپرت (EA Lifecycle Functions) در متاتریدر ۴ (MetaTrader 4) و متاتریدر ۵ (MetaTrader 5) را تشریح کرد. هر اکسپرت ادوایزر (Expert Advisor) مراحل مشخصی را در طول عمر خود طی می‌کند که این مراحل توسط پلتفرم مدیریت می‌شوند. این چرخه معمولاً با فراخوانی تابع OnInit (OnInit Function) آغاز می‌شود، جایی که اکسپرت منابع لازم را مقداردهی اولیه کرده و آمادگی خود را برای شروع معاملات اعلام می‌کند. پس از موفقیت‌آمیز بودن تابع OnInit (OnInit Function)، سیستم وارد حلقه اصلی اجرا می‌شود که در آن تابع OnTick (OnTick Function) به‌ازای هر تیک جدید بازار فراخوانی شده و منطق معاملاتی اجرا می‌شود. اما این چرخه برای تکمیل شدن نیاز به یک پایان مشخص دارد، و اینجاست که تابع OnDeinit (OnDeinit Function) وارد عمل می‌شود. این تابع، در واقع، آخرین فرصت برنامه نویس برای انجام عملیات‌های “پاک‌سازی” و “خداحافظی” است. زمانی که کاربر تصمیم می‌گیرد اکسپرت را از روی چارت بردارد، یا زمانی که پلتفرم در حال بسته شدن است، یا حتی هنگام تغییر نماد معاملاتی یا تایم‌فریم، سیستم معاملاتی یک اخطار نهایی به اکسپرت ادوایزر (Expert Advisor) ارسال می‌کند و این تابع را برای اجرای آخرین دستورات فرا می‌خواند. تفاوت اساسی تابع OnDeinit (OnDeinit Function) با سایر توابع در این است که اجرای آن تضمین می‌کند که هیچ عملیات جاری و طولانی‌مدتی (مانند ارسال دستورات باز یا بستن پنجره‌های خاص) نباید در آن انجام شود؛ هدف صرفاً آزاد‌سازی و بستن هر چیزی است که در طول حیات اکسپرت باز شده است. این ساختار سه‌گانه (Init, Tick, Deinit) ستون فقرات طراحی سیستم‌های معاملاتی پایدار را تشکیل می‌دهد و تابع OnDeinit (OnDeinit Function) به‌عنوان موازنه دهنده تابع OnInit (OnInit Function) عمل می‌کند. اگر تابع OnInit (OnInit Function) وظیفه ایجاد و مقداردهی را دارد، تابع OnDeinit (OnDeinit Function) وظیفه نابودی کنترل شده و ایمن را بر عهده دارد.

دلایل فراخوانی تابع OnDeinit: درک منطق پشت پرده

یکی از پیچیده‌ترین و مهم‌ترین جنبه‌های استفاده از تابع OnDeinit (OnDeinit Function)، درک دلایلی است که باعث فراخوانی آن می‌شود. پلتفرم متاتریدر این دلایل را از طریق پارامتر reason (در MQL4) یا در ساختار const int reason (در MQL5) به این تابع منتقل می‌کند. این پارامتر به برنامه‌نویس اجازه می‌دهد تا بسته به علت حذف اکسپرت (Removing Expert Advisor)، اقدامات متفاوتی را انجام دهد. این تمایز کلیدی، امکان مدیریت منابع (Resource Management) هوشمندانه را فراهم می‌آورد.

در متاتریدر ۴ (MetaTrader 4)، پارامتر reason یک عدد صحیح ساده است که نشان‌دهنده حالت توقف است. اما در متاتریدر ۵ (MetaTrader 5)، این پارامتر (که اغلب به صورت const int reason در تعریف تابع نمایش داده می‌شود، هرچند که مستقیماً به‌عنوان یک پارامتر در تعریف تابعی که بدون پارامتر تعریف می‌شود ممکن است کمتر مورد استفاده قرار گیرد و در MQL5 بیشتر بر روی تابع OnDeinit بدون پارامتر تمرکز می‌شود، با این حال، دلیل توقف از طریق توابع سیستمی قابل استنتاج است یا در ساختارهای پیشرفته‌تر مدیریت می‌شود)، مفهوم دقیق‌تری را منتقل می‌کند. برای سادگی و پوشش دادن هر دو محیط، مهم‌ترین دلایل شامل موارد زیر هستند:

  1. REASON_REMOVE: این رایج‌ترین دلیل است و زمانی رخ می‌دهد که کاربر به‌صورت دستی اکسپرت را از روی چارت حذف می‌کند. در این حالت، باید تمامی عملیات‌های مرتبط با چارت و معاملات به‌طور کامل خاتمه یابد.
  2. REASON_RECOMPILE: زمانی که کد منبع اکسپرت ادوایزر (Expert Advisor) تغییر کرده و مجدداً کامپایل می‌شود. در این حالت، پلتفرم نسخه قدیمی را خاتمه می‌دهد تا نسخه جدید با موفقیت تابع OnInit (OnInit Function) را اجرا کند. این امر نشان می‌دهد که نیازی به ذخیره تنظیمات موقتی نیست، زیرا بلافاصله نسخه جدید فعال خواهد شد.
  3. REASON_CHARTCHANGE: هنگامی که نماد معاملاتی (Symbol) یا تایم‌فریم (Timeframe) چارت تغییر می‌کند. این دلیل نیاز به یک پاک‌سازی عمیق دارد، زیرا متغیرهای داخلی اکسپرت معمولاً به داده‌های نماد فعلی وابسته هستند.
  4. REASON_ACCOUNT: زمانی که شماره حساب کاربری تغییر می‌کند (مثلاً خروج از حساب دمو و ورود به حساب اصلی یا برعکس). این امر اغلب مستلزم بازنگری در احراز هویت یا متغیرهای مرتبط با حساب است.
  5. REASON_TEMPLATE: در صورتی که یک قالب (Template) جدید روی چارت اعمال شود که حاوی تنظیمات یا اکسپرت دیگری باشد.
  6. REASON_PARAMETERS: زمانی که پارامترهای ورودی (Inputs) اکسپرت از طریق پنجره تنظیمات تغییر می‌کنند. در این حالت، اکسپرت ابتدا با تابع OnDeinit (OnDeinit Function) غیرفعال شده و سپس با پارامترهای جدید مجدداً با تابع OnInit (OnInit Function) فراخوانی می‌شود.

درک تفاوت بین REASON_REMOVE و REASON_PARAMETERS حیاتی است؛ در حالت تغییر پارامتر، ما می‌دانیم که قرار است دوباره فراخوانی شویم، بنابراین شاید نیازی به بسته شدن کامل فایل‌های لاگ نباشد، اما در REASON_REMOVE، این آخرین شانس ماست. این تحلیل دقیق دلایل، هسته اصلی دیباگ اکسپرت (Expert Advisor Debugging) پیشرفته را تشکیل می‌دهد.

کاربردهای عملی و فنی تابع OnDeinit: مدیریت منابع در اوج حرفه‌ای‌گری

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

۱. حذف اشیای گرافیکی (Deleting Graphical Objects)

یکی از رایج‌ترین منابع مشکل در اکسپرت‌های نادیده گرفته شده، باقی ماندن اشیای گرافیکی است. اگر اکسپرت خطوط روند، کانال‌ها، فلش‌ها، مستطیل‌ها یا اندیکاتورهای سفارشی (Custom Indicators) را روی چارت ایجاد کرده باشد (با استفاده از توابعی مانند ObjectCreate() در MQL4/MQL5)، باید در تابع OnDeinit (OnDeinit Function) با استفاده از ObjectDelete() یا حلقه زدن بر روی تمام اشیای دارای پیشوند مشخص، آن‌ها را حذف کند. باقی ماندن این اشیا نه تنها باعث شلوغ شدن محیط کاربری می‌شود، بلکه می‌تواند سربار پردازشی غیرضروری بر روی متاتریدر ۴ (MetaTrader 4) یا متاتریدر ۵ (MetaTrader 5) تحمیل کند و همچنین در هنگام تغییر تایم‌فریم، باعث خطاهای رندرینگ شود. در کدنویسی MQL4 (MQL4 Programming) و کدنویسی MQL5 (MQL5 Programming)، استفاده از پیشوندها برای شناسایی اشیای ایجاد شده توسط اکسپرت، یک ضرورت امنیتی است.

۲. بستن فایل‌ها و مدیریت فایل و حافظه (Closing Files and Memory and File Management)

هرگاه اکسپرت ادوایزر (Expert Advisor) از مدیریت فایل و حافظه (Memory and File Management) برای ثبت سوابق (Logging) یا ذخیره داده‌های داخلی استفاده کرده باشد (مانند استفاده از FileOpen() برای نوشتن در فایل‌های .log یا .csv)، بستن این فایل‌ها در تابع OnDeinit (OnDeinit Function) حیاتی است. باز ماندن فایل‌ها می‌تواند منجر به قفل شدن فایل توسط سیستم عامل، از دست رفتن داده‌های نهایی ثبت شده یا اشغال منابع سیستمی شود. برای مثال، اگر داده‌های لحظه آخری که در آخرین تیک محاسبه شده‌اند، قبل از بسته شدن کامل فایل ثبت نشوند، بخشی از تاریخچه از دست می‌رود. این امر در کدنویسی MQL5 (MQL5 Programming) که معمولاً با ساختارهای داده پیچیده‌تر سروکار دارد، اهمیت مضاعفی پیدا می‌کند.

۳. ذخیره تنظیمات نهایی و وضعیت (Saving Final Settings)

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

۴. ثبت لاگ‌ها و بازخورد (Logging)

حتی اگر اکسپرت شما عملیات معاملاتی انجام ندهد، ثبت دقیق رویدادهای پایانی در فایل لاگ ضروری است. این شامل ثبت دلایل خاتمه (reason)، خلاصه‌ای از عملکرد در آن جلسه، یا هرگونه خطای داخلی که در آخرین لحظات رخ داده است، می‌شود. این لاگ‌ها نقش حیاتی در دیباگ اکسپرت (Expert Advisor Debugging) و بازبینی عملکرد سیستم در تستر استراتژی ایفا می‌کنند.

۵. غیرفعال‌سازی تایمرها و رویدادهای زمان‌بندی شده

در متاتریدر ۵ (MetaTrader 5)، توابع زمان‌بندی (مانند استفاده از رویدادهای زمان‌بندی شده) وجود دارند. اگر اکسپرت شما تایمرهایی را برای اجرای کد در فواصل مشخص تنظیم کرده باشد، این تایمرها باید در تابع OnDeinit (OnDeinit Function) به‌صراحت غیرفعال شوند تا از اجرای بی‌مورد کد پس از حذف اکسپرت جلوگیری شود.

بررسی اشتباهات رایج برنامه‌نویسان در استفاده از OnDeinit

بسیاری از مشکلات پنهان و باگ‌هایی که کاربران نهایی از اکسپرت ادوایزر (Expert Advisor) گزارش می‌دهند، ناشی از ضعف در تابع OnDeinit (OnDeinit Function) است. نادیده گرفتن این تابع، یک رویکرد غیرحرفه‌ای و پرخطر در کدنویسی MQL4 (MQL4 Programming) و کدنویسی MQL5 (MQL5 Programming) محسوب می‌شود.

اشتباه رایج اول، صرفاً خالی گذاشتن این تابع است. اگر اکسپرت شما، به عنوان مثال، ۵۰ خط شیء گرافیکی ایجاد کرده باشد، این ۵۰ شیء پس از حذف اکسپرت (Removing Expert Advisor) به حیات خود ادامه می‌دهند. اگر کاربر چارت را ببندد، متاتریدر سعی می‌کند این اشیاء را پاک کند، اما اگر اکسپرت مجدداً با پارامترهای دیگر فعال شود، ممکن است تداخل ایجاد شود یا اشیاء قدیمی با اشیاء جدید تداخل کنند. این امر به کند شدن رابط کاربری و در نهایت ایجاد باگ‌های غیرقابل پیش‌بینی منجر می‌شود.

اشتباه دوم، اجرای عملیات نادرست در تابع OnDeinit (OnDeinit Function) است. به عنوان مثال، تلاش برای ارسال دستور خرید یا فروش جدید در این تابع. این عملیات معمولاً با شکست مواجه می‌شود زیرا پلتفرم در حال حذف اکسپرت (Removing Expert Advisor) است و وضعیت اتصال به سرور معاملاتی ممکن است در حالت ناپایدار باشد. این تابع صرفاً برای پاک‌سازی است، نه اجرای منطق اصلی. اگر دلیل فراخوانی REASON_PARAMETERS باشد، تلاش برای معامله در این مرحله کاملاً بی‌معنی است، زیرا اکسپرت مجدداً مقداردهی اولیه خواهد شد.

اشتباه سوم، عدم تشخیص صحیح reason است. اگر اکسپرت در هنگام تغییر نماد (REASON_CHARTCHANGE) نتواند متغیرهای مرتبط با نماد قبلی را به‌طور کامل پاک کند، و این متغیرها در تابع OnInit (OnInit Function) جدید برای نماد دیگر به اشتباه مقداردهی شوند، منطق معاملاتی کاملاً منحرف خواهد شد. این امر نیاز به یک ساختار شرطی قوی در تابع OnDeinit (OnDeinit Function) دارد تا هر سناریوی خروج به‌درستی مدیریت شود.

مقایسه OnDeinit در MQL4 و MQL5: تفاوت‌های معماری

اگرچه مفهوم کلی تابع OnDeinit (OnDeinit Function) در هر دو پلتفرم یکسان است – یعنی خاتمه و پاک‌سازی – تفاوت‌های فنی قابل توجهی بین کدنویسی MQL4 (MQL4 Programming) و کدنویسی MQL5 (MQL5 Programming) وجود دارد که بر نحوه پیاده‌سازی تأثیر می‌گذارد.

در متاتریدر ۴ (MetaTrader 4)، تابع معمولاً به شکل void OnDeinit(const int reason) تعریف می‌شود. مدیریت اشیاء گرافیکی و فایل‌ها در MQL4 نسبتاً ابتدایی‌تر است و برنامه‌نویس باید به صورت دستی آرایه‌ها و اشاره‌گرها را در صورت استفاده، مدیریت کند. در MQL4، مدیریت حساب‌ها و درخواست‌های معاملاتی اغلب به صورت مستقیم و مبتنی بر توابع سطح پایین‌تر انجام می‌شد، که این امر بر اهمیت پاک‌سازی دستی منابع متمرکز بود.

در متاتریدر ۵ (MetaTrader 5)، معماری رویدادمحور پیچیده‌تر است و توابع چرخه حیات اکسپرت (EA Lifecycle Functions) عمیق‌تر در سیستم تعبیه شده‌اند. MQL5 امکانات بسیار پیشرفته‌تری برای مدیریت منابع (Resource Management) فراهم می‌کند. برای مثال، کلاس‌های داخلی C++-مانند، مدیریت حافظه را تا حدی خودکار می‌کنند. با این حال، در MQL5، تابع OnDeinit (OnDeinit Function) باید به دقت با ساختارهای داده پیچیده‌تر و همچنین کلاس‌های اندیکاتورهای سفارشی که ممکن است در طول عمر اکسپرت ایجاد شده باشند، تعامل داشته باشد. همچنین، در MQL5، فرایند مدیریت پیام‌های معاملاتی (Orders) و موقعیت‌ها (Positions) از طریق کلاس‌های CTrade یا CPositionInfo انجام می‌شود و اطمینان از بسته شدن یا لغو هرگونه درخواست باز مانده قبل از خروج، امری حیاتی است که باید در این مرحله بررسی شود. در حالی که MQL4 گاهی اوقات با “سکوت” در مورد پاک‌سازی منابع پیچیده کنار می‌آمد، MQL5 نیاز به وضوح بیشتری در این زمینه دارد.

نقش OnDeinit در دیباگ و تست اکسپرت

تابع OnDeinit (OnDeinit Function) یکی از ابزارهای کلیدی در فرآیند دیباگ اکسپرت (Expert Advisor Debugging) و تست عملکرد آن در تستر استراتژی (Strategy Tester) است. زمانی که یک اکسپرت را در حالت تست اجرا می‌کنیم، فرآیند توقف و شروع مجدد به دفعات تکرار می‌شود. هر بار که تستر استراتژی به پایان یک دوره تست می‌رسد یا تنظیمات آن تغییر می‌کند، تابع OnDeinit (OnDeinit Function) فراخوانی می‌شود.

استفاده از این تابع برای ثبت خلاصه‌ای از عملکرد در پایان هر اجرای تست، می‌تواند دیدگاه‌های بسیار ارزشمندی ارائه دهد. به عنوان مثال، می‌توان در آن ثبت کرد که: “تست در تاریخ X با سود Y و ضرر Z پایان یافت. تعداد معاملات موفق: N.” این اطلاعات به برنامه‌نویس کمک می‌کند تا الگوهای عملکردی اکسپرت را در شرایط مختلف شبیه‌سازی شده ردیابی کند. اگر در حین تست، اکسپرت به دلیل خطای غیرمنتظره متوقف شود و این توقف در لاگ اصلی ثبت نشود، تنها راه تشخیص علت، بررسی خروجی تابع OnDeinit (OnDeinit Function) خواهد بود تا ببینیم آخرین ورودی‌ها چه بوده‌اند. در واقع، این تابع به عنوان یک “جعبه سیاه” لحظه پایانی عمل می‌کند که جزئیات آخرین حالت سیستم را قبل از ریست شدن ذخیره می‌سازد.

بهترین شیوه‌های طراحی (Best Practices) برای پیاده‌سازی OnDeinit

برای اطمینان از اینکه اکسپرت ادوایزر (Expert Advisor) شما عملکردی پایدار، قابل اعتماد و حرفه‌ای داشته باشد، رعایت اصول زیر در پیاده‌سازی تابع OnDeinit (OnDeinit Function) ضروری است:

  1. مدیریت مشروط بر اساس Reason: هرگز نباید یک تابع OnDeinit (OnDeinit Function) بدون منطق شرطی (switch/case یا if/else) پیاده‌سازی شود. عملیات حساس مانند بستن کامل فایل‌های لاگ باید تنها در صورتی انجام شود که reason برابر با REASON_REMOVE یا موارد مشابهی باشد که نشان‌دهنده خروج دائمی است. برای REASON_RECOMPILE یا REASON_PARAMETERS، پاک‌سازی باید سبک‌تر باشد و منابعی که قرار است بلافاصله مجدداً مقداردهی شوند، دست‌نخورده باقی بمانند یا با فرض مقداردهی مجدد در تابع OnInit (OnInit Function) بعدی پاک شوند.
  2. مستندسازی دقیق: تمام عملیات‌های پاک‌سازی انجام شده در تابع OnDeinit (OnDeinit Function) باید با کامنت‌های واضح مستند شوند. برای مثال، باید مشخص شود که چرا اشیای با پیشوند “EA_MyBot_” حذف می‌شوند یا چرا فایل لاگ با موفقیت بسته شد. این امر نگهداری کد را برای توسعه‌دهندگان آینده (یا خود شما پس از شش ماه) تسهیل می‌کند.
  3. عدم وابستگی به منابع خارجی: در تابع OnDeinit (OnDeinit Function)، نباید هیچ گونه تلاشی برای ارسال مجدد دستورات معاملاتی یا برقراری ارتباط با سرورهای خارجی انجام شود. این تابع باید فقط بر روی منابعی تمرکز کند که اکسپرت در طول عمر خود در محیط محلی متاتریدر ایجاد کرده است.
  4. تأکید بر توابع استاندارد: همیشه از توابع داخلی پلتفرم برای حذف اشیاء استفاده کنید (مانند ObjectDelete یا ChartIndicatorDelete). تلاش برای دستکاری مستقیم فایل‌های تنظیمات کاربر یا اشیاء سیستمی می‌تواند منجر به خطاهای سطح پایین و غیرقابل بازیابی شود.
  5. آزادسازی حافظه و متغیرهای سراسری: اگر از کلاس‌ها یا ساختارهای داده پیچیده‌ای در سطح سراسری استفاده می‌کنید، اطمینان حاصل کنید که حافظه اختصاص داده شده به‌طور صریح در تابع OnDeinit (OnDeinit Function) آزاد شده باشد، به خصوص اگر از ساختارهای تخصیص حافظه دستی در کدنویسی MQL4 (MQL4 Programming) استفاده می‌کنید.

تابع OnDeinit (OnDeinit Function) فراتر از یک تابع حاشیه‌ای است؛ آن نماد تعهد برنامه‌نویس به نظم و ساختار در یک سیستم معاملاتی پیچیده است. پیاده‌سازی دقیق آن، تفاوت بین یک ربات معاملاتی آزمایشی و یک ابزار مالی پایدار در محیط واقعی معاملات را مشخص می‌کند و تضمین می‌کند که اکسپرت ادوایزر (Expert Advisor) شما حتی هنگام خاموش شدن نیز، مسئولیت‌پذیر باقی بماند.

دیدگاه‌ها (0)

  • نظرات نامربوط به محتوا تأیید نخواهند شد.
  • لطفاً از افزودن نظرات تکراری خودداری کنید.
  • نظرات مربوط به دوره‌ها فقط برای خریداران محصول است.

*
*