preview
Desenvolvendo um sistema de Replay (Parte 50): Complicando as coisas (II)

Desenvolvendo um sistema de Replay (Parte 50): Complicando as coisas (II)

MetaTrader 5Exemplos | 23 maio 2024, 10:23
240 1
Daniel Jose
Daniel Jose

Introdução

No artigo anterior Desenvolvendo um sistema de Replay (Parte 49): Complicando as coisas (I), começamos a complicar um pouco mais as coisas dentro do sistema de replay / simulação. Esta complicação, não que seja proposital, tem como finalidade fazer com que o sistema ganhe em estabilidade e segurança. Isto no que tange as possibilidades de poder ser utilizado dentro de um modelo completamente modular.

Por mais que tais mudanças, a princípio, parecem completamente desnecessárias, as mesmas nos permitem evitar que o usuário venha a fazer mal uso de algumas das coisas que estão sendo desenvolvidas e necessárias apenas e somente para o sistema de replay / simulação. Livrando assim de o usuário, menos experiente venha por ventura a remover, ou descaracterizar as partes que o serviço de replay / simulador precise que estejam no gráfico. Isto no momento que tal serviço se encontra em operação.

No final do artigo passado, informei que existia um problema que tornava o indicador de controle, instável, a ponto de ele fazer, ou permitir que algumas coisas viessem a acontecer, quando na verdade, não deveriam acontecer. Tais problemas, não são graves, ou podem quebrar a plataforma a fazendo travar. Mas pode fazer o serviço funcionar de maneira inesperada em alguns momentos. Por conta disto, não foi possível colocar nenhum anexo no artigo anterior, a fim de que você caro leitor, viesse a ter acesso ao sistema no atual estágio de desenvolvimento.

Mas neste artigo, vamos corrigir, ou tentar corrigir o problema. Pelo menos um deles.

 

Encarando o primeiro problema

O primeiro dos problemas que temos é o fato de que o serviço de replay / simulação, durante todo este tempo, foi implementado de forma a fazer uso de um template. Este iria conter todos os dados necessários, para que o gráfico fosse aberto, e o serviço pudesse executar. 

Apesar desta abordagem ser funcional, e se mostrado bastante atraente por um bom tempo. A mesma trava o usuário. Ou para que você compreenda melhor. O fato de que o serviço, venha a fazer uso de um template especifico, faz com que o usuário deixe de poder usar métodos próprios, ou uma configuração de gráfico, que para ele é adequada. Isto por que é muito mais prático, você colocar todas as coisas necessárias em um template, que você criou ao configurar um gráfico, do que a ter que configurar manualmente o gráfico, cada vez que for operar um dado ativo.

Se você é novo no mercado, talvez isto pareça não ter muito sentido. Mas, operadores com mais experiência, tem em seu arsenal, templates já previamente configurados, a fim de poder fazer uma análise padronizada de um ativo especifico em um momento especifico. Tais templates, muitas das vezes são desenvolvidos ao logo de vários meses, e até mesmo anos, a fim de deixar tudo previamente configurado. Assim tal operador, muitas das vezes, simplesmente salva o template, e quando precisar, ele o repõe no gráfico.

Este tipo de coisa foi mostrado no artigo Desenvolvendo um sistema de Replay (Parte 48): Entendendo e compreendendo alguns conceitos que deu início, a todo este trabalho, de modificação do sistema se replay / simulação. No dito artigo, mostrei como você pode fazer, para configurar, sem fazer uso de um template, e sim de um serviço, um padrão gráfico, de modo a independentemente do template que estivesse em uso, você pudesse ter certas coisas no gráfico. Isto sempre mantido por um serviço que ficaria rodando no MetaTrader 5, a fim de padronizar as coisas.

Mas, no entanto, objetos gráficos, como o que precisamos colocar no gráfico, a fim de permitir que o indicador de controle possa conseguir controlar o serviço. Precisam de um dado importante: Este dado é o ID do gráfico que irá receber tais objetos.

Você pode estar pensando: Mas isto é simples de fazer o indicador conseguir saber: Basta usar a função ChartID() e você fará com que o indicador saiba qual é o ID do gráfico. Ok, não tiro a sua razão. Mas como alguns dizem: A ignorância é uma dadiva. Não me entenda mal. Eu mesmo tive diversas dores de cabeça, para conseguir entender por que as coisas não funcionam de maneira adequada em dados momentos. Então longe de mim dizer que você está errado em pensar assim.

De fato, usar a função ChartID(), irá com toda a certeza retornar o ID do gráfico, a fim de que possamos colocar objetos no gráfico. Lembre-se: Precisamos da ID para dizer ao MetaTrader 5 em qual gráfico o objeto estará vinculado.

Porém, toda via e, entretanto, a função ChartID(), não irá funcionar quando o gráfico é aberto via serviço. Ou seja, quando, o serviço usar a classe C_Replay.mqh, e executar o código presente na linha 183, uma ID diferente será criada. Você pode ver este código no artigo anterior. Nesta mesma linha 183, será executada uma chamada a ChartOpen, a fim de criar o gráfico para comportar o ativo que estaremos fazendo o replay / simulação.

Se você comparar os valores retornados por ChartOpen, presente no serviço, com o valor ChartID presente no indicador de controle, irá notar que os valores serão diferentes. Assim a plataforma MetaTrader 5 não conseguirá saber qual ID utilizar. Se ela fizer uso da ID retornada por ChartID irá lançar os objetos em uma janela errada, ou mesmo inexistente, no entanto se ela usar a ID gerada dentro do serviço, no momento que ChartOpen criar a ID, teremos como fazer uso dos objetos.

Pois bem, agora é que vem o problema: Qual seria a melhor solução a fim de resolver esta questão da ID do gráfico ?!?! Você talvez esteja pensando: Tanto faz, jogue logo o valor de ID que foi informado pela chamada ChartOpen e ponto final. Mas é neste ponto é que mora o problema. Lembra que no artigo anterior removemos a variável global de terminal que era responsável por informar ao indicador de controle a ID do gráfico gerado em ChartOpen ?!?!

No momento em que isto foi feito, o código da classe C_Terminal, passou a tomar conta de procurar a ID do gráfico. E isto é feito fazendo uso da função ChartID. Se você está acompanhando esta sequência de artigos, e conforme vou explicando os códigos, você os está atualizando. O código da classe C_Terminal que você terá é parecido com o que é visto no fragmento abaixo:

59. //+------------------------------------------------------------------+              
60.             C_Terminal(const long id = 0)
61.                     {
62.                             m_Infos.ID = (id == 0 ? ChartID() : id);
63.                             m_Mem.AccountLock = false;
64.                             CurrentSymbol();
65.                             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
66.                             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
67.                             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
68.                             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);
69.                             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
70.                             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
71.                             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
72.                             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
73.                             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
74.                             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
75.                             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
76.                             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
77.                             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
78.                             m_Infos.ChartMode       = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
79.                             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
80.                             ResetLastError();
81.                     }
82. //+------------------------------------------------------------------+
83. 

Fragmento do código fonte de C_Terminal.mqh

Neste código você pode notar que na linha 60, temos o constructor da classe C_Terminal. Este recebe um valor default, ZERO, sendo que desta forma o constructor fica como sendo um constructor padrão. A questão de fato acontece na linha 62, onde ao verificar o valor passado ao constructor, iremos definir qual a ID do gráfico a ser usado. Pois bem, se este valor for o default, a classe C_Terminal irá pedir para o MetaTrader 5 informar qual é a ID do gráfico, passando a usar o valor que está sendo retornado por ChartID. Mas este valor será incorreto quando a chamada deriva do fato de que o serviço criou o gráfico, e lançou o indicador, que fará a chamada a C_Terminal a fim de saber o valor da ID.

Sendo assim, podemos informar ao constructor da classe C_Terminal, o valor da ID, e caso isto seja feito, a chamada a ChartID, será ignorada, e a ID informada ao constructor é que será de fato utilizada pelo indicador.

Mais uma vez, você deve se lembrar que não iremos utilizar mais, uma variável global de terminal, a fim de repassar este valor para o indicador. Até poderíamos fazer isto de forma temporária. Mas a solução que faremos será outra. Iremos passar o valor da ID via parâmetro de chamada do indicador.


Implementando a solução

Talvez você esteja de fato surpreso e confuso com o que iremos fazer. Mas isto se deve ao fato de que no artigo anterior não mostrei como implementar a coisa e como ela estava funcionado antes que a implementação fosse feita. Para facilitar as coisas, veja no vídeo 01 como o sistema estava se comportando. Isto antes que a ID fosse lançada via parâmetro para dentro do indicador de controle.


Vídeo 01

Você pode notar que está sendo gerada uma mensagem de erro, esta mensagem se deve ao fato de que o indicador não consegue saber o ID do gráfico, assim a chamada ChartID, retorna um erro e como o código do indicador verifica isto, e você pode perceber isto ao olhar a linha 25 no código do indicador presente no artigo anterior. Mas olhando aquele código, você irá notar que existe algo diferente entre o código e o vídeo. E de fato existe algo diferente. Mas não se preocupe, você irá em breve ter acesso ao código visto no vídeo, assim as coisas ficarão bem mais autenticas. Mas o fato de existir uma diferença entre o que é visto no vídeo e o código do artigo anterior, é por que naquele momento, eu não estava conseguindo entender, por que o indicador aparecia sendo listado como estando presente no gráfico, mas não aparecia no mesmo.

Foi preciso modificar o código, a fim de que eu viesse a saber por que as coisas não estavam dando certo. Por isto falei que não se sentissem ofendidos ao mencionar que a ignorância é uma dadiva. Pois eu mesmo não estava compreendendo por que o código estava agindo daquela forma.

No entanto, não irei dizer que as coisas foram totalmente corrigidas, pois isto não seria honesto, e nem verdadeiro da minha parte. O fato de que ao passar a ID do gráfico para o indicador, faz com que o indicador seja apresentado. Porém ... Bem, antes de ver este, porém, vamos ver como o código foi modificado a fim de fazer com que as coisas voltassem a funcionar, pelo menos agora o indicador de controle é apresentado no gráfico.

Para fazer isto, não foi preciso modificar o código fonte do serviço, mas foi necessário modificar o código do arquivo de cabeçalho C_Replay.mqh. O arquivo modificado pode ser visto na integra logo abaixo:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. class C_Replay : private C_ConfigService
007. {
008.    private :
009.            long    m_IdReplay;
010.            struct st01
011.            {
012.                    MqlRates Rate[1];
013.                    datetime memDT;
014.            }m_MountBar;
015.            struct st02
016.            {
017.                    bool    bInit;
018.                    double  PointsPerTick;
019.                    MqlTick tick[1];
020.            }m_Infos;
021. //+------------------------------------------------------------------+
022.            void AdjustPositionToReplay(const bool bViewBuider)
023.                    {
024.                            u_Interprocess Info;
025.                            MqlRates       Rate[def_BarsDiary];
026.                            int            iPos, nCount;
027.                            
028.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
029.                            if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
030.                            iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
031.                            Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
032.                            CreateBarInReplay(true);
033.                            if (bViewBuider)
034.                            {
035.                                    Info.s_Infos.isWait = true;
036.                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
037.                            }else
038.                            {
039.                                    for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++);
040.                                    for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
041.                                    nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
042.                            }
043.                            for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false);
044.                            CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount);
045.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
046.                            Info.s_Infos.isWait = false;
047.                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
048.                    }
049. //+------------------------------------------------------------------+
050. inline void CreateBarInReplay(const bool bViewTicks)
051.                    {
052. #define def_Rate m_MountBar.Rate[0]
053. 
054.                            bool    bNew;
055.                            double  dSpread;
056.                            int     iRand = rand();
057.                            
058.                            if (BuildBar1Min(m_ReplayCount, def_Rate, bNew))
059.                            {
060.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
061.                                    if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
062.                                    {                                               
063.                                            dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 );
064.                                            if (m_Infos.tick[0].last > m_Infos.tick[0].ask)
065.                                            {
066.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last;
067.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread;
068.                                            }else   if (m_Infos.tick[0].last < m_Infos.tick[0].bid)
069.                                            {
070.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread;
071.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last;
072.                                            }
073.                                    }
074.                                    if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
075.                                    CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
076.                            }
077.                            m_ReplayCount++;
078. #undef def_Rate
079.                    }
080. //+------------------------------------------------------------------+
081.            void ViewInfos(void)
082.                    {
083.                            MqlRates Rate[1];
084.                            
085.                            ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX);
086.                            ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX);
087.                            ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE);
088.                            m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);
089.                            m_MountBar.Rate[0].time = 0;
090.                            m_Infos.bInit = true;
091.                            CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate);
092.                            if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
093.                                    for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
094.                            if (Rate[0].close > 0)
095.                            {
096.                                    if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else
097.                                    {
098.                                            m_Infos.tick[0].bid = Rate[0].close;
099.                                            m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick);
100.                                    }                                       
101.                                    m_Infos.tick[0].time = Rate[0].time;
102.                                    m_Infos.tick[0].time_msc = Rate[0].time * 1000;
103.                            }else
104.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
105.                            CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
106.                            ChartRedraw(m_IdReplay);
107.                    }
108. //+------------------------------------------------------------------+
109.            void CreateGlobalVariable(const string szName, const double value)
110.                    {
111.                            GlobalVariableDel(szName);
112.                            GlobalVariableTemp(szName);     
113.                            GlobalVariableSet(szName, value);
114.                    }
115. //+------------------------------------------------------------------+
116.            void AddIndicatorControl(void)
117.                    {
118.                            int handle;
119.                       
120.                            handle = iCustom(ChartSymbol(m_IdReplay), ChartPeriod(m_IdReplay), "::" + def_IndicatorControl, m_IdReplay);
121.                            ChartIndicatorAdd(m_IdReplay, 0, handle);
122.                            IndicatorRelease(handle);
123.                    }
124. //+------------------------------------------------------------------+
125.    public  :
126. //+------------------------------------------------------------------+
127.            C_Replay(const string szFileConfig)
128.                    {
129.                            m_ReplayCount = 0;
130.                            m_dtPrevLoading = 0;
131.                            m_Ticks.nTicks = 0;
132.                            m_Infos.bInit = false;
133.                            Print("************** Market Replay Service **************");
134.                            srand(GetTickCount());
135.                            GlobalVariableDel(def_GlobalVariableReplay);
136.                            SymbolSelect(def_SymbolReplay, false);
137.                            CustomSymbolDelete(def_SymbolReplay);
138.                            CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
139.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
140.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
141.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
142.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
143.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
144.                            CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
145.                            CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
146.                            m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
147.                            SymbolSelect(def_SymbolReplay, true);
148.                    }
149. //+------------------------------------------------------------------+
150.            ~C_Replay()
151.                    {
152.                            ArrayFree(m_Ticks.Info);
153.                            ArrayFree(m_Ticks.Rate);
154.                            m_IdReplay = ChartFirst();
155.                            do
156.                            {
157.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
158.                                            ChartClose(m_IdReplay);
159.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
160.                            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
161.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
162.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
163.                            CustomSymbolDelete(def_SymbolReplay);
164.                            GlobalVariableDel(def_GlobalVariableReplay);
165.                            GlobalVariableDel(def_GlobalVariableServerTime);
166.                            Print("Finished replay service...");
167.                    }
168. //+------------------------------------------------------------------+
169.            bool ViewReplay(ENUM_TIMEFRAMES arg1)
170.                    {
171. #define macroError(A) { Print(A); return false; }
172.                            u_Interprocess info;
173.                            
174.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
175.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
176.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
177.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
178.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
179.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
180.                            if (m_IdReplay == -1) return false;
181.                            if ((m_IdReplay = ChartFirst()) > 0) do
182.                            {
183.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
184.                                    {
185.                                            ChartClose(m_IdReplay);
186.                                            ChartRedraw();
187.                                    }
188.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
189.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
190.                            info.ServerTime = ULONG_MAX;
191.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
192.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
193.                            AddIndicatorControl();
194.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
195.                            info.s_Infos.isHedging = TypeAccountIsHedging();
196.                            info.s_Infos.isSync = true;
197.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
198. 
199.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
200. #undef macroError
201.                    }
202. //+------------------------------------------------------------------+
203.            bool LoopEventOnTime(const bool bViewBuider)
204.                    {
205.                            u_Interprocess Info;
206.                            int iPos, iTest, iCount;
207.                            
208.                            if (!m_Infos.bInit) ViewInfos();
209.                            iTest = 0;
210.                            while ((iTest == 0) && (!_StopFlag))
211.                            {
212.                                    iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
213.                                    iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1);
214.                                    iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
215.                                    if (iTest == 0) Sleep(100);
216.                            }
217.                            if ((iTest < 0) || (_StopFlag)) return false;
218.                            AdjustPositionToReplay(bViewBuider);
219.                            Info.ServerTime = m_Ticks.Info[m_ReplayCount].time;
220.                            GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
221.                            iPos = iCount = 0;
222.                            while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
223.                            {
224.                                    iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0);
225.                                    CreateBarInReplay(true);
226.                                    while ((iPos > 200) && (!_StopFlag))
227.                                    {
228.                                            if (ChartSymbol(m_IdReplay) == "") return false;
229.                                            GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value);
230.                                            if (!Info.s_Infos.isPlay) return true;
231.                                            Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
232.                                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
233.                                            Sleep(195);
234.                                            iPos -= 200;
235.                                            iCount++;
236.                                            if (iCount > 4)
237.                                            {
238.                                                    iCount = 0;
239.                                                    GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value);
240.                                                    if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else
241.                                                    {
242.                                                            Info.ServerTime += 1;
243.                                                            Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time);
244.                                                    };
245.                                                    GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
246.                                            }
247.                                    }
248.                            }                               
249.                            return (m_ReplayCount == m_Ticks.nTicks);
250.                    }                               
251. //+------------------------------------------------------------------+
252. };
253. //+------------------------------------------------------------------+
254. #undef macroRemoveSec
255. #undef def_SymbolReplay
256. //+------------------------------------------------------------------+

Código fonte da classe C_Replay.mqh

No entanto, este código apesar de ter sido modificado, a fim de dar suporte ao que necessitamos, já nos possibilitará fazer bem mais coisas em breve. Mas vamos primeiro ver o que foi colocado nele. Basicamente você pode notar que muito do código permanece bastante similar ao que foi visto no artigo anterior. Mas como quero que você compreenda perfeitamente bem o que está sendo implementado. Coloquei o código, na integra, para que você não fique em dúvida onde colocar as funções que serão usadas.

Muito bem, na linha 116, adicionei um novo procedimento, este tem como finalidade adicionar o indicador de controle no gráfico. Este procedimento é chamado na linha 193, ou seja, logo depois que o gráfico foi lançado pelo serviço, sendo apresentado pelo MetaTrader 5. Mas vamos voltar na linha 116. A primeira coisa que fazemos é criar um handle na linha 120, este irá fazer referência ao indicador que está presente no código serviço. Lembre-se o indicador está sendo embutido no executável do serviço, como sendo um recurso. Então depois de dizer ao MetaTrader 5, onde o indicador está, precisamos passar uma informação para ele. Esta informação é o m_IdReplay, ou seja, a ID do gráfico gerado pela chamada a ChartOpen.

Desta maneira, o indicador irá ficar sabendo qual é a ID correta do gráfico, em que ele está sendo colocado. Preste atenção a isto. Mesmo que você abra um outro gráfico, ligado ao ativo de replay, o indicador não irá aparecer, ele somente irá aparecer, no gráfico que o serviço criou. Isto é imposto e feito na linha 121. Logo depois na linha 122, liberamos o handle que foi criado, visto que não precisamos mais dele.

Mas o que acabamos de ver é somente parte da solução, a outra parte, está justamente no código fonte do indicador de controle. Este pode ser visto na integra logo abaixo:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.50"
07. #property link "https://www.mql5.com/pt/articles/11871"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Terminal *terminal = NULL;
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. input long user00 = 0;   //ID
17. //+------------------------------------------------------------------+
18. int OnInit()
19. {
20.     u_Interprocess Info;
21. 
22.     ResetLastError();
23.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal(user00))) == POINTER_INVALID)
24.             SetUserError(C_Terminal::ERR_PointerInvalid);
25.     if ((!(*terminal).IndicatorCheckPass("Market Replay Control")) || (_LastError != ERR_SUCCESS))
26.     {
27.             Print("Control indicator failed on initialization.");
28.             return INIT_FAILED;
29.     }       
30.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
31.     EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
32.     (*control).Init(Info.s_Infos.isPlay);
33.         
34.     return INIT_SUCCEEDED;
35. }
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     static bool bWait = false;
40.     u_Interprocess Info;
41.     
42.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
43.     if (!bWait)
44.     {
45.             if (Info.s_Infos.isWait)
46.             {
47.                     EventChartCustom(user00, C_Controls::ev_WaitOn, 1, 0, "");
48.                     bWait = true;
49.             }
50.     }else if (!Info.s_Infos.isWait)
51.     {
52.             EventChartCustom(user00, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
53.             bWait = false;
54.     }
55.     
56.     return rates_total;
57. }
58. //+------------------------------------------------------------------+
59. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
60. {
61.     (*control).DispatchMessage(id, lparam, dparam, sparam);
62. }
63. //+------------------------------------------------------------------+
64. void OnDeinit(const int reason)
65. {
66.     switch (reason)
67.     {
68.             case REASON_REMOVE:
69.             case REASON_CHARTCLOSE:
70.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
71.                     GlobalVariableDel(def_GlobalVariableReplay);
72.                     ChartClose(user00);
73.                     break;
74.     }
75.     delete control;
76.     delete terminal;
77. }
78. //+------------------------------------------------------------------+

Código fonte do Indicador de controle

Note que agora temos na linha 16, um input, ou seja o indicador irá receber um parâmetro. Aqui mora um dos motivos de não permitir ao usuário acesso direto a este indicador, pelo menos para que ele não possa colocá-lo de forma manual no gráfico. Este parâmetro, que o indicador irá receber na linha 16, informa qual é a ID do gráfico, onde os objetos serão colocados. Este valor tem que ser corretamente preenchido. Por padrão ele será ZERO, ou seja, se o serviço, tentar colocar o indicador, mas não informar o gráfico, será gerado um erro. A mensagem de erro é lançada na linha 27. Isto explica a diferença entre o que era esperado, e o que foi apresentado no vídeo 01.

Agora observe onde o valor do parâmetro que entra na linha 16 é usado. Ele é usado em diversos pontos, mas o principal mesmo, é na linha 23, onde dizemos a classe C_Terminal, que ela não deverá mais usar um valor gerado, quando busca o ID do gráfico. A classe C_Terminal deverá usar o valor informado pelo serviço que criou o gráfico. Como você pode notar outros pontos onde este mesmo valor informado na linha 16, também é usado. Mas de forma geral ao compilarmos novamente o arquivo de serviço, e o colocarmos em funcionamento no MetaTrader 5, teremos como resultado o que é visto no vídeo 02.


Vídeo 02

Observem com atenção este vídeo 02. Vejam que o sistema está agindo de uma forma bastante estranha quando tentamos interagir com ele. A pergunta é: Por que isto está acontecendo ?!?! Já que não fizemos nenhuma mudança que eventualmente poderia gerar tal comportamento.

Este é o segundo problema que temos que resolver. Porém este problema é consideravelmente mais complexo de ser resolvido. Isto por que a causa dele não é nem o serviço, nem o indicador de controle, muito menos a plataforma MetaTrader 5. Mas sim a interação ou falta de interação do ponteiro do mouse com os objetos presentes no gráfico.

Então você já deve estar pensando: Agora o bicho vai pegar. Pois como iremos corrigir uma falha que não existia antes de começarmos a modificar o código que aparentemente já estava funcionando perfeitamente bem. Isto antes de tomar a fatídica decisão de permitir ao usuário utilizar um template pessoal, e não um que o sistema de replay / simulação vinha utilizando a muito tempo. Pois bem. Isto sim que é programação. Resolver os problemas que vão surgindo enquanto você tenta aplicar nova coisas e resolver outros problemas que com certeza irão surgir.

Mas antes de partir para esta questão, e resolver o problema de interação entre o ponteiro do mouse e os objetos no gráfico, que são criados pelo indicador de controle. Vamos implementar a questão que permite ao usuário fazer uso de um template qualquer, que ele decida por assim usar. Isto quando ele já tiver algum template, já montado e voltado a ser utilizado no ativo, seja em conta demo ou conta real. Mas agora, vamos permitir que o usuário possa usar este mesmo template no replay / simulador.

Fazer isto não é algo complexo, é até relativamente bastante simples. Mas como estamos lidando com usuários, e estes tendem a sempre fazer as coisas de maneira desordenada e sem seguir uma dada lógica. Precisaremos garantir que o indicador de controle permaneça no gráfico. Mesmo quando o usuário insistir em usar o sistema de replay / simulação de maneira completamente diferente do que este foi projetado para ser utilizado.

Então a primeira coisa a de fato se fazer, é dar ao usuário a possibilidade de informar ao serviço, que ele deverá abrir o gráfico fazendo uso de um dado template. Isto é facilmente conseguido com poucas modificações. Para não repetir todo o código, vou colocar apenas a função fragmento onde a mudança deverá acontecer. Primeiramente vamos ver o código do serviço. Este pode ser visto logo abaixo:

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.50"
06. #property description "Replay-Simulator service for MT5 platform."
07. #property description "This is dependent on the Market Replay indicator."
08. #property description "For more details on this version see the article."
09. #property link "https://www.mql5.com/pt/articles/11871"
10. //+------------------------------------------------------------------+
11. #define def_IndicatorControl        "Indicators\\Replay\\Market Replay.ex5"
12. #resource "\\" + def_IndicatorControl
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Service Graphics\C_Replay.mqh>
15. //+------------------------------------------------------------------+
16. input string             user00 = "Forex - EURUSD.txt";  //Replay Configuration File.
17. input ENUM_TIMEFRAMES    user01 = PERIOD_M5;             //Initial Graphic Time.
18. input string             user02 = "Default";             //Template File Name
19. //+------------------------------------------------------------------+
20. void OnStart()
21. {
22.     C_Replay  *pReplay;
23. 
24.     pReplay = new C_Replay(user00);
25.     if ((*pReplay).ViewReplay(user01, user02))
26.     {
27.             Print("Permission granted. Replay service can now be used...");
28.             while ((*pReplay).LoopEventOnTime(false));
29.     }
30.     delete pReplay;
31. }
32. //+------------------------------------------------------------------+

Código fonte do Serviço

Neste código você pode notar que foi adicionada a linha 18, onde damos ao usuário a possibilidade de informar qual será o template a ser utilizado, no momento que o serviço abrir o gráfico do ativo a ser usado como replay / simulação. Este valor é repassado a classe na linha 25, onde informamos fazemos os devidos ajustes. Para entender o que estará acontecendo, vamos ver o fragmento desta rotina. Irei colocar o fragmento, pois não faz sentido repetir todo o código.

175. //+------------------------------------------------------------------+
176.            bool ViewReplay(ENUM_TIMEFRAMES arg1, const string szNameTemplate)
177.                    {
178. #define macroError(A) { Print(A); return false; }
179.                            u_Interprocess info;
180.                            
181.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
182.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
183.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
184.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
185.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
186.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
187.                            if (m_IdReplay == -1) return false;
188.                            if ((m_IdReplay = ChartFirst()) > 0) do
189.                            {
190.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
191.                                    {
192.                                            ChartClose(m_IdReplay);
193.                                            ChartRedraw();
194.                                    }
195.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
196.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
197.                            info.ServerTime = ULONG_MAX;
198.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
199.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
200.                            if (!ChartApplyTemplate(m_IdReplay, szNameTemplate + ".tpl"))
201.                                    Print("Failed apply template: ", szNameTemplate, ".tpl Using template default.tpl");
202.                            AddIndicatorControl();
203.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
204.                            info.s_Infos.isHedging = TypeAccountIsHedging();
205.                            info.s_Infos.isSync = true;
206.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
207. 
208.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
209. #undef macroError
210.                    }
211. //+------------------------------------------------------------------+

Fragmento do código fonte C_Replay.mqh ( Atualização )

Observe que na linha 176, adicionamos um parâmetro extra. Isto para que a classe saiba qual o template será carregado. Então na linha 200, tentamos aplicar o template informado pelo usuário. Se isto não for possível, na linha 201 informamos ao usuário isto, e passamos a usar o template Default. Vale uma pausa neste ponto, a fim de explicar uma coisa. Por padrão o MetaTrader 5, sempre usará o template Default, por conta disto, não precisamos forçar uma nova chamada a fim de fazer com que este template seja de fato carregado e aplicado ao gráfico.

Neste momento, vale explicar uma coisa, que pode ser interessante: Caso você opere mais de um ativo, e queira fazer estudos direcionados a cada um, de maneira a fazer uso de um template especifico. Pode ser viável você pensar em adicionar no arquivo de configuração da simulação, o nome do template a ser usado. Assim ele já ficará configurado, não precisando ser informado no momento que o serviço for colocado em execução. Mas isto é apenas uma ideia, para quem desejar fazer isto. Neste sistema não irei de fato fazer este tipo de coisa. Mesmo por que existe uma outra coisa, nesta história sobre template. E é justamente esta coisa a nossa dor de cabeça como programador. O usuário.

Por que o usuário é um dor de cabeça para nós ?! Isto talvez não faça muito sentido. Mas sim, ele é uma baita de uma dor de cabeça, isto por conta que ele pode dizer ao MetaTrader 5, para executar o serviço, e em um dado momento, mudar o template do gráfico onde o replay / simulador está sendo executado. Isto sem reiniciar o serviço a fim de que o serviço faça a mudança. Talvez você não tenha de fato entendido o problema. Mas ao fazer isto, as configurações do template irá se sobrepor ao que está no gráfico, e o problema é que o indicador de controle será removido do gráfico.

Lembre-se o usuário não terá como colocar manualmente o indicador de controle no gráfico. Se bem que existe formas de se fazer isto. Mas vamos considerar o fato de que o usuário não saiba como trabalhar no MetaTrader 5 e no MQL5, então ele não conseguirá repor o indicador de controle no gráfico. Este tipo de coisa é que é a tal falha na qual o usuário é o responsável por gerar, e o programador responsável por corrigir.

Pois bem, ao modificar o template, o MetaTrader 5 irá informar os programas que estão no gráfico, que houve troca do template, isto para que eles saibam o acontecido e possam tomar algum tipo de posição a este respeito. A forma como o MetaTrader 5, faz isto pode ser visto no fragmento abaixo:

void OnDeinit(const int reason)
{
        switch (reason)
        {
                case REASON_TEMPLATE:
                        Print("Template change ...");
                        break;

Quando o template for modificado, o MetaTrader 5 irá disparar um evento DeInit, que irá chamar o procedimento visto acima. Podemos testar a condição que fez o MetaTrader 5 disparar o evento DeInit, e se for a troca do template, a mensagem será impressa no terminal.

Ou seja, temos como saber se houve troca do template. Mas saber disto não faz com que o indicador seja reposto logo em seguida. Aqui temos que tomar uma decisão. Forçar o serviço a ser encerrado, ou forçar o serviço a repor o indicador de controle no gráfico. Ao meu ver, e isto é uma opinião particular, o que devemos de fato fazer é forçar o serviço a ser encerrado. O motivo é simples. Se é permitido ao usuário, configurar o template que será utilizado, no gráfico que irá conter o replay / simulação. Por que devemos permitir que o usuário mude de forma manual o template ?!?! Não faz sentido. Então ao meu ver, o correto, é simplesmente encerra o serviço, e fazer com que o usuário informe o template no momento que o serviço for posto em execução. Caso contrário, qual seria a necessidade de permitir ao usuário informar o template ao serviço ?!?!

Desta forma, o código do indicador de controle, irá passar por mais uma atualização bem simples, esta pode ser vista no fragmento logo abaixo:

64. //+------------------------------------------------------------------+
65. void OnDeinit(const int reason)
66. {
67.     switch (reason)
68.     {
69.             case REASON_TEMPLATE:
70.                     Print("Modified template. Replay/simulation system shutting down.");
71.             case REASON_PARAMETERS:
72.             case REASON_REMOVE:
73.             case REASON_CHARTCLOSE:
74.                     if (ChartSymbol(user00) != def_SymbolReplay) break;
75.                     GlobalVariableDel(def_GlobalVariableReplay);
76.                     ChartClose(user00);
77.                     break;
78.     }
79.     delete control;
80.     delete terminal;
81. }
82. //+------------------------------------------------------------------+

Fragmento do código fonte do Indicador de controle ( Atualização )

Observe o fato, de que na linha 69, adicionamos o teste a fim de verificar o motivo pelo qual o indicador está sendo removido do gráfico. Caso o motivo seja a troca de template, a linha 70 irá imprimir uma mensagem no terminal, e teremos o mesmo resultado que seria dado, caso o gráfico fosse fechado pelo usuário, ou o indicador fosse removido pelo usuário. Ou seja, o serviço será encerrado. Talvez esta solução lhe pareça radical demais. Mas como expliquei, não faz sentido fazer as coisas de uma outra forma.

Apesar de termos corrigido uma das falhas, temos um outro problema: O usuário pode simplesmente trocar algum parâmetro que está sendo usado pelo indicador de controle, e foi repassado para ele pelo serviço. Lembre-se ainda estamos montando o sistema, então novos parâmetros podem vim a surgir. Este tipo de situação é bem mais complicada de ser contornada. Mas iremos ser radicais neste tipo de abordagem também. A solução já foi implementada no próprio fragmento que você pode ver acima.

Preste atenção a linha 71. Esta linha não existia antes. Mas foi colocada ali, justamente para evitar de que o usuário venha a modificar algum dos parâmetros que o serviço informou para o indicador de controle. Se isto acontecer, o MetaTrader 5, irá gerar um evento DeInit, mas o argumento será a troca de parâmetro. Não iremos informar nenhum tipo de falha, não neste momento, já que a falha irá acontecer no momento que o indicador for ser recolocado pelo MetaTrader 5. Mas como pode acontecer de algum usuário mais esperto, informar a ID de um gráfico válido. Fechamos o gráfico do replay / simulador, isto na linha 76, assim quando o serviço for testar se o gráfico está aberto, irá receber um erro, e para o serviço isto indica que ele deve ser encerrado. Desta maneira corrigimos esta falha também.


Conclusão

Neste artigo, corrigimos algumas falhas primarias que surgiram pelo fato de que o indicador de controle, deixou de poder ser acessado pelo usuário. Apesar de permanecer listado na janela de indicadores usando o atalho CTRL + I, este não é mais listado em meio a outros indicadores que podem ser usados em qualquer gráfico. Este tipo de mudança, está provocando bastante mudanças no código, a fim de torná-lo mais estável, e consistente. Evitando que o usuário faça coisas que não desejamos ou esperamos que sejam feitas.

Mesmo assim, ainda continuamos com a falha que dificulta a interação entre o usuário e o indicador, isto a fim de ajustar, e manipular o indicador de controle com auxílio do ponteiro do mouse. Mas esta falha se deve a uma coisa que iremos corrigir em breve, deixando assim o sistema ainda mais livre para poder ser utilizado de forma totalmente modular.

No vídeo 03, presente logo abaixo, você pode ver como o sistema está se comportando agora, com as atualizações que foram feitas neste artigo. Mas como o código ainda é instável, não haverá anexo neste artigo.


Vídeo 03

No próximo artigo, vamos continuar a resolver as questões e problemas ainda envolvidos, nesta interação entre o usuário e o serviço de replay / simulação.


Arquivos anexados |
Anexo.zip (420.65 KB)
Últimos Comentários | Ir para discussão (1)
Уроборос
Уроборос | 25 mai 2024 em 21:29
Will there be playback speed control?
Filtragem e extração de características no domínio da frequência Filtragem e extração de características no domínio da frequência
Neste artigo, vamos explorar a aplicação de filtros digitais em séries temporais representadas no domínio da frequência, com o objetivo de extrair características únicas que podem ser úteis para modelos de previsão.
Ciência de Dados e Aprendizado de Máquina (Parte 16): Uma nova perspectiva sobre árvores de decisão Ciência de Dados e Aprendizado de Máquina (Parte 16): Uma nova perspectiva sobre árvores de decisão
Na última parte da nossa série sobre aprendizado de máquina e trabalho com big data, voltamos a falar sobre as árvores de decisão. Este artigo é destinado a traders que desejam entender o papel das árvores de decisão na análise de tendências de mercado. Aqui, reunimos todas as informações principais sobre a estrutura, o propósito e o uso dessas árvores. Vamos explorar as raízes e os ramos das árvores algorítmicas e descobrir como elas podem ser aplicadas na tomada de decisões de negociação. Vamos juntos dar um novo olhar às árvores de decisão e ver como elas podem ajudar a superar as dificuldades nos mercados financeiros.
Algoritmos de otimização populacional: Mudamos a forma e deslocamos as distribuições de probabilidade e testamos com o "Cabeçudinho Inteligente" (Smart Cephalopod, SC) Algoritmos de otimização populacional: Mudamos a forma e deslocamos as distribuições de probabilidade e testamos com o "Cabeçudinho Inteligente" (Smart Cephalopod, SC)
Com este artigo investigaremos como a mudança de forma das distribuições de probabilidade afetam o desempenho dos algoritmos de otimização. Realizaremos experimentos baseados no algoritmo de teste "cabeçudinho inteligente" (Smart Cephalopod, SC) para avaliar o desempenho de diferentes distribuições de probabilidade no contexto de tarefas de otimização.
Algoritmos de otimização populacional: simulação de têmpera isotrópica (Simulated Isotropic Annealing, SIA). Parte II Algoritmos de otimização populacional: simulação de têmpera isotrópica (Simulated Isotropic Annealing, SIA). Parte II
A primeira parte do artigo foi dedicada ao conhecido e popular algoritmo de têmpera simulada, onde foram analisadas suas vantagens e descritos detalhadamente os pontos fracos. A segunda parte do artigo é dedicada a uma transformação radical do algoritmo, seu renascimento em um novo algoritmo de otimização, a simulação de têmpera isotrópica, SIA.