Перейти к содержимому

Theme© by Fisana
 

Фотография
* * * * * 1 Голосов

Полезные mql функции


  • Авторизуйтесь для ответа в теме
Сообщений в теме: 91

#76 webic

webic

    Не сидит в окопе

  • Пользователи
  • PipPipPipPipPip
  • 76 сообщений

Отправлено 25 November 2010 - 14:30

Всем здравствуйте!
Великолепный раздел для тех кто хочет писАть сам! Очень много полезного нашёл для себя.
Вопрос к Michelangelo, а возможно ли в принципе написание упоминавшегося Вами ТрейлингПрофита?
С уважением, спасибо!

Возможно.
  • Michelangelo® это нравится

 
 

#77 Night

Night

    Начинающий

  • Пользователи
  • PipPip
  • 4 сообщений

Отправлено 25 November 2010 - 23:31

Возможно.



Изображение Спасибо! Более чем исчерпывающий ответ!Изображение

Действительно возможно, уже нашёл. Вопрос снят.

#78 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 27 November 2010 - 15:25

Учет ордеров в моновалютном советнике

Функция написана по идее, предложенной Сергеем Ковалевым (SK.) в его статье "Учет ордеров в большой программе" (гугл в помощь или через личку сброшу ссылку). Вообще, как мне кажется, это наиболее удобный способ следить со всеми ордерами эксперта + большой функционал + выполняется по-моему гораздо быстрее стандартного перебора ордеров (специальных тестов не устраивал, на глаз :good: ).

Все ордера заносятся в двухмерный массив, затем ордера сортируются по времени открытия: последний (по времени) открытый ордер будет в начале.
/*
order[][] - двухмерный массив для хранения ордеров
sSymbol - торговая пара: "0" - на которой установлен советник
iMagic - магический ордер советника: отрицательное число - любой
*/
int OrdersManagement_Mono(double &order[][], string sSymbol="0",int  iMagic=0)   
   {
      if(sSymbol=="0")sSymbol=Symbol();
      int i=0, k=OrdersTotal()-1,cnt=0;
      int dg=MarketInfo(Symbol(),MODE_DIGITS);
      ArrayInitialize(order,0);
      ArrayResize(order,k+2);
      for(i=k;i>=0;i--) {
         if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) {
            if(OrderSymbol()==sSymbol)   {
               if(OrderMagicNumber()==iMagic || iMagic<0) {
                  cnt++;
                  order[cnt][1]=OrderTicket();
                  order[cnt][2]=OrderType();
                  order[cnt][3]=NormalizeDouble(OrderLots(),2);
                  order[cnt][4]=NormalizeDouble(OrderOpenPrice(),dg);
                  order[cnt][5]=NormalizeDouble(OrderStopLoss(),dg);
                  order[cnt][6]=NormalizeDouble(OrderTakeProfit(),dg);
                   order[cnt][7]=OrderProfit()+OrderCommission()+OrderSwap();
                  order[cnt][8]=OrderMagicNumber();
                  order[cnt][9]=OrderOpenTime();
               }  
            }
         }
      }
      order[0][0]=cnt;
      for(i=1;i<=order[0][0];i++) {
         double tmp[1][10];
         if(order[i][9]<order[i+1][9])  {
            for(int j=1;j<10;j++)   {
               tmp[1][j]=order[i+1][j];
            }
            for(j=1;j<10;j++)   {
               order[i+1][j]=order[i][j];
            }
            for(j=1;j<10;j++)   {
               order[i][j]=tmp[1][j];
            }
         }
      }
   /*   
      for(i=1;i<=order[0][0];i++) {
         Print("order["+i+"][9]="+order[i][9]);
      }
   */
    return(cnt);  
   }
   

Пользоваться можно так:

  int start() 
  {
     double Orders[10][10];
     OrdersManagement_Mono(Orders,"0",MagicNumber);
  }
А затем если нужно найти к примеру последнюю цену открытия, то:
double dLastOpenPrice(double order[][],int direction=-1) 
  {
     for(int i=1;i<=order[0][0];i++)  {
        int TY=order[i][2];
        if(TY==direction || direction==-1) {
           return(order[i][4]); 
        }
     }
   return(-1.0);  
  }
где order[][] - массив с ордерами советника, а direction - тип сделки (OP_BUY, OP_SELL и т.д.), -1 - любой
  • Sema и webic это нравится
Каждый сам кузнец своей судьбы.

#79 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 19 December 2010 - 18:06

Очередная интересная штуковина :thumbsup: .

Блок функций для определения оптимального лота путем анализа двух последних серий сделок (количество сделок в серии задается во внешних параметрах).
Идея не моя, где-то на просторах инета когда-то видел, вот решил написать. Суть следующая:
  • строим простую скользящую среднюю по N последним прибылям в пунктах сделок какого-либо типа: или на покупку, или на продажу(т.е. цена открытия сделки-цена закрытия сделки, по модулю);
  • строим простую скользящую среднюю по M последним прибылям в пунктах какого-либо типа: или на покупку, или на продажу (т.е. цена открытия сделки-цена закрытия сделки, по модулю);
  • сравниваем полученные значения: если первая средняя выше второй, значит тренд по линии баланса вверх, и работаем обычным лотом :); в противном случае работаем минимально разрешенным лотом, снижая риск.
Очень неплохие результаты показывает для трендовых систем.
extern int           Ma1               =  10             ;           //период первой средней по истории сделок;
extern int           Ma2               =  20             ;           //период второй средней по истории сделок;


bool AboveMaProfit(int ty)
{
   double History[10][12];
   HistoryOrdersManagement_Mono(History,Symbol(),MagicNumber);
   
   double tmp=0.0;
   double tmp_l=0.0;
   double tmp_1=0.0;
   double tmp_l_1=0.0;
   if(History[0][0]<MathMax(Ma1,Ma2)+1) return(true);
   
   int cnt=0;
   int cnt_1=0;
   int TY=-1;
   for(int i=1;i<=History[0][0];i++)  {
      if(cnt==Ma1) break;
      TY=History[i][2];
      if(TY==ty)  {
         tmp+=MathAbs(History[i][4]-History[i][11]);
         //tmp_l+=History[i][3];
//         tmp_l+=History[i][10]-History[i][9];
         cnt++;
      }
   }
//   tmp/=tmp_l;
   
   tmp/=(Ma1);
   cnt_1=0;
   for(i=1;i<=History[0][0];i++)  {
      if(cnt_1==Ma2) break;
      TY=History[i][2];
      if(TY==ty)  {
         tmp_1+=MathAbs(History[i][4]-History[i][11]);
         //tmp_l_1+=History[i][3];
//         tmp_l_1+=History[i][10]-History[i][9];
         cnt_1++;
      }
   }
//   tmp_1/=tmp_l_1;
   tmp_1/=(Ma2);
   
   if(tmp>tmp_1)return(true);
   return(false);
}
int HistoryOrdersManagement_Mono(double &order[][], string sSymbol="0",int iMagic=0)   
{
   if(sSymbol=="0")sSymbol=Symbol();
   int i=0, k=OrdersHistoryTotal()-1,cnt=0;
   int dg=MarketInfo(Symbol(),MODE_DIGITS);
   ArrayInitialize(order,0);
   ArrayResize(order,k+2);
   for(i=k;i>=0;i--) {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) {
         if(OrderSymbol()==sSymbol || sSymbol=="")   {
            if(OrderMagicNumber()==iMagic || iMagic<0) {
               cnt++;
               order[cnt][1]=OrderTicket();
               order[cnt][2]=OrderType();
               order[cnt][3]=NormalizeDouble(OrderLots(),2);
               order[cnt][4]=NormalizeDouble(OrderOpenPrice(),dg);
               order[cnt][5]=NormalizeDouble(OrderStopLoss(),dg);
               order[cnt][6]=NormalizeDouble(OrderTakeProfit(),dg);
               order[cnt][7]=OrderProfit()+OrderCommission()+OrderSwap();
               order[cnt][8]=OrderMagicNumber();
               order[cnt][9]=OrderOpenTime();
               order[cnt][10]=OrderCloseTime();
               order[cnt][11]=OrderClosePrice();
            }  
         }
      }
   }
   order[0][0]=cnt;
   for(i=1;i<=order[0][0];i++) {
      double tmp[1][12];
      if(order[i][10]<order[i+1][10])  {
         for(int j=1;j<10;j++)   {
            tmp[1][j]=order[i+1][j];
         }
         for(j=1;j<10;j++)   {
            order[i+1][j]=order[i][j];
         }
         for(j=1;j<10;j++)   {
            order[i][j]=tmp[1][j];
         }
      }
   }
/*   
   for(i=1;i<=order[0][0];i++) {
      Print("order["+i+"][10]="+order[i][10]);
   }
*/
 return(cnt);  
}

Использовать так:
//здесь расчет лота в вашей торговой системе, результат которого сохраняется в переменную _return

if(!AboveMaProfit(ty)) {Print("Работаем минимальным лотом");_return=MarketInfo(_Symbol,MODE_MINLOT);}
//ty - тип сделки, OP_BUY,OP_SELL
//в OrderSend в качестве лота пишем _return


Как видно по коду, что таким же образом можно использовать и другие фильтры (убрать знаки комментирования // где нужно), в частности я пробовал следующие:
  • прибыль в пунктах делим на суммарный лот по этим же сделкам;
  • прибыль в пунктах делим на суммарное время открытия сделок;
Во всех этих случаях кривая баланса становится более сглаженной, зачастую уменьшается просадка и увеличивается прибыль.
  • BUZINESSZONE и Olej это нравится
Каждый сам кузнец своей судьбы.

#80 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 19 December 2010 - 19:51

Пример использования вышеуказанного фильтра.

Без использования фильтра:
TesterGraph1.gif

После включения фильтра:
TesterGraph2.gif

Используется фиксированный лот 0.1 в обоих вариантах. Тест за два последние года на GBPUSD H1, на этих данных не оптимизировался.

В аттаче советник, используемый индикатор и сет.

Параметры советника:
extern int           TakeProfit        =  0              ;           //тейкпрофит (отрицательное число или ноль чтобы не использовать);
extern int           NumOfBarsSL       =  4              ;           //количества баров, среди которых выбирается экстремум для выставления стоплосса;
extern bool          CheckProfit       =  true           ;           //если true, то используем анализ истории сделок;
extern int           Ma1               =  10             ;           //период первой средней по истории сделок;
extern int           Ma2               =  20             ;           //период второй средней по истории сделок;
extern bool          UseMM             =  true           ;           //при true используется процент от депозита, при false фиксированный лот, задаваемый параметром TradeVolume;
extern double        TradeVolume       =  0.1            ;           //фиксированный лот, используется при UseMM=false;
extern double        RiskPercent       =  5.0            ;           //риск в процентах от депозита, используется при UseMM=true;       

Прикрепленные файлы

  • Прикрепленный файл  MT4.rar   6.59К   102 скачиваний

  • Admin, BUZINESSZONE и Kortizon это нравится
Каждый сам кузнец своей судьбы.

#81 BUZINESSZONE

BUZINESSZONE

    Рвется в бой

  • Свой человек
  • PipPipPipPipPipPip
  • 130 сообщений

Отправлено 19 December 2010 - 20:56

Пример использования вышеуказанного фильтра.

Спасибо большое за Грааль.

#82 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 05 January 2011 - 02:05

Учет ордеров в моновалютном советнике


Продолжение серии функций учета ордеров. На этот раз для мультивалютного советника. Функция написана по все той же статье Сергея Ковалева, в данной реализации также используется массив для хранения ордеров, но уже трехмерный.

 /*
 order - трехмерный массив, например, Orders[1][100][10]; 1-ое измерение - торговый символ (номер), 2 - количество ордеров на одной паре, 3 - для параметров ордера;
 Sym - массив с названиями торговых инструментов, расчет начинается с единицы;
 mn - магический номер ордеров;
 */
 void OrdersManagement(double &order[][][],string Sym[],int mn) 
 {
    int k=OrdersTotal()-1;
    ArrayInitialize(order,0);
    ArrayResize(order,ArraySize(Sym));
 
    for(int i=k;i>=0;i--)   {
   	if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) {
          if(OrderMagicNumber()==mn || mn<0)  {
         	string symb=OrderSymbol();
         	bool found=false;
 
         	for(int j=1;j<ArraySize(Sym);j++)   {
                if(symb==Sym[j])  {
               	found=true;
               	break;
                }      
         	}
 
         	if(!found)continue; 
         	order[0][0][0]++;      
         	order[j][0][0]++;      
         	int k0=order[j][0][0];
         	int Digits_=MarketInfo(Sym[j],MODE_DIGITS);
 
         	order[j][k0][1]=OrderTicket();
         	order[j][k0][2]=OrderType();
         	order[j][k0][3]=NormalizeDouble(OrderLots(),2);
         	order[j][k0][4]=NormalizeDouble(OrderOpenPrice(),Digits_);
         	order[j][k0][5]=NormalizeDouble(OrderStopLoss(),Digits_);
         	order[j][k0][6]=NormalizeDouble(OrderTakeProfit(),Digits_);
         	order[j][k0][7]=OrderProfit()+OrderCommission()+OrderSwap();
         	order[j][k0][8]=OrderMagicNumber();
         	order[j][k0][9]=OrderOpenTime();  
          }
   	}
    }
    return;
 }

Так же заносим ордера в массив, затем сортируем, чтобы по каждому инструменту последние открытые ордера были на первых позициях.

Как пользоваться:
 double Orders[1][100][10];
    string Symbols[]={"1","EURUSD","GBPUSD","AUDUSD"};
    int sz=ArraySize(Symbols);
    ArrayResize(Orders,sz);
 
    OrdersManagement(Orders,Symbols,0); 
   
    for(int i=1;i<sz;i++)  {    
        if(Orders[i][0][0]<1)   {
            Print("нету открытых ордеров на паре "+Symbols[i]);
   	}  
    }

Каждый сам кузнец своей судьбы.

#83 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 16 February 2011 - 18:08

Блок функций для определения оптимального лота путем анализа двух последних серий сделок (количество сделок в серии задается во внешних параметрах).


Как видно по коду, что таким же образом можно использовать и другие фильтры (убрать знаки комментирования // где нужно), в частности я пробовал следующие:

  • прибыль в пунктах делим на суммарный лот по этим же сделкам;
  • прибыль в пунктах делим на суммарное время открытия сделок;
Во всех этих случаях кривая баланса становится более сглаженной, зачастую уменьшается просадка и увеличивается прибыль.


Заинтересовала меня вот эта штука... : 1). сама по себе идея оригинальная, красивая + 2). это впрямую ответ (или база для такого ответа) на вопрос, который я задавал в теме: экспромт на тему ММ , на который никто так и не ответил, а тут - на тебе :)

Только вот так, впраямую, использовать, как предлагалось в тексте - не получится, потому как оно просто не откомпилируется, из-за переменной MagicNumber , появляющейся в 1-й же исполнимой строке, которая никак не объявлена...

Что оно такое и как поправить - всё понятно ... но удивительно: неужели никто не использует и не проверяет то, что пишется в этой премного таки интересной теме?

:)

#84 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 16 February 2011 - 18:33

Только вот так, впраямую, использовать, как предлагалось в тексте - не получится, потому как оно просто не откомпилируется, из-за переменной MagicNumber , появляющейся в 1-й же исполнимой строке, которая никак не объявлена...


Думаю любой, кто хочет сам написать советник, додумается (к тому же после того как MetaEditor выдаст мол, параметр MagicNumber не определен) добавить одну строчку:
int MagicNumber=1234567890;
:) Проблемы здесь не вижу, надо же чтобы человек хоть что-то сам сделал, а не только скопипастил код, ничего не понимая в нем :) .

По использованию. Что-то отбрасывается, что-то дорабатывается и используется в дальнейшем, но не всегда есть возможность выкладывать какие-либо результаты. В вашей теме я давал примерные варианты как можно поступить, если этот способ нравится больше - пожалуйста :) . Применительно к способу - оптимальным был вариант с использованием динамических периодов скользящих средних, но к чему их привязать - у меня вариантов нету.
Каждый сам кузнец своей судьбы.

#85 Olej

Olej

    Почётный житель форума

  • Свой человек
  • PipPipPipPipPipPipPipPipPipPip
  • 411 сообщений

Отправлено 16 February 2011 - 19:31

Думаю любой, кто хочет сам написать советник, додумается (к тому же после того как MetaEditor выдаст мол, параметр MagicNumber не определен) добавить одну строчку:

 int MagicNumber=1234567890;
:) Проблемы здесь не вижу, надо же чтобы человек хоть что-то сам сделал, а не только скопипастил код, ничего не понимая в нем :) .


Я не к тому, что это нужно писать так или иначе... Но я бы считал, что где-то как-то предупредить бы надо, тем более, что по разным страницам темы довольно много по примерам таких глобальных переменных необъяснённых разбросано ... уровни лосов, профитов и т.д.

Но я к тому, что меня сильно удивило, что за прошедшее время никто не проверил, не использовал, не написал замечание... Это к вопросу оценки продуктивности писания на форумах о подобных вопросах - мысли себе вслух.

#86 Старик

Старик

    Расстрелял целый магазин

  • Пользователи
  • PipPipPipPip
  • 45 сообщений

Отправлено 25 December 2011 - 00:40

Я не к тому, что это нужно писать так или иначе... Но я бы считал, что где-то как-то предупредить бы надо, тем более, что по разным страницам темы довольно много по примерам таких глобальных переменных необъяснённых разбросано ... уровни лосов, профитов и т.д.

Но я к тому, что меня сильно удивило, что за прошедшее время никто не проверил, не использовал, не написал замечание... Это к вопросу оценки продуктивности писания на форумах о подобных вопросах - мысли себе вслух.

Ветка по любому полезная, много хорошего кода - просто качественный учебный материал и полезный по любому.
И мысли интересные в последней части, очень.
Wizard и Necron респект!

Достойный Night, если память не изменяет, классный программист с форума МТ5, конечно, не мог не постебаться чуток...
Но если умные и ядовитые программисты перестанут стебаться, то кто ж тогда будет?!...

С Olej согласен - используемые в функциях внешние переменные все же желательно описать.
Можно в отдельном посте их перечислить - и сказать, что где встретятся, то они везде вот такие.
А то как бы код с загадками, а это неправильно: коды и загадки - отдельно.

Кстати, Olej, зря вы насчет неполезности работы на форуме...
Форум хороший, люди и мысли интересные, а я так вообще ваш фанат, у меня даже небольшой ваш цитатник есть и папочка в компе - у вас очень сильная профессиональная школа за плечами, я вижу.

Что касается трала с убегающим ТП от Michelangelo®, то, как по мне, он мог бы быть весьма к месту в мартинах.
Там игры плохие с тралами, в просадке на 8 колене...
Но и на 8 колене надо пытаться отжать профит и, при достижении курсом уровня сворачивания сетки, как раз отодвинуть ТП и подпереть СЛ выглядит вполне обоснованным - и тралить парой ТП/СЛ потихоньку. Интересные очень цифры на выходе могут получится, если такое реализовать.
Да и от перерывов в связи, сбоев компа подстраховаться явными серверными ТП/СЛ в мартинах лишним никак не будет.

Но и не только в мартинах отбегающий ТП может быть интересен.
Скажем, при затухании импульса часто бывает последняя длинная свеча м1, которая может достать зазевавшийся отбегающий ТП.
На последней свече отскок быстрый часто, стоповый трал на такой экстремум не успеет, лишь отскок на много пипсов хуже поймает.
А вот все менее отбегающий ТП последнюю свечу затухающего импульса принять на себя может - только переменный шаг трала ТП надо как-то вычислить.

Есть в идее отбегающего ТП что-то, точно есть.
  • Ira это нравится

#87 Goodman2

Goodman2

    Расстрелял целый магазин

  • Пользователи
  • PipPipPipPip
  • 58 сообщений

Отправлено 09 April 2012 - 20:29

по статье в ФТ
Торговая стратегия RSI-Profit – простота и точность
кусок кода из ф-ии ММ по машкам от баланса

order[0][0]=cnt;
   for(i=1;i<=order[0][0];i++) {
      double tmp[1][12];
      if(order[i][10]<order[i+1][10])  {
     	for(int j=1;j<10;j++)   {
            tmp[1][j]=order[i+1][j];
     	}
     	for(j=1;j<10;j++)   {
            order[i+1][j]=order[i][j];
     	}
     	for(j=1;j<10;j++)   {
            order[i][j]=tmp[1][j];
     	}
      }
   }

как понял, здесь сортировка для того чтобы упорядочить массив по времени закрытия ордера

может я не вижу толком от недосыпа но мне кажется сортировка здесь не получится, мало перемещений элементов. Только по одному разу меняются соседние элементы. В результате только один элемент переместится в начало или в конец. Остальные останутся неупорядоченными. Или я неправ?

И еще.

На каждом тике перебор всей истории с записью в массив - не сильно замедлит тесты? Может есть смысл только последний закрытый ордер добавлять каждый раз в массив? И, допустим, не на каждом тике, а после закрытия очередного ордера.

Или же эти операции быстрые и не стоит париться?




а за функции эти огромный респект! :thumbsup:

#88 Goodman2

Goodman2

    Расстрелял целый магазин

  • Пользователи
  • PipPipPipPip
  • 58 сообщений

Отправлено 12 April 2012 - 12:45

Еще есть момент

начинает наша мм работать только когда наберется сделок = SlowMA. А это может быть например 50

и это бывает очень не гут

надо думать как сделать чтобы последние 50 сделок из тестера переносить в сову чтобы мм начинал сработать СРАЗУ



#89 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 12 April 2012 - 13:59

можно без сортировки - насколько знаю, сейчас ордера при выборе в цикле через OrderSelect() сортируются автоматически по времени закрытия, поэтому тот кусок кода можно вообще удалить.

На каждом тике перебор всей истории с записью в массив - не сильно замедлит тесты? Может есть смысл только последний закрытый ордер добавлять каждый раз в массив?

Тот код я писал для проверки идеи, цель "сделать оптимальный алгоритм" перед собой не ставил. Можно и только последний добавлять в массив.
Лично я для себя делаю советники только по ценам открытия - не верю в моделирование тестера, особенно учитывая "высокое качество котировок" в наших дц и "небольшие дыры" в истории на два-три месяца на минутном графике - поэтому для меня более-менее нормальная производительность функций, хотя их можно и улучшить.

И, допустим, не на каждом тике, а после закрытия очередного ордера.

Закрытие ордера еще нужно отследить, а это нужно делать на каждом тике(если советник не по ценам открытия), поэтому без тестирования производительности, к сожалению, нельзя сказать, какой вариант будет быстрее работать.

надо думать как сделать чтобы последние 50 сделок из тестера переносить в сову чтобы мм начинал сработать СРАЗУ

чтение/запись файлов в помощь ;) добавляешь в deinit() советника функцию, которая будет сохранять текущую историю ордеров, прогоняешь в тестере, потом добавляешь в init() функцию чтения файла и запись данных из него в массив ордеров. Этот массив потом дополняешь новыми значениями ордеров. Или вынести в настройки параметры вроде LoadHistory и WriteHistory, чтобы код постоянно не изменять.
Каждый сам кузнец своей судьбы.

#90 Necron

Necron

    Пошёл в рукопашку

  • Специалист
  • PipPipPipPipPipPipPipPipPipPipPip
  • 683 сообщений

Отправлено 12 April 2012 - 14:03

Goodman2, возможно вам будет интересен еще один метод управления капиталом - Нейронная сеть по истории счета - там есть как описание работы, так и результаты тестирования + код. Лично мне вариант с нейронной сетью нравится больше.
Каждый сам кузнец своей судьбы.



Copyright © 2024 Your Company Name