← К каталогу
Исходный код · ID 3486

FiboTrader Scaner

fibotrader scaner mt4.mq4
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

FiboTrader Scaner — советник для MetaTrader 4, предназначенный для автоматического сканирования инструментов из списка наблюдения и поиска торговых сигналов на основе анализа фибоначчи-импульсов и ценовых уровней. Советник отслеживает динамику цены относительно ключевых уровней и формирует сигналы на покупку или продажу при достижении заданных условий входа.

В качестве дополнительного фильтра используется комбинация скользящей средней и параболического SAR, настройка которых доступна пользователю. Советник поддерживает различные режимы торговли: только лонги, только шорты или оба направления одновременно.

Система уведомлений включает отправку Push-уведомлений, email-алертов и сводку за конец торгового дня. Вся информация о текущих сигналах отображается на графике в виде таблицы с возможностью открытия графика соответствующего инструмента по клику.

Советник содержит встроенную систему проверки лицензии с шифрованием и ограниченным пробным режимом. Данные о сигналах сохраняются в файл и обновляются в течение торгового дня.

Настройки программы

НастройкаОписание
Debug mode (Debug) Отладка — Включает или отключает режим отладки.
TradeMode Режим торговли — Режим торговли: только лонги, только шорты или оба направления.
Default template for signal charts (DefaultTemplate) Шаблон по умолчанию — Имя шаблона графика, применяемого при открытии нового графика по сигналу.
Push alert (AlertPush) Push-алерт — Включает или отключает отправку Push-уведомлений о торговых сигналах.
Mail alert (AlertMail) Email-алерт — Включает или отключает отправку email-уведомлений о торговых сигналах.
Alert in end day (AlertEndDay) Алерты в конце дня — Включает или отключает отправку сводки о сигналах в конце торгового дня.
TimeAlert Время алерта — Время в формате ЧЧ:ММ, до которого отправляется сводка о сигналах за день.
tf (tf) Таймфрейм анализа — Таймфрейм для основного анализа ценовых данных.
Flat Minimal Bars (FlatMinimalBars) Минимальные бары для флэта — Минимальное количество баров для коррекции при анализе импульса.
Open Level1 (OpenLevel1) Уровень входа 1 — Процент от размера импульса, на котором происходит вход в позицию.
Filter mode (Filter) Режим фильтра — Режим фильтрации сигналов. Доступны варианты: отключён фильтр или фильтр на основе MA+SAR.
Filter tf (FilterTF) Таймфрейм фильтра — Таймфрейм, на котором рассчитывается фильтрующий индикатор.
Получить полный код

Поиск по коду файла

#property version   "1.15"
#property strict

/*
1.14
- исправление загрузки и расчета фильтров
- отображение статуса поиска сигнала
- исправление отправки сигналов
- исправление лагов из-за анализа
*/

#define LICENSE_OFF

#define LICENSE_DEMO

#define TRIAL_DAYS 30

#define LICENSE_NAME "$zq2fl{t}{n~~~@mf5j#y|~b@aanx66x"
#define LICENSE_FILE "ftl"

#define VALID_KEY "Expert active. "
#define EMPTY_KEY "You must activate the adviser."
#define INVALID_KEY "Invalid license."
#define EXPERIED "License expired."
#define NOTESTER "No testing."

bool     bcheck=false;

#define LINE_HEIGTH 16
#define LINE_WIDTH 400
#define TABLE_X1 100
#define TABLE_X2 250
#define START_Y 35

#define POINTS_NEW
#include <sovetnikovNET\Orders.mqh>
#include <sovetnikovNET\Time.mqh>
#include <sovetnikovNET\Draw.mqh>
#include <sovetnikovNET\Bcrypt.mqh>

enum enum_filter
  {
   FILTER_OFF, //Off
   FILTER_MASAR, //MA+SAR
  };

B Debug=true; //Debug mode
input ENUM_TRADEMODE TradeMode=TRADEMODE_LONG;
S DefaultTemplate="FiboTrader Scaner.tpl"; //Default template for signal charts
B AlertPush=false; //Push alert
B AlertMail=false; //Mail alert
B AlertEndDay=true; //Alert in end day
S TimeAlert="23:00";

TF tf=PERIOD_D1; //tf
bool  ManualFirstFibo=false; //Manual first fibo
bool  NewFibo=true; //Draw new fibo
I   FlatMinimalBars=1; //Flat Minimal Bars
double ReverseSensitivity=5; //Reverse signal sensitivity %
double ImpulseSizeMin=0; //Impulse minimal at points (0 - off)
double ImpulseSizeMax=0; //Impulse maximum at points (0 - off)
D OpenLevel1=38.2; //Open Level1
double SL_Level=100; //SL Level %
double CloseSensitivity=30; //Close signal sensitivity %
input enum_filter Filter=FILTER_MASAR; //Filter mode
TF FilterTF=PERIOD_D1; //Filter tf
int MA_Fast=20;
int MA_Slow=200;
double SAR_Step=0.002;
double SAR_Maximum=0.02;

C Long_Color=clrGreen; //Long color
C Short_Color=clrRed; //Short color
C Long_Text_Color=clrWhite; //Long text color
C Short_Text_Color=clrWhite;//Short text color
C Table_Lines_Color=clrBlack; //Table lines color

CGlobal m_global;

class CDataSignals
  {
public:
   bool              bfile;
   datetime          timeSignal; //время сигнала
   string            smb; //символ
   int               direct; //направление бай 1, -1 селл, 0 -нет
   datetime          time100;
   datetime          time0;
   double            price100;
   double            price0;
   double            priceOpen;
   bool              balert; //был отправлен алерт

  };

class CAlertsSignals
  {
public:

   CTime             m_time;
   CDataSignals      data[];
   bool              m_day_alerts;

   int               hfile;
   ENUM_TIMEFRAMES   m_tf;
   string            m_FileName;

   CCanvas           m_canvas;

   string            TypeToDir(int itype)
     {
      if(itype>0)
         return "LONG";
      if(itype<0)
         return "SHORT";

      return "INVALID";
     }

   void              Reset(bool bNewDay)
     {
      if(bNewDay)
         m_day_alerts=false;
     }

   void              SendSignals(bool bSend,bool bAlertDay,string stime,bool bTelegram,bool bMail,bool bPush)
     {

      datetime tmDay=iTime(_Symbol,PERIOD_D1,0); //грузим текущий день
      string ssignal=StringFormat("Signals %s\n",TimeToString(tmDay,TIME_DATE));
      string stema=ssignal;

      int isize=ArraySize(data);
      int icount=0;

      bool btime=m_time.IsEndTime(TimeAlert);

      bool bDayDebug=false;
      if(bDayDebug)
         btime=true;

      if(bSend)
        {
         for(int i=0; i<isize; i++)
           {

            if(data[i].timeSignal>=tmDay && (!data[i].balert || (bAlertDay && btime && !m_day_alerts)))
              {
               int idgt=(int)SymbolInfoInteger(data[i].smb,SYMBOL_DIGITS);

               MqlTick tick;
               SymbolInfoTick(data[i].smb,tick);

               double drisk=0,dopen=0,ddelta=0;
               bool bShow=true;

               if(data[i].direct>0)
                 {
                  drisk=ND2(MathAbs(tick.ask-data[i].price100)*100/tick.ask);
                  dopen=tick.ask;
                  ddelta=data[i].priceOpen-tick.ask;
                  if(ddelta<0)
                     bShow=false;
                 }

               if(data[i].direct<0)
                 {
                  drisk=ND2(MathAbs(data[i].price100-tick.bid)*100/tick.bid);
                  dopen=tick.bid;
                  ddelta=tick.bid-data[i].priceOpen;

                  if(ddelta<0)
                     bShow=false;
                 }

               if(bShow)
                 {
                  ssignal=StringFormat("%s%s %s OPEN(%s): %s SL: %s (%.2f%%)\n",
                                       ssignal,data[i].smb,TypeToDir(data[i].direct),DoubleToString(data[i].priceOpen,idgt),DoubleToString(dopen,idgt),
                                       DoubleToString(data[i].price100,idgt),drisk);

                  DEBUG("%s %s OPEN(%s): %s SL: %s (%.2f%%)",
                        data[i].smb,TypeToDir(data[i].direct),DoubleToString(data[i].priceOpen,idgt),DoubleToString(dopen,idgt),
                        DoubleToString(data[i].price100,idgt),drisk);
                 }
               data[i].balert=true;
               icount++;
              }
           }

         if(icount>0)
           {
            if(bMail)
              {
               SendMail(stema,ssignal);

              }

            if(bPush)
               SendNotification(ssignal);

            if(btime)
              {
               m_day_alerts=true; //поставим флаг что в этот день уже отправляли суммарные данные
              }

            Save();
           }

        }

      int x_size=ChartWidth();
      int y_size=ChartHeight();

      m_canvas.Resize(x_size,y_size);

      tmDay=iTime(_Symbol,PERIOD_D1,1); //грузим за текущий и вчерашний
      ssignal=StringFormat("Signals %s\n",TimeToString(tmDay,TIME_DATE));
      int j=0;

      for(int i=0; i<isize; i++)
        {

         if(data[i].timeSignal>=tmDay)
           {
            int idgt=(int)SymbolInfoInteger(data[i].smb,SYMBOL_DIGITS);

            MqlTick tick;
            SymbolInfoTick(data[i].smb,tick);

            double drisk=0,dopen=0,ddelta=0;

            bool bShow=true;

            if(data[i].direct>0 && tick.ask>0)
              {
               drisk=ND2(MathAbs(tick.ask-data[i].price100)*100/tick.ask);
               dopen=tick.ask;
               ddelta=data[i].priceOpen-tick.ask;
               if(ddelta<0)
                  bShow=false;
              }

            if(data[i].direct<0 && tick.bid>0)
              {
               drisk=ND2(MathAbs(data[i].price100-tick.bid)*100/tick.bid);
               dopen=tick.bid;
               ddelta=tick.bid-data[i].priceOpen;
               if(ddelta<0)
                  bShow=false;
              }

            if(bShow)
              {
               ssignal=StringFormat("%s LEVEL(%s): %s SL: %s (%.2f%%)",
                                    data[i].smb,DoubleToString(data[i].priceOpen,idgt),DoubleToString(dopen,idgt),
                                    DoubleToString(data[i].price100,idgt),drisk);

               int x=5,y=START_Y+(LINE_HEIGTH+1)*j;

               m_canvas.FillRectangle(x,y,x+LINE_WIDTH,y+LINE_HEIGTH,ColorToARGB(data[i].direct>0?Long_Color:Short_Color));
               m_canvas.Line(x,y+LINE_HEIGTH,x+LINE_WIDTH,y+LINE_HEIGTH,ColorToARGB(Table_Lines_Color));

               m_canvas.TextOut(x+5,y+2,data[i].smb,ColorToARGB(data[i].direct>0?Long_Text_Color:Short_Text_Color),TA_LEFT|TA_TOP);
               m_canvas.Line(x+TABLE_X1-5,y+1,x+TABLE_X1-5,y+LINE_HEIGTH-1,ColorToARGB(Table_Lines_Color));
               m_canvas.TextOut(x+TABLE_X1,y+2,StringFormat("LEVEL(%s): %s",DoubleToString(data[i].priceOpen,idgt),DoubleToString(dopen,idgt)),ColorToARGB(data[i].direct>0?Long_Text_Color:Short_Text_Color),TA_LEFT|TA_TOP);
               m_canvas.Line(x+TABLE_X2-5,y+1,x+TABLE_X2-5,y+LINE_HEIGTH-1,ColorToARGB(Table_Lines_Color));
               m_canvas.TextOut(x+TABLE_X2,y+2,StringFormat("SL: %s (%.2f%%)",DoubleToString(data[i].price100,idgt),drisk),ColorToARGB(data[i].direct>0?Long_Text_Color:Short_Text_Color),TA_LEFT|TA_TOP);

               j++;

              }
           }
        }

      m_canvas.Update();

     }

   int               getIndex(int iindex)
     {

      datetime tmDay=iTime(_Symbol,PERIOD_D1,0); //грузим текущий день

      int isize=ArraySize(data);

      int j=0;

      for(int i=0; i<isize; i++)
        {

         if(data[i].timeSignal>=tmDay)
           {
            int idgt=(int)SymbolInfoInteger(data[i].smb,SYMBOL_DIGITS);

            MqlTick tick;
            SymbolInfoTick(data[i].smb,tick);

            double drisk=0,dopen=0,ddelta=0;

            bool bShow=true;

            if(data[i].direct>0 && tick.ask>0)
              {
               drisk=ND2(MathAbs(tick.ask-data[i].price100)*100/tick.ask);
               dopen=tick.ask;
               ddelta=data[i].priceOpen-tick.ask;
               if(ddelta<0)
                  bShow=false;
              }

            if(data[i].direct<0 && tick.bid>0)
              {
               drisk=ND2(MathAbs(data[i].price100-tick.bid)*100/tick.bid);
               dopen=tick.bid;
               ddelta=tick.bid-data[i].priceOpen;
               if(ddelta<0)
                  bShow=false;
              }

            if(bShow)
              {
               if(iindex==j)
                  return i;

               j++;

              }
           }
        }

      return -1;
     }

   bool              Find(string smb,datetime &time100,datetime &time0,double &price100,double &price0)
     {

      int isize=ArraySize(data);

      for(int i=isize-1; i>=0; i--)
        {
         if(smb==data[i].smb)
           {

            time100=data[i].time100;
            time0=data[i].time0;

            price100=data[i].price100;
            price0=data[i].price0;

            return true;
           }
        }
      return false;
     }

   void              AddSignal(bool bfile,string smb,datetime time100,datetime time0,double price100,double price0,double priceOpen,bool bAlert)
     {

      int isize=ArraySize(data);

      bool bfind=false;
      for(int i=0; i<isize; i++)
        {
         if(data[i].price100==price100 && data[i].time100==time100 && smb==data[i].smb)
           {
            bfind=true;
           }

         if(smb==data[i].smb)
           {

            data[i].bfile=bfile;
            data[i].timeSignal=iTime(smb,m_tf,0);
            data[i].smb=smb;
            data[i].direct=0;
            if(price0>price100)
               data[i].direct=1;
            if(price0<price100)
               data[i].direct=-1;

            data[i].time100=time100;
            data[i].time0=time0;
            data[i].price100=price100;
            data[i].price0=price0;
            data[i].priceOpen=priceOpen;

            bfind=true;
           }

         if(bfind)
            break;
        }

      if(!bfind)
        {

         if(ArrayResize(data,isize+1)!=-1)
           {
            int i=isize;
            data[i].bfile=bfile;
            data[i].timeSignal=iTime(smb,m_tf,0);
            data[i].smb=smb;
            data[i].direct=0;
            if(price0>price100)
               data[i].direct=1;
            if(price0<price100)
               data[i].direct=-1;

            data[i].time100=time100;
            data[i].time0=time0;
            data[i].price100=price100;
            data[i].price0=price0;
            data[i].priceOpen=priceOpen;
            data[i].balert=bAlert;

           }

        }

     }

   int               Load()
     {

      if(hfile==INVALID_HANDLE)
         return 0;

      datetime tmDay=iTime(_Symbol,PERIOD_D1,1); //грузим 2 дня прошлый и текущий

      ArrayResize(data,0);
      int i=0;

      string sline="";
      FileSeek(hfile,0,SEEK_SET);//ставим на начало файла
      while(!IsStopped() && !FileIsEnding(hfile))
        {

         sline=FileReadString(hfile);
         string sret[];

         int iread=string_explode(";",sline,sret);

         if(iread==9)
           {

            datetime timeSignal=StringToTime(sret[0]);

            if(timeSignal>=tmDay)
              {

               string smb=sret[1];
               int direct=(int)StringToInteger(sret[2]);
               datetime time100=StringToTime(sret[3]);
               datetime time0=StringToTime(sret[4]);
               double price100=StringToDouble(sret[5]);
               double price0=StringToDouble(sret[6]);
               double priceOpen=StringToDouble(sret[7]);
               bool bAlert=(bool)StringToInteger(sret[8]);

               AddSignal(true,smb,time100,time0,price100,price0,priceOpen,bAlert);//сохраняем в памяти данныые

              }
           }

        }

      return ArraySize(data);
     }

   void              Save()
     {
      if(hfile==INVALID_HANDLE)
        {
         this.OnInit(m_tf,m_FileName);

         if(hfile==INVALID_HANDLE)
           {
            Print("Invalid file");
            return;
           }
        }

      string sline="";
      FileSeek(hfile,0,SEEK_END);//ставим на начало файла и переписываем файл
      ulong itell=FileTell(hfile);

      int isize=ArraySize(data);
      int i=0;

      datetime tmDay=iTime(_Symbol,PERIOD_D1,1);

      while(!IsStopped() && i<isize)
        {

         /*
         datetime          timeSignal; //время сигнала
         string            smb; //символ
         int               direct; //направление бай 1, -1 селл, 0 -нет
         datetime          time100;
         datetime          time0;
         double            price100;
         double            price0;
         double            priceOpen;
         };
         */
         if(data[i].timeSignal>0)
           {

            int idgt=(int)SymbolInfoInteger(data[i].smb,SYMBOL_DIGITS);

            sline=StringFormat("%s;%s;%d;%s;%s;%s;%s;%s;%d\r\n",
                               TimeToString(data[i].timeSignal),
                               data[i].smb,
                               data[i].direct,
                               TimeToString(data[i].time100),
                               TimeToString(data[i].time0),
                               DoubleToString(data[i].price100,idgt),
                               DoubleToString(data[i].price0,idgt),
                               DoubleToString(data[i].priceOpen,idgt),
                               data[i].balert
                              );
            itell=FileTell(hfile);

            FileSeek(hfile,0,SEEK_SET); //вернем положение в начало

            bool bfind=false;

            while(!IsStopped() && !FileIsEnding(hfile))
              {

               ulong ilastread=FileTell(hfile);
               string sline_check=FileReadString(hfile);
               string sret[];

               int iread=string_explode(";",sline_check,sret);

               if(iread==9)
                 {

                  datetime timeSignal=StringToTime(sret[0]);

                  string smb=sret[1];
                  int direct=(int)StringToInteger(sret[2]);
                  datetime time100=StringToTime(sret[3]);
                  datetime time0=StringToTime(sret[4]);
                  double price100=StringToDouble(sret[5]);
                  double price0=StringToDouble(sret[6]);
                  double priceOpen=StringToDouble(sret[7]);

                  if(data[i].timeSignal>=tmDay && smb==data[i].smb && direct==data[i].direct && time100==data[i].time100
                     && price100==data[i].price100)
                    {
                     FileSeek(hfile,ilastread,SEEK_SET); //вернем положение в начало этой строчки и перепишем ее
                     FileWriteString(hfile,sline); //обновляем строчку

                     bfind=true;
                    }

                 }

               if(bfind)
                  break;

              }

            FileSeek(hfile,itell,SEEK_SET); //вернем положение в последнюю точку

            if(!bfind)
              {
               if(data[i].bfile==false)
                 {
                  if(FileWriteString(hfile,sline)>0) //сохраняем строчку
                     data[i].bfile=true; //сохранили в файл
                 }
              }
            else
              {
               data[i].bfile=true; //сохранили в файл
              }
           }

         i++;

        }

     }

   bool              OnInit(ENUM_TIMEFRAMES etf,string sFileName)
     {
      // код доступен только после оплаты
   }

   void              OnDeinit()
     {

      if(hfile!=INVALID_HANDLE)
        {
         FileClose(hfile);
        }

      m_canvas.Destroy();
      ObjectDelete(ChartID(),"symbol"); //патч для мт4

      ArrayFree(data);
     }

  };

class CFiboSignal
  {
public:
   string            m_name;

   double            ma_fast;
   double            ma_slow;
   double            sar;

   int               iFlatMinimalBars;

   double            dLevel0,dLevel100;
   datetime          tmTime0,tmTime100;

   int               hma_fast,hma_slow,hsar;

   bool              IsWork(string sSymbol=NULL)
     {
      if(sSymbol==NULL)
         sSymbol=m_name;

      if(IsTesting() || IsOptimization())
         return true;

      if(TerminalInfoInteger(TERMINAL_CONNECTED)==false)
         return false; //дисконнект

      return true;
     }

   virtual void      Reset()
     {
      dLevel0=0;
      dLevel100=0;
      tmTime0=0;
      tmTime100=0;

     }

   virtual bool      OnInit(string m_smb)
     {
      // код доступен только после оплаты
   }

   virtual bool      OnDeinit()
     {

#ifdef __MQL5__
      IndicatorRelease(hsar);
      IndicatorRelease(hma_fast);
      IndicatorRelease(hma_slow);

#endif
      return true;
     }

   int               filterMASAR(ENUM_TIMEFRAMES etf,int ibar=0)
     {

      ma_fast=0;
      ma_slow=0;
      sar=0;

#ifdef __MQL5__

      double buf1[],buf2[],buf3[];

      if(BarsCalculated(hma_fast)>0 && BarsCalculated(hma_slow)>0 && BarsCalculated(hsar)>0)
        {

         if(CopyBuffer(hma_fast,0,ibar,1,buf1)==1)
            ma_fast=buf1[0];

         if(CopyBuffer(hma_slow,0,ibar,1,buf2)==1)
            ma_slow=buf2[0];

         if(CopyBuffer(hsar,0,ibar,1,buf3)==1)
            sar=buf3[0];
        }

#else
      ma_fast=iMA(m_name,etf,MA_Fast,0,MODE_EMA,PRICE_CLOSE,ibar);
      ma_slow=iMA(m_name,etf,MA_Slow,0,MODE_EMA,PRICE_CLOSE,ibar);
      sar=iSAR(m_name,etf,SAR_Step,SAR_Maximum,ibar);
#endif

      if(sar>0 && ma_fast>0 && ma_slow>0)
        {

         if(ma_fast>0 && ma_fast>ma_slow && sar<ma_fast && iClose(m_name,etf,1)>ma_fast && iClose(m_name,etf,1)>ma_slow)
            return OP_BUY;
         if(ma_fast>0 && ma_fast<ma_slow && sar>ma_fast && iClose(m_name,etf,1)<ma_fast && iClose(m_name,etf,1)<ma_slow)
            return OP_SELL;
        }

      return -1;
     }

   void              FindImpulse(string name,bool bFilter,ENUM_TIMEFRAMES etf,datetime &time0,double &level0,datetime &time100,double &level100)
     {

      if(level0<=0 || level100<=0)
        {
         return;
        }

      int icount=0;
      int ibar0=-1,ibar100=-1;

      double dhigh=0,dlow=0,dopen=0,dclose=0;

      ibar0=iBarShift(m_name,etf,time0); //определим сколько баров от 0 точки
      ibar100=iBarShift(m_name,etf,time100); //определим сколько баров от 100 точки

      int imaxSize=(int)MathMin(ibar0,ibar100);

      MqlRates rates[];
      bool bUpdate=false;

      if(CopyRates(m_name,etf,0,imaxSize+1,rates)==imaxSize+1) //загрузим данные
        {
         ArraySetAsSeries(rates,true);

         for(int i=imaxSize; i>=0; i--)
           {

            dhigh=rates[i].high;//iHigh(m_name,etf,i);
            dlow=rates[i].low;//iLow(m_name,etf,i);
            dclose=rates[i].close;//iClose(m_name,etf,i);
            dopen=rates[i].open; //iOpen(m_name,etf,i);

            ibar0=iBarShift(m_name,etf,time0); //определим сколько баров от 0 точки
            ibar100=iBarShift(m_name,etf,time100); //определим сколько баров от 100 точки

            datetime tmDebug=iTime(m_name,tf,i);

            if(level0>level100)
              {

               if((dclose<dopen || icount>0) && i>0)
                 {
                  icount++; //нужна хотя бы одна коррекционная свечка, с нее включаем анализ коррекций, но не от этйо свечки. от тайм0
                 }

               if(dhigh>=level0 && level0>0)
                 {

                  level0=dhigh;
                  time0=rates[i].time;

                  int ibarsHigh=ibar0-i;
                  int ibarsLow=ibar0-i;

                  if(ibarsHigh>=0)
                    {

                     if(icount>0 && icount>iFlatMinimalBars)
                       {

                        int ihigh=iHighest(m_name,etf,MODE_HIGH,ibarsHigh+1,i); //ищем реальный максимум от точки 0
                        int ilow=iLowest(m_name,etf,MODE_LOW,ibarsLow+1,i);

                        if(ihigh!=-1)
                          {

                           time0=iTime(m_name,etf,ihigh); //верхняя точка
                           level0=iHigh(m_name,etf,ihigh);

                          

Показано первые 50% кода (символов: 26809)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
2 680,90₽ ≈ $32.77