English 中文 Español Deutsch 日本語 Português
preview
Разработка торгового советника с нуля (Часть 29): Говорящая платформа

Разработка торгового советника с нуля (Часть 29): Говорящая платформа

MetaTrader 5Примеры | 19 октября 2022, 10:02
1 768 2
Daniel Jose
Daniel Jose

1.0 - Введение

А что если мы сделаем советника более веселым? Торговля на финансовых рынках часто является чрезвычайно скучным и монотонным занятием, но мы можем сделать эту работу менее утомительной. Этот проект может стать опасным, если у вас есть проблема, делающая вас зависимым, но на самом деле весь сценарий с модификациями может быть более увлекательным и менее скучным.

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

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

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


2.0 - Удаление Chart Trade

Chart Trade - это то, что всё еще имеет смысл в менее сложной системе ордеров, чем та, которая есть в нашем советнике. Но в случае с разрабатываемым советником и на текущем этапе развития, не имеет смысла иметь Chart Trade на графике, поэтому мы можем его убрать. При желании вы можете сохранить его, и это можно сделать, только отредактировав одну команду. Да, именно так, я люблю, чтобы всё было очень просто, чтобы потом я мог изменить (или не изменять) это очень быстро и не создавая никакого стресса в процессе работы, например, как в тех случаях, когда возникают проблемы с кодом или происходят катастрофические сбои в критических точках.

Для того, чтобы управление происходило как можно быстрее и в то же время безопасно, в код советника мы добавили следующее определение:

#define def_INTEGRATION_CHART_TRADER            // Интеграция chart trader с ЕА ...

Если это определение не существует или становится комментарием, то CHART TRADE не будет составлен вместе с советником. Давайте рассмотрим пункты, затрагиваемые этим определением. Первый и самый очевидный из них включает следующее:

#ifdef def_INTEGRATION_CHART_TRADER
        #include <NanoEA-SIMD\SubWindow\C_TemplateChart.mqh>
#endif 

Хотя приведенный выше код находится не в файле советника, а в файле C_IndicatorTradeView.mqh, для компилятора определение будет видно везде в коде, поэтому нам не нужно беспокоиться о корректировке кода. Для этого мы просто создаем определение в легкодоступном месте, в данном случае в коде советника, и используем его там, где это будет необходимо.

Но продолжим немного внутри файла C_IndicatorTradeView.mqh. Поскольку мы можем скомпилировать советника без Chart Trade, нам необходимо организовать доступ к данным, определенным в окне сообщений об инициализации советника, которое можно увидеть на следующем изображении:

Помните, что нам нужно получить доступ к этим данным. Мы передавали их в Chart Trade, и когда возникала необходимость к ним обратиться, мы заходили в Chart Trade и получали их. Но теперь без Chart Trade нам придется идти иным путем, чтобы получить доступ к тем же данным.

В файле C_IndicatorTradeView.mqh эти значения нужны только в одном месте - когда мы создаем индикатор 0, который используется, чтобы показать, где будет расположен отложенный ордер. Это место находится внутри функции DispatchMessage, и его можно увидеть во фрагменте ниже:

// ... Предыдущий код ...

                                        case CHARTEVENT_MOUSE_MOVE:
                                                Mouse.GetPositionDP(dt, price);
                                                mKeys   = Mouse.GetButtonStatus();
                                                bEClick  = (mKeys & 0x01) == 0x01;    //Клик левой кнопкой
                                                bKeyBuy  = (mKeys & 0x04) == 0x04;    //Нажатый SHIFT
                                                bKeySell = (mKeys & 0x08) == 0x08;    //Нажатый CTRL
                                                if (bKeyBuy != bKeySell)
                                                {
                                                        if (!bMounting)
                                                        {
#ifdef def_INTEGRATION_CHART_TRADER
                                                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
#else 
                                                                m_Selection.vol = EA_user20 * Terminal.GetVolumeMinimal();
                                                                valueTp = EA_user21;
                                                                valueSl = EA_user22;
                                                                m_Selection.bIsDayTrade = EA_user23;
#endif 
                                                                valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                m_Selection.it = IT_PENDING;
                                                                m_Selection.pr = price;
                                                        }

// ... Остальной код ...

Обратите внимание на выделенные строки, нет смысла искать эти значения EA_userXX внутри этого файла, их здесь нет, потому что они приходят из кода советника и их можно увидеть во фрагменте ниже:

#ifdef def_INTEGRATION_CHART_TRADER
        input group "Chart Trader"
#else 
        input group "Base Operacional do EA"
#endif 
input int       EA_user20   = 1;     //Коэффицент плеча
input double    EA_user21   = 100;   //Take Profit ( ФИНАНСОВЫЙ )
input double    EA_user22   = 81.74; //Stop Loss ( ФИНАНСОВЫЙ )
input bool      EA_user23   = true;  //Day Trade ?

С помощью этого у нас уже есть контроль так, как это было бы сделано, если бы Chart Trade находился на графике. Обратите внимание, что мы практически ничего не меняем в коде, мы просто перемещаем в нужное место необходимые данные, определенные пользователем. Некоторые люди могут посчитать эту настройку ненужной для трейдера во время загрузки советника, и это в некотором смысле верно, поскольку система ордеров позволяет нам настраивать все переменные без каких-либо трудностей. Поэтому вы можете установить минимальное значение в качестве коэффициента плеча и оставить СТОП и ТЕЙК на 0, и такие начальные операции, как Day Trade, и сделать это в функции DispatchMessage класса C_IndicatorTradeView, так как это вообще не повлияет на систему, поскольку трейдер может изменить ордер, находящийся на графике, а затем отправить его на сервер. Следует отметить, что данный вид модификации остается на усмотрение трейдера, так как является очень индивидуальным.


2.0.1 - Корректируем некоторые вещи

Прежде чем мы вернемся к той части, в которой удаляем Chart Trade, нам нужно сделать еще одну вещь, которая улучшит стабильность советника в целом.

Сделаем следующее. В классе C_IndicatorTradeView определим частную структуру данных, которую можно увидеть ниже:

struct st01
{
        bool    ExistOpenPosition,
                SystemInitilized;
}m_InfoSystem;

Она должна быть инициализирована в следующем коде:

void Initilize(void)
{
        static int ot = 0, pt = 0;
                                
        m_InfoSystem.ExistOpenPosition = false;
        m_InfoSystem.SystemInitilized = false;
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);                             
        if ((ot != OrdersTotal()) || (pt != PositionsTotal()))
        {
                ObjectsDeleteAll(Terminal.Get_ID(), def_NameObjectsTrade);
                ChartRedraw();
                for (int c0 = ot = OrdersTotal(); c0 >= 0; c0--)  IndicatorAdd(OrderGetTicket(c0));
                for (int c0 = pt = PositionsTotal(); c0 >= 0; c0--) IndicatorAdd(PositionGetTicket(c0));
        }
        m_InfoSystem.SystemInitilized = true;
}

Но почему мы создаем и инициализируем эти данные здесь?! Помните, что MetaTrader 5 посылает советнику СОБЫТИЯ, а одним из этих событий является OnTick. В простых системах проблем не так много, но по мере того, как система становится всё сложнее, мы должны убедиться, что всё работает правильно. И может случиться так, что MetaTrader 5 посылает советнику события до того, как сам советник будет готов к обработке этих событий. По этой причине мы должны убедиться в том, что советник готов, создав определенные переменные, которые могут указать на уровень функционирования самого советника, и если советник не полностью готов, то события, запускаемые MetaTrader 5, следует игнорировать до тех пор, пока советник не сможет адекватно реагировать на указанные события.

Самый критический момент можно увидеть в приведенном ниже коде:

inline double SecureChannelPosition(void)
                        {
                                static int nPositions = 0;
                                double Res = 0;
                                ulong ticket;
                                int iPos = PositionsTotal();
                                
                                if (!m_InfoSystem.SystemInitilized) return 0;
                                if ((iPos != nPositions) || (m_InfoSystem.ExistOpenPosition))
                                {
                                        m_InfoSystem.ExistOpenPosition = false;
                                        for (int i0 = iPos - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
                                        {
                                                m_InfoSystem.ExistOpenPosition = true;
                                                ticket = PositionGetInteger(POSITION_TICKET);
                                                if (iPos != nPositions) IndicatorAdd(ticket);
                                                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), Res += PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                                        }
                                        nPositions = iPos;
                                }
                                return Res;
                        };

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

Этот вопрос тестирования всего и всех возможностей довольно сложен, поскольку множество тестов помогает сделать систему очень стабильной, но это также может усложнить обслуживание или изменение кода. Некоторые из них должны быть выполнены в определенной логической последовательности, чтобы быть эффективными, а это довольно дорогостоящее занятие.

Но, как видите, проверяя, есть ли позиция в активе, который отслеживает советник, мы можем придать ему некоторую динамичность, тем более, когда в MetaTrader 5 есть несколько открытых активов, а в каждом из них есть определенное количество позиций, которые будут отображаться в советнике. Отфильтровав это здесь, мы устраняем цикл, чтобы советник выполнял код только в том случае, когда это действительно необходимо, в противном случае мы немного сокращаем время обработки, потребляемое советником, это не долго, но в очень экстремальных случаях может иметь большое значение.


2.0.2 - "Удаление" Chart Trade из советника

Теперь, когда мы внесли изменения в класс C_IndicatorTradeView, мы можем опять сконцентрироваться на коде советника и удалить из него Chart Trade. Первое, что нужно сделать - это удалить его из кода OnInit, далее можно увидеть, как это сделать:

int OnInit()
{       
        Terminal.Init();

#ifdef def_INTEGRATION_WITH_EA
        WallPaper.Init(user10, user12, user11);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        EventSetTimer(1);
#endif 

        Mouse.Init(user50, user51, user52);
        
#ifdef def_INTEGRATION_CHART_TRADER
        static string   memSzUser01 = "";
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(EA_user20 * Terminal.GetVolumeMinimal(), EA_user21, EA_user22, EA_user23);
        TradeView.Initilize();
        OnTrade();
#else 
        TradeView.Initilize();
#endif 
   
        return INIT_SUCCEEDED;
}

Весь код зеленого цвета будет заменен кодами синего цвета. Если мы не используем Chart Trade, то может показаться, что разница невелика, что это просто изменение размера исполняемого файла. Но дело не только в этом. Обратите внимание, что вместе с кодом Chart Trade мы также удалили событие OnTrade, и теперь это событие больше не будет нужно советнику, он не будет обрабатывать это событие.

А вы, может, подумаете, что со мной не всё в порядке. Как можно удалить событие OnTrade из советника? И как мы теперь будем обрабатывать торговые события? Эти события будут обрабатываться событием OnTradeTransaction, и будут обрабатываться более эффективным способом, чем событие OnTrade, и это означает, что советник будет более простым и, следовательно, более надежным.

Другой момент, который также претерпевает изменения - это функция, которую можно увидеть чуть ниже:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
#ifdef def_INTEGRATION_WITH_EA
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        VolumeAtPrice.DispatchMessage(id, sparam);
#endif 

#ifdef def_INTEGRATION_CHART_TRADER
        Chart.DispatchMessage(id, lparam, dparam, sparam);
#endif 

        TradeView.DispatchMessage(id, lparam, dparam, sparam);  
}

В случае отсутствия интеграции внутри советника, единственными строками, которые будут скомпилированы, станут выделенные строки. Поскольку эти события имеют тенденцию быть довольно постоянными (чем эффективнее обработка этих событий, тем лучше), они часто конкурируют с другим событием, которое MetaTrader 5 запускает для обработки советником. Это другое событие показано ниже:

void OnTick()
{
#ifdef def_INTEGRATION_CHART_TRADER
        Chart.DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, TradeView.SecureChannelPosition(), C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT]);
#else 
        TradeView.SecureChannelPosition();
#endif 

#ifdef def_INTEGRATION_WITH_EA
        TimesAndTrade.Update();
#endif 
}

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

Хорошо, после всех этих изменений, Chart Trade не будет интегрирован в советник. Конечно, мы можем превратить Chart Trade в индикатор, и это будет иметь некоторые преимущества, при этом система советника будет сосредоточена только на своей основной работе: обработке, позиционировании и поддержании системы ордеров в рамках правильного управления. Хотя перенос Chart Trade в индикатор предполагает некоторые дополнительные изменения, я не буду показывать, как это сделать сейчас, этот перенос будет обсуждаться в другом месте. Но правда заключается в том, что у нас есть способ перенести Chart Trade в индикатор и иметь возможность отправлять через него ордера.


3.0 - Добавление оповещений

Часто мы не смотрим на график, но всё равно хотим знать, что происходит в данный момент. Одним из способов оповещения о чем-то является звуковое оповещение, и это один из лучших видов оповещения, потому что он действительно сразу привлекает наше внимание, и зачастую благодаря ему мы уже знаем, как действовать, без необходимости получения какого-либо другого сообщения.

Итак, давайте научимся настраивать несколько основных звуковых оповещений, в некоторых случаях - фразу, которая говорит нам что-то конкретное. Хотя то, что я покажу вам здесь и передам в ваше распоряжение в продолжении и является очень базовым, возможно, это побудит вас увеличить количество этих предупреждений и оповещений, чтобы не тратить время на чтение сообщений, поскольку звук часто может указывать на конкретное событие, и это даст вам выигрыш во времени при определенных торговых моментах. Поверьте мне, это имеет большое значение.

Первое, что нужно сделать - это создать новый файл, который будет содержать новый класс, и который будет поддерживать и изолировать нашу звуковую систему. Как только это будет сделано, мы сможем начать производить вещи очень стабильным способом. Создаваемый класс можно полностью увидеть в следующем коде:

class C_Sounds
{
        protected:
                enum eTypeSound {TRADE_ALLOWED, OPERATION_BEGIN, OPERATION_END};
        public  :
//+------------------------------------------------------------------+
inline bool PlayAlert(const int arg)
                {
                        return PlaySound(StringFormat("NanoEA-SIMD\\RET_CODE\\%d.wav", arg));
                }
//+------------------------------------------------------------------+
inline bool PlayAlert(const eTypeSound arg)
                {
                        string str1;
        
                        switch (arg)
                        {
                                case TRADE_ALLOWED   : str1 = def_Sound00; break;
                                case OPERATION_BEGIN : str1 = def_Sound01; break;
                                case OPERATION_END   : str1 = def_Sound02; break;
                                defaultreturn false;
                        }
                        PlaySound("::" + str1);
                        
                        return true;
                }
//+------------------------------------------------------------------+
};

Несмотря на чрезвычайную простоту этого кода, в нем есть кое-что интересное. Обратите внимание, что мы переписываем функцию PlayAlert, поэтому у нас есть две версии одной и той же функции. Но почему так? Причина в том, что мы должны иметь эти две вариации из-за того, как будет работать наша звуковая система. В случае с первой версией функции мы будем воспроизводить звук, который находится в файле, а во второй версии мы будем воспроизводить звук, который является частью советника, являясь его функцией. Однако есть одна вещь, которую многие не умеют делать - это воспроизведение звуков непосредственно из аудиофайлов. Но не волнуйтесь, я покажу вам, как это сделать. Причина первой версии в том, что некоторые люди могут захотеть поставить свой собственный голос или другой звук в качестве оповещения, и изменить его в любое время, и для этого вам не нужно перекомпилировать советника, фактически вы можете изменить эти звуки даже при запущенном советнике внутри MetaTrader 5. С того момента, когда звук должен быть воспроизведен, советник будет использовать последнюю версию, которую вы поставили, так что вам нужно только изменить один аудиофайл на другой, и советник даже не заметит разницы, это очень помогает. Но есть и другая причина, которую вы заметите, когда посмотрите на код советника.

На самом деле, первый вариант используется в довольно специфическом месте, что можно увидеть во фрагменте класса, выделенном ниже:

class C_Router
{
        protected:
        private  :
                MqlTradeRequest TradeRequest;
                MqlTradeResult  TradeResult;
//+------------------------------------------------------------------+
inline bool Send(void)
        {
                if (!OrderSend(TradeRequest, TradeResult))
                {
                        if (!Sound.PlayAlert(TradeResult.retcode))Terminal.MsgError(C_Terminal::FAILED_ORDER, StringFormat("Error Number: %d", TradeResult.retcode));
                        
                        return false;
                }
                return true;
        }

// ... Остальной код класса ....

Только представьте себе объем работы, который потребуется для обработки всех возможных ошибок, возвращаемых торговым сервером, это было бы почти безумием. Но, записав аудиофайл и назвав его так, чтобы отразить значение, которое будет возвращать торговый сервер, советник будет знать, какой файл воспроизводить, что значительно упрощает нашу жизнь, так как нам нужно будет только указать, какой это файл, исходя из возвращаемого сервером значения, а советник будет искать и воспроизводить этот же файл, выдавая нам звуковое предупреждение или голосовое сообщение, чтобы мы точно знали, что произошло. Замечательно, не правда ли?! Теперь, чтобы отклонить ордера, платформа будет очень четко информировать нас о том, что произошло или если что-то идет не так. И он будет делать это очень уместным образом, особым для нас - эксклюзивным и уникальным для нашего способа торговли и действий на рынках. Мы увидим, насколько повысится наша оперативность, потому что в одном и том же аудиофайле мы сможем наглядно показать, как решить проблему.

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

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound.PlayAlert(C_Sounds::TRADE_ALLOWED);
                return INIT_FAILED;
        }
        
        Terminal.Init();

// ... Остальной код ...

Этот код проверяет, включен или нет AlgoTrading на платформе, и в случае если мы забыли включить его, советник сообщит нам, что он не будет доступен для торговли. Чтобы проверить, включен он или нет, достаточно посмотреть на индикатор на платформе, как показано на следующем изображении:


Второй момент, когда мы будем использовать вспомогательный звук, можно увидеть в следующем фрагменте:

void CreateIndicator(ulong ticket, eIndicatorTrade it)
{
        string sz0;
                                
        switch (it)
        {
                case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                case IT_PENDING:
                        macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit);
                        m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled);
                        m_BtnCheck.SetStateButton(sz0, true);
                        macroInfoBase(IT_PENDING);
                        break;
                case IT_RESULT  :
                        macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult);
                        macroInfoBase(IT_RESULT);
                        Sound.PlayAlert(C_Sounds::OPERATION_BEGIN);
                        m_InfoSystem.ExistOpenPosition = true;
                        break;
        }
        m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
}

Каждый раз, когда создается индикатор позиции, будет воспроизводиться звук. Это значительно облегчит нам жизнь, поскольку мы будем знать, что отложенный ордер стал позицией, и что нам нужно начать обращать на него внимание.

Третья и последняя точка, где мы имеем вспомогательный звук - это когда позиция закрывается по какой-либо причине. Это делается в очень специфическом месте, которое представляет собой приведенный ниже код:

inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL)
{
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT))
        {
                if (macroGetPrice(ticket, IT_RESULT, EV_LINE) > 0) Sound.PlayAlert(C_Sounds::OPERATION_END);
                ObjectsDeleteAll(Terminal.Get_ID(), StringFormat("%s%c%llu%c", def_NameObjectsTrade, def_SeparatorInfo, ticket, (ticket > 1 ? '*' : def_SeparatorInfo)));
        } else ObjectsDeleteAll(Terminal.Get_ID(), StringFormat("%s%c%llu%c%c", def_NameObjectsTrade, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)it));
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
        m_Selection.ticket = 0;
        Mouse.Show();
        ChartRedraw();
}

Вы можете подумать, что, удалив советника, он издаст звук закрытия, но нет, этого не произойдет, потому что ценовая линия всё еще будет присутствовать в системе. Но когда позиция будет закрыта, произойдет нечто иное, и ценовая линия позиции окажется на ценовой позиции 0, и в этот момент раздастся звук, указывающий на то, что позиция закрылась.

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

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

В прилагаемом коде у нас будет папка под названием SOUNDS. Не имеет смысла оставлять эту папку в той же папке, где будет находиться код, потому что звуки, содержащиеся в этой папке, не будут воспроизводиться. Вам нужно переместить ее в другое место, и это место можно легко найти, но если вы не знаете, где оно находится, не волнуйтесь - оно будет вместе с остальным кодом:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
void OnStart()
{
        Print(TerminalInfoString(TERMINAL_PATH));
}
//+------------------------------------------------------------------+

При запуске этого скрипта вы получите информацию на панели инструментов, которая указывает на местоположение, которое мы будем использовать. Пример результата выполнения можно увидеть на изображении ниже:

Сделать нужно следующее:

  1. Открыть приложение к статье;
  2. Открыть File Explorer;
  3. Зайти в папку, указанную на изображении выше;
  4. Скопировать содержимое папки SOUNDS из прикрепленного файла в указанную выше папку;
  5. При желании можно удалить 3 файла (WARRING, BEGIN, END), потому что они будут скомпилированы вместе с советником;
  6. При желании можно изменить содержимое файлов .WAV на что-то более приятное, но важно оставить его с тем же именем;
  7. Запустить советник в терминале MetaTrader 5 и наслаждаться!
Но помните, что для того, чтобы звуки (WARRING, BEGIN, END) были скомпилированы в советнике, мы должны иметь в каталоге кода MQL5 папку SOUNDS с этими же звуками, иначе они не интегрируются в код советника.


4.0 - Заключение

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

Приятной деталью является то, что если мы используем те же концепции и идеи, которые предложены здесь, мы можем записывать голосовые сообщения, говорящие или предупреждающие о чем-то. И когда советник или любой другой процесс, выполняемый MetaTrader 5 и использующий звуковую систему, становится активным через триггеры, показанные в статье, вы или другой трейдер получите такое звуковое сообщение, которое сообщит или предупредит вас о каком-то действии, которое вы уже предвидели и которое следует предпринять.

И это не текстовое, а голосовое сообщение, что делает его гораздо более эффективным, так как оно может быстро объяснить пользователям системы, что нужно делать, или какова была причина сообщения.

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


Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/10664

Прикрепленные файлы |
EA_-_h_Parte_29_6.zip (14465.62 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
Alexey Volchanskiy
Alexey Volchanskiy | 20 окт. 2022 в 13:31
Как замечательно, что все на португальском! А я думал, международный язык английский. Даешь статью с японскими иероглифами!
Rashid Umarov
Rashid Umarov | 20 окт. 2022 в 13:39
В статье 2 картинки, что конкретно вам непонятно?
Машинное обучение и Data Science — Нейросети (Часть 01): Разбираем нейронные сети с прямой связью Машинное обучение и Data Science — Нейросети (Часть 01): Разбираем нейронные сети с прямой связью
Многие любят, но немногие понимают все операции, лежащие в основе нейронных сетей. В этой статье я постараюсь простым языком объяснить все, что происходит за закрытыми дверями многоуровневого перцептрона с прямой связью Feed Forward.
Разработка торгового советника с нуля (Часть 28): Навстречу будущему (III) Разработка торгового советника с нуля (Часть 28): Навстречу будущему (III)
Наша система ордеров по-прежнему не справляется с одной задачей, но мы, НАКОНЕЦ, разберемся с этим. На платформе MetaTrader 5 есть система тикетов, которая позволяет нам создавать или корректировать значения ордеров. Кстати, идея состоит в том, чтобы иметь советника, который поможет нам сделать ту же систему тикетов быстрее и эффективнее.
DoEasy. Элементы управления (Часть 23): дорабатываем WinForms-объекты TabControl и SplitContainer DoEasy. Элементы управления (Часть 23): дорабатываем WinForms-объекты TabControl и SplitContainer
В статье добавим новые события мышки относительно границ рабочих областей WinForms-объектов и доработаем некоторые недочёты в работе элементов управления TabControl и SplitContainer.
Разработка торгового советника с нуля (Часть 27): Навстречу будущему (II) Разработка торгового советника с нуля (Часть 27): Навстречу будущему (II)
Давайте перейдем к более полноценной системе ордеров непосредственно на графике. В этой статье я вам покажу способ исправить систему ордеров или, скорее, как сделать её более интуитивно понятной.