
چگونه در MQL4 یک ربات ترید سالم و قابل اطمینان را توسعه دهیم
در فرآیند تولید یک برنامه، برنامهنویس و توسعهدهندگان با این واقعیت مواجه میشوند که برنامهشان ممکن است شامل تمام خطاهای ممکن و ناممکن باشد! و در مرحلهی توسعه، این خطاها بسیار دردسرساز هستند، که منجر به بیاعتمادی به روش کار شده، و اگر برنامهی مدنظر، یک ربات معاملهگر باشد، تاثیر منفیاَش را روی سرمایهتان خواهید دید! چه بد! بیایید رایجترین خطاها، سرمنشأ آنها، و روشهای شناسایی و پردازش آنها را با هم آنالیز کنیم. در فرآیند توسعه و استفاده از یک اکسپرت برای نرمافزار متاتریدر ۴، این خطاها را ممکن است داشته باشیم:
- سینتکس – این خطاها ممکن است در مرحلهی کامپایل کردن ظاهر شوند و برنامهنویس بهراحتی میتواند آنها را برطرف سازد؛
- منطقی – این خطاها با کامپایلر مشخص نمیشوند. برای مثال: بههمریختگی در نام متغیرها، فراخوانهای اشتباه تابع، بهرهبرداری از انواع مختلف دادهها و غیره؛
- الگوریتمی – این خطاها وقتی اتفاق میاُفتند که براکتها دُرست سر جای خودشان نباشند، یا در صورت بههمریختگی با گزارههای شاخهها و غیره؛
- بُحرانی – این مدل خطاها غیرمحتملتر هستند. [در صورت رخ دادن]، برای بیرون کشیدن آنها باید کمی زحمت بکشید. با این حال، وقتی با dll کار میکنید، کمتر این مدل خطا را دارید؛
- معاملهای – این مدل خطاها وقتی اتفاق میاُفتند که با معاملات سروکار دارید. این خطاها برای رباتهای معاملهگر، نقطهی مناقصه هستند.
ابتدا اول از همه، پیشنهاد میکنیم، مستندات مربوط به خطاهای اجرایی را مطالعه کنید. با انجام این کار، زمان بسیار زیادی را ذخیره کردهاید بطوریکه بعداً نیاز نیست برای یک سری از موضوعات، وقت بگذارید. خطاهای ناشی از عملیاتهای ترید را نیز، از اینجا میتوانید دریافت کنید.
خطاهای سینتکس
خطاهایی از این نوع، نتیجهی اشتباهات چاپی عملگرها، متغیرها، و فراخوانهای مختلف توابع است. حین کامپایل شدن، کد برنامه بررسی میشود و تمام خطاهای سینتکس در پنجرهی “Tools” در نرمافزار متااِدیتور، نشان داده میشوند. در واقع، تقریباً همهی خطاها شناسایی شده و برنامهنویس میتواند آنها را برطرف کند.
بههمریختگی براکتها یکی از موارد استثنا است. وقتی براکتهایی که بازشدن و بستهشدن را نشان میدهند، در جای اشتباهی قرار داده شوند، در مرحلهی کامپایل شناسایی شده، اما محل قرارگیری خطا، اشتباه نشان داده میشود. آنگاه مجبور هستید دوباره کد را چک کرده تا خطا را چشمی پیدا کنید که متاسفانه این کار ممکن است همیشه نتیجه ندهد. رویکرد دوم، خاموش شدن پیدرپی بلوکهای کد با استفاده از comments(کامنتها) است. در این شرایط، اگر بعد از کامنتگذاری یک بلوک جدید، خطا از بین رفت، میتوان گفت خطا مسلماً در همان بلوک کامنتگذاریشده، بوده است. این کار بهشکل چشمگیری محدودهی جستجو را کم کرده و کمک میکند، قرارگیری اشتباه براکتها را سریع پیدا کنیم.
خطاهای منطقی، الگوریتمی، و بُحرانی
رایجترین خطاها از این نوع را میتوان در بههمریختگی نامها و نوع متغیرها، و نیز خطاهای الگوریتمی را در شاخههای اکسپرت، پیدا کرد. برای مثال، بیایید این کد را بررسی کنیم:
bool Some = false; void check() { // Much code Some = true; } // Very much code int start() { bool Some = false; // if(Some) { //sending the order } return(0); }
چه چیزی میتوانیم ببینیم؟ متغیر منطقی “Some”، که در تمام برنامه رایج است و شاخص مهمی برای بازشدن پوزیشن بهحساب میآید، بهطور تصادفی، کمتر از آنچه باید باشد، تعیین شدهاست. نتیجه، اشتباه بازشدن معامله و ضرر، خواهد بود. میتوانید اسامی بسیاری را برای متغیرها انتخاب کنید، اما بنا بر دلایلی، این اسامی در برنامههای بزرگ، بهطور تصادفی تکرار میشوند. و در نهایت میشود آن مشکلی که به آن اشاره کردیم.
این خطا وقتی اتفاق میاُفتد که متغیرها بهنوعی با هم مخلوط شده، یا بیان یک نوع، به بیان نوع دیگری اختصاص یافته است. برای مثال، در این خط
int profit = NormalizeDouble(SomeValue*point*2 / 3, digit);
سعی میکنیم که بیان مقدارِ نوعِ “double” را به متغیر نوع “int”، اختصاص دهیم، که نتیجه میشود: مقدار صفر. و ما هم خوشخیال در حال محاسبهی حد سود هستیم! این نوع خطا منجر به معاملهی اشتباه میشود.
خطای الگوریتمی در شاخههای یک اکسپرت یعنی براکتها طبق الگوریتم قرار نگرفتهاند، یا پوشش اشتباه عملگرهای “if” توسط عملگرهای “else” اتفاق اُفتاده است. در نتیجه اکسپرتی داریم که مطابق با نیاز فنی کار نمیکند.
برخی خطاها غیرقابل تصور هستند، بطوریکه ساعتها روی کد وقت میگذارید، و “به حالت مراقبه” میرسید تا خطا را پیدا کنید. متاسفانه امکان ردگیری مقادیر متغیرها در متااِدیتور وجود ندارد، که البته در محیطهایی مانند زبانهای خانوادهی C++ چنین محدودیتی را نداریم. بنابراین، تنها راهی که میماند، پیگیری خطاها از طریق پیامهای (صادر شده توسط) تابع ()Print است.
تابع ()GetLastError، کد خطا را برمیگرداند. توصیه میشود که آخرین مقدار را بعد از هر یک از بخشهای آسیبپذیر و حساس برنامه، بررسی کنید. با استفاده از کد خطا بهسادگی میتوانید توضیحات مربوط به خطا را در بخش مستندات پیدا کرده، و برای برخی از خطاها حتی روشهای برطرف کردن هم موجود است.
باید این نکته را بگوییم که خطاهای مذکور، به احتمال خیلی زیاد، در مرحلهی تست گرفتن، قبل از رفتن روی حساب دمو، شناسایی میشوند، بنابراین ضررهای همراه این خطاها، غیرمحتمل هستند.
اصلیترین ویژگی خطاهای بحرانی این است که وقتی اتفاق میاُفتند، اجرای برنامه بلافاصله متوقف میشود. با این حال، کد خطا در متغیر از پیش تعیینشدهی “last_error”، دستنخورده باقی میماند. این کار به ما این امکان را میدهد، کد خطایی که تابع ()GetLastError را فرامیخواند، یاد بگیریم.
خطاهای معاملهای
این خطاها اغلب منجر به ضرر و کار نکردن اکسپرت روی حساب دمو، و علاوه بر آن، روی حسابهای واقعی، میشوند. این خطاها وقتی رخ میدهند که دارید معاملات را ارسال یا اصلاح میکنید، و به بیان دیگر، حین تعامل با سرور ترید، این خطاها را خواهید داشت.
پردازش ساده مثل این:
ticket = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3, Bid + StopLoss*Point, Bid - TakeProfit*Point, 0, MAGICMA, ۰, Red); if(ticket > 0) { err = GetLastError(); Print("While opening the order the error # occured", err); }
کمکی نخواهد کرد. مطمئن شدیم که معامله به سرور ارسال نشده، و کد خطا را یاد گرفتیم. پس چه چیزی؟ یک ورود مهم به بازار را از دست دادیم، البته، اگر اکسپرتی سودده میداشتیم.
حالتی با حلقهی بیپایان:
while (true) { ticket = OrderSend(Symbol(), OP_SELL, Lots, Bid, slippage, Bid + StopLoss*Point, Bid - TakeProfit*Point, 0, MAGICMA, 0, Red); if(ticket > 0) { err = GetLastError(); Print("While opening the order the error # occured", err); break; } Sleep(1000); RefleshRates(); }
کمی کمک میکند. معامله احتمالاً به سرور میرسد. اما برخی مشکلات ممکن است در سر راه باشند:
- بروکر درخواستهای مکرر را دوست نخواهد داشت؛
- خطا ممکن است مهلک باشد، در اینجا درخواست به هیچ عنوان به سرور نخواهد رسید؛
- اکسپرت برای مدتی طولانی پاسخگو نخواهد بود؛
- سرور ممکن است درخواستهای ترید را اصلاً نپذیرد – ممکن است آخر هفته باشد، تعطیلات باشد، کارهای تعمیر و نگهداری در دست انجام باشند و غیره.
تقریباً هر خطایی منحصربهفرد است و نیاز به برطرف شدن به سبک خودش را دارد. بیایید دربارهی حالتی با عملگر Switch صحبت کنیم و هر خطا را کم و بیش بهصورت جداگانه رواج دهیم. خطای استاندارد #۱۴۶ –”Trade flow is busy”، با استفاده از سِمافور محققشده در کتابخانهی TradeContext.mqh، پردازش شدهاست. این کتابخانه و توضیحات دقیق آن را میتوانید در این مقاله پیدا کنید.
//The library for differentiation of work with the trading flow //written by komposter #include <TradeContext.mqh> //parameters for the signals extern double MACDOpenLevel=3; extern double MACDCloseLevel=2; extern double MATrendPeriod=26; // maximum acceptable slippage int slippage = 3; //total number of transactions int deals = 0; //time for the pause after transaction int TimeForSleep = 10; //period of request int time_for_action = 1; //number of tries of opening/closing the position int count = 5; //indicator of operability of the EA bool Trade = true; //indicator of availability of funds for opening the position bool NoOpen = false; //+------------------------------------------------------------------+ //| Do not ask the server for quotes on weekends | //+------------------------------------------------------------------+ bool ServerWork() { if(DayOfWeek() == 0 || DayOfWeek() == 6) return(false); return(true); } //+------------------------------------------------------------------+ //| Generation of magik | //+------------------------------------------------------------------+ int GenericMagik() { return(deals); } //+------------------------------------------------------------------+ //| Closing of transactions | //+------------------------------------------------------------------+ bool CloseOrder(int magik) { int ticket,i; double Price_close; int err; int N; //Function tries to shut the server at count attempts, if it fails, //it gives an error message to the logfile while(N < count) { for(i = OrdersTotal() - 1; i >= 0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) if(OrderSymbol() == Symbol()) if(OrderMagicNumber() == magik) { if(OrderType() == OP_BUY) Price_close = NormalizeDouble(Bid, Digits); if(OrderType() == OP_SELL) Price_close = NormalizeDouble(Ask, Digits); if(OrderClose(OrderTicket(), OrderLots(), Price_close,slippage)) { //reduce the number of transactions for the EA deals--; //the piece of the margin became available - you can open again NoOpen = false; return(true); } //we have reached this place, it means that the order has not been sent N++; //processing of possible errors err = ErrorBlock(); //if the error is seriuos if(err > 1) { Print("Manual closing of the order # needed", OrderTicket()); return(false); } } } // taking a pause of 5 seconds and trying to close the transaction again Sleep(5000); RefreshRates(); } //if we have reached this place, the transaction was not closed at count attempts Print("Manual closing of the order # needed",OrderTicket()); return(false); } //+-----------------------------------------------------------------------------+ //|Transaction for act 1-buy, 2-sell, the second parameter - the number of lots | //+-----------------------------------------------------------------------------+ int Deal(int act, double Lot) { int N = 0; int ticket; int err; double Price_open; double Lots; int cmd; int magik; magik = GenericMagik(); Lots = NormalizeDouble(Lot,1); if(act == 1) { Price_open = NormalizeDouble(Ask, Digits); cmd = OP_BUY; } if(act == 2) { Price_open = NormalizeDouble(Bid, Digits); cmd = OP_SELL; } //checking the margin for opening the position AccountFreeMarginCheck(Symbol(), cmd,Lots); err = GetLastError(); if(err>0) { Print("No money for new position"); NoOpen = true; return(0); } //Sending the order ticket = OrderSend(Symbol(), cmd, Lots, Price_open, slippage, ۰, ۰, ۰, magik); if(ticket > 0) { deals++; return(ticket); } //If the order has not been sent, we will try to open it 5 times again else { while(N < count) { N++; err = ErrorBlock(); if(err == 1) { Sleep(5000); RefreshRates(); if(act == 1) Price_open = NormalizeDouble(Ask, Digits); if(act == 2) Price_open = NormalizeDouble(Bid, Digits); ticket = OrderSend(Symbol(), cmd, Lots, Price_open, slippage, 0, 0, 0, magik); if(ticket > 0) { deals++; return(ticket); } } // we have got a serious error if(err > 1) return(0); } } return(0); } //+------------------------------------------------------------------+ //| ۰-no error, 1-need to wait and refresh, | //| ۲-transaction rejected, 3-fatal error | //+------------------------------------------------------------------+ //Block of the error control int ErrorBlock() { int err = GetLastError(); switch(err) { case 0: return(0); case 2: { Print("System failure. Reboot the computer/check the server"); Trade = false; return(3); } case 3: { Print("Error of the logic of the EA"); Trade = false; return(3); } case 4: { Print("Trading server is busy. Wait for 2 minutes."); Sleep(120000); return(2); } case 6: { bool connect = false; int iteration = 0; Print("Disconnect "); while((!connect) || (iteration > 60)) { Sleep(10000); Print("Connection not restored", iteration*10, " seconds passed"); connect = IsConnected(); if(connect) { Print("Connection restored"); return(2); } iteration++; } Trade = false; Print("Connection problems"); return(3); } case 8: { Print("Frequent requests"); Trade = false; return(3); } case 64: { Print("Account is blocked!"); Trade = false; return(3); } case 65: { Print("Wrong account number???"); Trade = false; return(3); } case 128: { Print("Waiting of transaction timed out"); return(2); } case 129: { Print("Wrong price"); return(1); } case 130: { Print("Wrong stop"); return(1); } case 131: { Print("Wrong calculation of trade volume"); Trade = false; return(3); } case 132: { Print("Market closed"); Trade = false; return(2); } case 134: { Print("Lack of margin for performing operation"); Trade = false; return(2); } case 135: { Print("Prices changed"); return (1); } case 136: { Print("No price!"); return(2); } case 138: { Print("Requote again!"); return(1); } case 139: { Print("The order is in process. Program glitch"); return(2); } case 141: { Print("Too many requests"); Trade = false; return(2); } case 148: { Print("Transaction volume too large"); Trade = false; return(2); } } return (0); } //+------------------------------------------------------------------+ //| generation of signals for opening/closing position on Macd | //+------------------------------------------------------------------+ int GetAction(int &action, double &lot, int &magik) { double MacdCurrent, MacdPrevious, SignalCurrent; double SignalPrevious, MaCurrent, MaPrevious; int cnt,total; MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0); MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0); SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1); MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0); MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1); if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDOpenLevel*Point) && MaCurrent>MaPrevious) { action=1; lot=1; return (0); } if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && MacdCurrent>(MACDOpenLevel*Point) && MaCurrent<MaPrevious) { action=2; lot=1; return (0); } total=OrdersTotal(); for(cnt=0;cnt<total;cnt++) { OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); if(OrderType()<=OP_SELL && // check for opened position OrderSymbol()==Symbol()) // check for symbol { if(OrderType()==OP_BUY) // long position is opened { // should it be closed? if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && MacdCurrent>(MACDCloseLevel*Point)) { action=3; magik=OrderMagicNumber(); return(0); // exit } } else // go to short position { // should it be closed? if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point)) { action=3; magik=OrderMagicNumber(); return(0); } } } } } //+------------------------------------------------------------------+ //| The EA initialization function | //+------------------------------------------------------------------+ int init() { if(!IsTradeAllowed()) { Print("Trade not allowed!"); return(0); } } //+------------------------------------------------------------------+ //| The EA deinitialization function | //+------------------------------------------------------------------+ int deinit() { //Closing all orders for(int k = OrdersTotal() - 1; k >= 0 ; k--) if(OrderSymbol() == Symbol()) { if(OrderType() == OP_BUY) OrderClose(OrderTicket(), OrderLots(), NormalizeDouble(Bid,Digits), 10); if(OrderType() == OP_SELL) OrderClose(OrderTicket(), OrderLots(), NormalizeDouble(Ask, Digits),10); } } //+------------------------------------------------------------------+ //| The EA start function | //+------------------------------------------------------------------+ int start() { int action =0; double lot = 1; int magik = 0; while(Trade) { Sleep(time_for_action*1000); RefreshRates(); /*Logic of the EA where the we calculate the action, position limit and magik for closing the order action 1-buy, 2-sell, 3-close for example, take the EA on Macd*/ GetAction(action,lot,magik); if(ServerWork()) { if(((action == 1) || (action == 2)) && (!NoOpen)) { if(TradeIsBusy() < 0) return(-1); Deal(action, lot); Sleep(TimeForSleep*1000); TradeIsNotBusy(); } if(action == 3) { if(TradeIsBusy() < 0) { return(-1); if(!CloseOrder(magik)) Print("MANUAL CLOSE OF TRANSATION NEEDED"); Sleep(TimeForSleep*1000); TradeIsNotBusy(); } } } else { Print("Weekends"); if(TradeIsBusy() < 0) return(-1); Sleep(1000*3600*48); TradeIsNotBusy(); } action = 0; lot = 0; magik = 0; } Print("Critical error occured and the work of the EA terminated"); return(0); } //+------------------------------------------------------------------+
این نسخه از ربات معاملهگر در یک حلقهی بیپایان کار میکند. و تقاضای آن زمانی است که اکسپرت چنداَرزی اِسکلپینگ، ایجاد شده باشد. الگوریتم عملکرد این اکسپرت، اینگونه است:
- سیگنال را از بلوک تحلیلی ()GetAction بگیر؛
- تراکنش لازم را در توابع ()Deal و ()CloseOrder انجام بده؛
- به نقطهی ۱ بعد از یک توقف کوتاه time_for_action برگرد، در شرایطی که مشکل جدی مانند عدم موفقیت نبوده است.
بعد از دریافت سیگنال (خرید، فروش، بستن) از بلوک تحلیلی، اکسپرت جریان معاملاتی را مسدود کرده (این مقاله را بخوانید) و سعی میکند تراکنش را انجام دهد، و بعد از آن برای چند ثانیه توقف کرده و جریان معاملاتی را برای دیگر اکسپرتها آزاد میکند. اکسپرت سعی میکند فرستادن معامله بیشتر از دفعات “شمارش” نباشد و همین برای عبور معامله در بازار ناپایداری که ممکن است ریکوت دریافت کنید، کافیست. اگر هنگام فرستادن معامله، خطایی جدی رخ دهد، اکسپرت کار را متوقف میکند. اگر هرگونه مشکلی پیش بیاید، پیام خطا در پوشهی “Expert Advisors” ظاهر میشود. اگر خطا بحرانی نباشد، اکسپرت به کار خود ادامه میدهد.
خطاها در روند ()ErrorBlock، مطابق با چنین طرحی پردازش میشوند: روند، کد خطا را میگیرد و یک الگوریتم کوتاه برای پردازش آن ارائه میدهد. برای بیشتر خطاها، فقط یک پیام در گزارش است. اگر خطا جدی باشد، آنگاه شاخصهای معاملاتی Trade و NoOpen تغییر میکنند. اگر مشکل قطع ارتباط باشد، پردازش شرایط کمی سختتر میشود. ربات شصت بار، همراه با توالی دورهای از پیش تعریفشده، تلاش میکند به سرور برسد. اگر به سرور دسترسی ایجاد نشد، احتمال زیاد مشکل جدی وجود دارد، و باید برای مدتی معاملات خود را متوقف کنید. بسته به تاثیر خطا روی ترید، الگوریتم پردازشی، معانی متفاوتی را برمیگرداند:
- ۰ – بدون خطا؛
- ۱ – خطا مربوط به نوسانات بازار است، میتوانید بار دیگر تلاش کنید و معامله را بفرستید؛
- ۲ – هنگام ارسال این معامله، خطای جدی رخ داد، برای مدتی پوزیشن جدید باز نکنید؛
- ۳ – خطای جدی در اکسپرت، قطع ارتباط، تا زمان شفاف شدن موضوع، ترید را متوقف کنید.
نتیجهگیری
خطاهای سینتکس، الگوریتمی، و منطقی، زمانی رخ میدهند که توجه کافی به کدنویسی الگوریتم ندارید. این خطاها با بررسی و اصلاح مقادیر متغیرها در گزارش، برطرف میشوند. همچنین، این خطاها را میتوان هنگام کامپایل کردن و تست گرفتن از اکسپرت نیز شناسایی کرد. این مدل از خطاها برای مدتی طولانی ماندگار نیستند، و قبل از رفتن به حساب دمو، برطرف میشوند.
خطاهای معاملهای زمانی رخ میدهند که معاملهای را به سرور میفرستیم. این خطاها با معاملهی واقعی سروکار دارند و ممکن است ریکوت، اُفت، نبرد معاملهگرها با اِسکلپینگ، و مشکل در تجهیزات را، داشته باشید. چنین خطاهایی را نمیتوان پیشبینی کرد. اما میتوان و باید آنها را پردازش کرد. بسته به منطق اکسپرت، تکرار تراکنشها و اصلاح معاملات، میتوانید هر هفته خطاها را بهصورت جداگانه، بررسی و پردازش کنید.
خطاهایی که حین کار کردن اکسپرت رخ میدهند، باید پردازش شوند. کار کوچکی نیست و به پیچیدگی اکسپرت و ویژگیهای آن بستگی دارد. در این مقاله میتوانید الگوی نمونهی اکسپرتی را پیدا کنید که این کار را انجام میدهد. ایجاد یک سیستم ترید با امنیت و سلامت بالا، زمان زیادی میطلبد. اما زمانی که صرف توسعهی یک سیستم خودکار بدون مشکل میشود، صدها برابر با ایمن ماندن سرمایه و کار خوب و خواب راحت شما، جبران خواهد شد.
این مقاله دارای فایل پیوست است.
فایل پیوست مقاله را میتوانید از اینجا دانلود کنید .
۲۳ مورد نظر
herbal remedies for ed
real viagra without a doctor prescription usa buy prescription drugs without doctor new ed treatments
cvs cialis over the counter cheapest cialis usa how often can you take cialis 20 mg
prednisone without prescription.net cheap prednisone buy prednisone online india
pay pal for cialis cialis how much cialis should i take
drugs that cause ed buy ed pills online drugs online
how much 1.87 ivermectin for horses do i give 15 lb dog ivermectin 1 dosage for dogs ivermectin mange treatment dosage
how to get prednisone without a prescription buy prednisone online buy 40 mg prednisone
best ed pill best ed pill best erectile dysfunction pills
propecia finasteride buy propecia online propecia women
propecia 1mg is propecia a prescription drug propecia for sale
ivermectin 3mg tablet how to use ivermectin for humans durvet ivermectin
ivermectin horse paste humans ivermectin covid trial stromectol for humans
propecia witout prescription finasterid propecia finasteride
finasteride 1 mg propecia uk finasteride 5 mg prices
mens ed pills erectile dysfunction drugs ed medications online
aarp approved canadian online pharmacies trusted online canadian pharmacy reviews canadian pharmacy certified canada pharmacy online
best erectile dysfunction pills erectile dysfunction pills ed pills
tadalafil 10mg price lowest price tadalafil tadalafil 10mg price
stromectol for humans for sale stromectol for sale stromectol price usa
sildenafil sildenafil 100 mg sildenafil 100 mg lowest price
cialis coupon cialis 20 mg price cialis pharmacy
clomid buy clomid 50mg clomid tablets