![Desenvolvendo um sistema de Replay (Parte 60): Dando play no serviço (I)](https://c.mql5.com/2/89/midjourney_image_12086_394_3792_2_600x314.jpg)
Desenvolvendo um sistema de Replay (Parte 60): Dando play no serviço (I)
Introdução
No artigo anterior Desenvolvendo um sistema de Replay (Parte 59): Um novo futuro, mostrei e expliquei algumas das mudanças que ocorreram nos módulos dos indicadores de controle e de mouse. Apesar de aquilo nos dar diversas possibilidades de uso futuro, para o modulo de indicador de mouse. Ainda assim ele contém uma pequena falha. Mas tal falha, não nos afeta neste momento. Isto por que nosso foco, neste exato momento será resolver alguns problemas que ainda temos com relação ao encapsulamento, presente no código das classes do serviço de replay/simulador.
Apesar de já termos feito algumas mudanças, que foram vistas nos artigos anteriores. As mesmas não foram o suficiente para resolver completamente o problema do encapsulamento. Ainda temos coisas vazando de dentro das classes. E precisamos corrigir isto agora, caso contrário teremos sérios problemas em breve. Além deste problema de encapsulamento, que está deixando algumas coisas que não deveriam vazar, serem acessíveis diretamente fora da classe. Temos algumas outras coisas, para serem remodeladas, a fim de nos permitir um acesso mais adequado a algumas variáveis e informações. Tudo isto será feito aqui, neste artigo.
Começando a ajustar as coisas
Muito bem. O primeiro ponto que iremos começar, será resolvendo o problema do encapsulamento da classe C_FilesTicks. Esta classe está no arquivo C_FilesTicks.mqh, e nela temos o vazamento de uma informação, que não poderia estar de fato sendo acessível. Pelo menos, não da forma como está sendo feito.
Para resolver este problema de vazamento, teremos que fazer algo bastante simples. Então o novo código da classe C_FilesTicks, pode ser visto logo abaixo, na integra.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_FileBars.mqh" 005. #include "C_Simulation.mqh" 006. //+------------------------------------------------------------------+ 007. #define macroRemoveSec(A) (A - (A % 60)) 008. //+------------------------------------------------------------------+ 009. class C_FileTicks 010. { 011. protected: 012. enum ePlotType {PRICE_EXCHANGE, PRICE_FOREX}; 013. struct stInfoTicks 014. { 015. MqlTick Info[]; 016. MqlRates Rate[]; 017. int nTicks, 018. nRate; 019. bool bTickReal; 020. ePlotType ModePlot; 021. };m_Ticks; 022. //+------------------------------------------------------------------+ 023. inline bool BuildBar1Min(const int iArg, MqlRates &rate, bool &bNew) 024. { 025. double dClose = 0; 026. 027. switch (m_Ticks.ModePlot) 028. { 029. case PRICE_EXCHANGE: 030. if (m_Ticks.Info[iArg].last == 0.0) return false; 031. dClose = m_Ticks.Info[iArg].last; 032. break; 033. case PRICE_FOREX: 034. dClose = (m_Ticks.Info[iArg].bid > 0.0 ? m_Ticks.Info[iArg].bid : dClose); 035. if ((dClose == 0.0) || (m_Ticks.Info[iArg].bid == 0.0)) return false; 036. break; 037. } 038. if (bNew = (rate.time != macroRemoveSec(m_Ticks.Info[iArg].time))) 039. { 040. rate.time = macroRemoveSec(m_Ticks.Info[iArg].time); 041. rate.real_volume = 0; 042. rate.tick_volume = (m_Ticks.ModePlot == PRICE_FOREX ? 1 : 0); 043. rate.open = rate.low = rate.high = rate.close = dClose; 044. }else 045. { 046. rate.close = dClose; 047. rate.high = (rate.close > rate.high ? rate.close : rate.high); 048. rate.low = (rate.close < rate.low ? rate.close : rate.low); 049. rate.real_volume += (long) m_Ticks.Info[iArg].volume_real; 050. rate.tick_volume++; 051. } 052. 053. return true; 054. } 055. //+------------------------------------------------------------------+ 056. private : 057. int m_File; 058. stInfoTicks m_Ticks; 059. //+------------------------------------------------------------------+ 060. inline bool Open(const string szFileNameCSV) 061. { 062. string szInfo = ""; 063. 064. if ((m_File = FileOpen("Market Replay\\Ticks\\" + szFileNameCSV + ".csv", FILE_CSV | FILE_READ | FILE_ANSI)) != INVALID_HANDLE) 065. { 066. for (int c0 = 0; c0 < 7; c0++) szInfo += FileReadString(m_File); 067. if (szInfo == "<DATE><TIME><BID><ASK><LAST><VOLUME><FLAGS>") return true; 068. Print("File ", szFileNameCSV, ".csv not a traded tick file."); 069. }else 070. Print("Tick file ", szFileNameCSV,".csv not found..."); 071. 072. return false; 073. } 074. //+------------------------------------------------------------------+ 075. inline bool ReadAllsTicks(const bool ToReplay) 076. { 077. string szInfo; 078. 079. Print("Loading replay ticks. Please wait..."); 080. ArrayResize(m_Ticks.Info, def_MaxSizeArray, def_MaxSizeArray); 081. m_Ticks.ModePlot = PRICE_FOREX; 082. while ((!FileIsEnding(m_File)) && (m_Ticks.nTicks < (INT_MAX - 2)) && (!_StopFlag)) 083. { 084. ArrayResize(m_Ticks.Info, m_Ticks.nTicks + 1, def_MaxSizeArray); 085. szInfo = FileReadString(m_File) + " " + FileReadString(m_File); 086. m_Ticks.Info[m_Ticks.nTicks].time = StringToTime(StringSubstr(szInfo, 0, 19)); 087. m_Ticks.Info[m_Ticks.nTicks].time_msc = (m_Ticks.Info[m_Ticks.nTicks].time * 1000) + (int)StringToInteger(StringSubstr(szInfo, 20, 3)); 088. m_Ticks.Info[m_Ticks.nTicks].bid = StringToDouble(FileReadString(m_File)); 089. m_Ticks.Info[m_Ticks.nTicks].ask = StringToDouble(FileReadString(m_File)); 090. m_Ticks.Info[m_Ticks.nTicks].last = StringToDouble(FileReadString(m_File)); 091. m_Ticks.Info[m_Ticks.nTicks].volume_real = StringToDouble(FileReadString(m_File)); 092. m_Ticks.Info[m_Ticks.nTicks].flags = (uchar)StringToInteger(FileReadString(m_File)); 093. m_Ticks.ModePlot = (m_Ticks.Info[m_Ticks.nTicks].volume_real > 0.0 ? PRICE_EXCHANGE : m_Ticks.ModePlot); 094. m_Ticks.nTicks++; 095. } 096. FileClose(m_File); 097. if (m_Ticks.nTicks == (INT_MAX - 2)) 098. { 099. Print("Too much data in tick file.\nIt is not possible to continue..."); 100. return false; 101. } 102. return (!_StopFlag); 103. } 104. //+------------------------------------------------------------------+ 105. int SetSymbolInfos(void) 106. { 107. int iRet; 108. 109. CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, iRet = (m_Ticks.ModePlot == PRICE_EXCHANGE ? 4 : 5)); 110. CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_TRADE_CALC_MODE, m_Ticks.ModePlot == PRICE_EXCHANGE ? SYMBOL_CALC_MODE_EXCH_STOCKS : SYMBOL_CALC_MODE_FOREX); 111. CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_CHART_MODE, m_Ticks.ModePlot == PRICE_EXCHANGE ? SYMBOL_CHART_MODE_LAST : SYMBOL_CHART_MODE_BID); 112. 113. return iRet; 114. } 115. //+------------------------------------------------------------------+ 116. public : 117. //+------------------------------------------------------------------+ 118. C_FileTicks() 119. { 120. ArrayResize(m_Ticks.Rate, def_BarsDiary); 121. m_Ticks.nRate = -1; 122. m_Ticks.Rate[0].time = 0; 123. } 124. //+------------------------------------------------------------------+ 125. bool BarsToTicks(const string szFileNameCSV) 126. { 127. C_FileBars *pFileBars; 128. C_Simulation *pSimulator = NULL; 129. int iMem = m_Ticks.nTicks, 130. iRet = -1; 131. MqlRates rate[1]; 132. MqlTick local[]; 133. bool bInit = false; 134. 135. pFileBars = new C_FileBars(szFileNameCSV); 136. ArrayResize(local, def_MaxSizeArray); 137. Print("Converting bars to ticks. Please wait..."); 138. while ((*pFileBars).ReadBar(rate) && (!_StopFlag)) 139. { 140. if (!bInit) 141. { 142. m_Ticks.ModePlot = (rate[0].real_volume > 0 ? PRICE_EXCHANGE : PRICE_FOREX); 143. pSimulator = new C_Simulation(SetSymbolInfos()); 144. bInit = true; 145. } 146. ArrayResize(m_Ticks.Rate, (m_Ticks.nRate > 0 ? m_Ticks.nRate + 3 : def_BarsDiary), def_BarsDiary); 147. m_Ticks.Rate[++m_Ticks.nRate] = rate[0]; 148. if (pSimulator == NULL) iRet = -1; else iRet = (*pSimulator).Simulation(rate[0], local); 149. if (iRet < 0) break; 150. for (int c0 = 0; c0 <= iRet; c0++) 151. { 152. ArrayResize(m_Ticks.Info, (m_Ticks.nTicks + 1), def_MaxSizeArray); 153. m_Ticks.Info[m_Ticks.nTicks++] = local[c0]; 154. } 155. } 156. ArrayFree(local); 157. delete pFileBars; 158. delete pSimulator; 159. m_Ticks.bTickReal = false; 160. 161. return ((!_StopFlag) && (iMem != m_Ticks.nTicks) && (iRet > 0)); 162. } 163. //+------------------------------------------------------------------+ 164. datetime LoadTicks(const string szFileNameCSV, const bool ToReplay = true) 165. { 166. int MemNRates, 167. MemNTicks; 168. datetime dtRet = TimeCurrent(); 169. MqlRates RatesLocal[], 170. rate; 171. bool bNew; 172. 173. MemNRates = (m_Ticks.nRate < 0 ? 0 : m_Ticks.nRate); 174. MemNTicks = m_Ticks.nTicks; 175. if (!Open(szFileNameCSV)) return 0; 176. if (!ReadAllsTicks(ToReplay)) return 0; 177. rate.time = 0; 178. for (int c0 = MemNTicks; c0 < m_Ticks.nTicks; c0++) 179. { 180. if (!BuildBar1Min(c0, rate, bNew)) continue; 181. if (bNew) ArrayResize(m_Ticks.Rate, (m_Ticks.nRate > 0 ? m_Ticks.nRate + 2 : def_BarsDiary), def_BarsDiary); 182. m_Ticks.Rate[(m_Ticks.nRate += (bNew ? 1 : 0))] = rate; 183. } 184. if (!ToReplay) 185. { 186. ArrayResize(RatesLocal, (m_Ticks.nRate - MemNRates)); 187. ArrayCopy(RatesLocal, m_Ticks.Rate, 0, 0); 188. CustomRatesUpdate(def_SymbolReplay, RatesLocal, (m_Ticks.nRate - MemNRates)); 189. dtRet = m_Ticks.Rate[m_Ticks.nRate].time; 190. m_Ticks.nRate = (MemNRates == 0 ? -1 : MemNRates); 191. m_Ticks.nTicks = MemNTicks; 192. ArrayFree(RatesLocal); 193. }else SetSymbolInfos(); 194. m_Ticks.bTickReal = true; 195. 196. return dtRet; 197. }; 198. //+------------------------------------------------------------------+ 199. inline stInfoTicks GetInfoTicks(void) const 200. { 201. return m_Ticks; 202. } 203. //+------------------------------------------------------------------+ 204. }; 205. //+------------------------------------------------------------------+ 206. #undef def_MaxSizeArray 207. //+------------------------------------------------------------------+
Código fonte do arquivo C_FileTicks.mqh
Você muito provavelmente, não está conseguindo ver alguma grande diferença. Com exceção é claro, de termos na linha 21, algo sendo riscado.
Esta coisa que se encontra riscada, é o tal vazamento que estava acontecendo. Ou seja, mesmo que tudo estivesse seguro, e funcionando perfeitamente, quando fossemos precisar fazer algo, relacionado aos ticks que foram carregados, ou simulados, teríamos uma grave falha de segurança. E o motivo disto, é que qualquer classe, poderia modificar os dados desta estrutura. Observe que fora do escopo das classes, tal mudança não poderia ser feita. Visto que a estrutura está em uma clausula protected. Em um artigo passado, expliquei como estas clausulas influenciam o nível de acesso ao que está dentro de uma classe.
Mas o fato de remover, a variável, e permitir apenas o uso da estrutura já nos dá um nível bem mais aceitável de segurança e encapsulamento. No entanto, ao fazer tal mudança, temos que tomar uma outra decisão. Tal decisão é: Declarar uma nova variável, mas desta vez ela será privativa, a fim de manter a estrutura, e adicionarmos uma função a fim de acessar tal variável. Ou declarar esta mesma variável em outro local, e passar ela, como argumento para as funções, que vierem a precisar ter acesso ao conteúdo presente nela.
Pode parecer estranho, mas cada caso é um caso, e tem suas consequências e também possibilidades. No entanto, devido à natureza do que está sendo projetado, tomei a decisão de fazer a primeira abordagem. Ou seja, iremos criar uma variável privativa, e esta é vista na linha 58. Note que fora isto não foi preciso mudar mais nada no código. Porém, ao retirar o acesso desta variável, das demais classes. E sim estávamos usando-a em outros locais, não somente acessando os dados, mas também, os modificando, com será visto em breve. Precisaremos inicializar corretamente a estrutura. Por conta disto, na linha 118, apareceu um constructor, note que este é bastante simples, fazendo apenas um trabalho que antes era feito em outro local, mas isto por que havia um vazamento por conta da falha que acabamos de corrigir no encapsulamento, dentro da classe C_FilesTicks.
Mas também, como foi dito antes, precisamos criar um meio de acessar a variável, a fim de conseguir acessar os dados presentes nela. Isto é feito pela função que se encontra na linha 199. Preste bastante atenção a isto. Nós iremos conseguir ler os dados de dento da estrutura, mas não iremos conseguir escrever na estrutura. Isto por conta, da declaração de que o valor retornado, deverá ser tratado como sendo uma constante. O uso deste tipo de coisa, já foi explicado em alguns dos meus artigos passados, dentro desta mesma sequência, qualquer dúvida, dê uma pesquisada nos mesmos.
Agora que encerramos um dos problemas, temos que resolver um outro, que de certa forma estava ligado ao que acabamos de sanar. Este outro problema, se encontra na classe C_ConfigService, que pode ser vista no arquivo C_ConfigService.mqh. O código deste arquivo de cabeçalho precisou sofrer algumas mudanças, tais mudanças se devem, primeiro ao fato de que agora não existe mais o vazamento de dados da classe C_FileTicks. E também pelo fato de que algumas coisas declaradas nela, irão ser removidas, pois não são necessárias na classe em questão, elas serão usadas em outro local.
Então, o novo código do arquivo C_ConfigService.mqh, pode ser visto logo abaixo:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Support\C_FileBars.mqh" 005. #include "Support\C_FileTicks.mqh" 006. #include "Support\C_Array.mqh" 007. //+------------------------------------------------------------------+ 008. class C_ConfigService : protected C_FileTicks 009. { 010. protected: 011. //+------------------------------------------------------------------+ 012. private : 013. enum eWhatExec {eTickReplay, eBarToTick, eTickToBar, eBarPrev}; 014. enum eTranscriptionDefine {Transcription_INFO, Transcription_DEFINE}; 015. struct st001 016. { 017. C_Array *pTicksToReplay, *pBarsToTicks, *pTicksToBars, *pBarsToPrev; 018. int Line; 019. bool AccountHedging; 020. char ModelLoading; 021. string szPath; 022. }m_GlPrivate; 023. string m_szPath; 024. bool m_AccountHedging; 025. datetime m_dtPrevLoading; 026. int m_ReplayCount, 027. m_ModelLoading; 028. //+------------------------------------------------------------------+ 029. inline void FirstBarNULL(void) 030. { 031. MqlRates rate[1]; 032. int c0 = 0; 033. 034. for(; (GetInfoTicks().ModePlot == PRICE_EXCHANGE) && (GetInfoTicks().Info[c0].volume_real == 0); c0++); 035. rate[0].close = (GetInfoTicks().ModePlot == PRICE_EXCHANGE ? GetInfoTicks().Info[c0].last : GetInfoTicks().Info[c0].bid); 036. rate[0].open = rate[0].high = rate[0].low = rate[0].close; 037. rate[0].tick_volume = 0; 038. rate[0].real_volume = 0; 039. rate[0].time = macroRemoveSec(GetInfoTicks().Info[c0].time) - 86400; 040. CustomRatesUpdate(def_SymbolReplay, rate); 041. } 042. //+------------------------------------------------------------------+ 043. inline eTranscriptionDefine GetDefinition(const string &In, string &Out) 044. { 045. string szInfo; 046. 047. szInfo = In; 048. Out = ""; 049. StringToUpper(szInfo); 050. StringTrimLeft(szInfo); 051. StringTrimRight(szInfo); 052. if (StringSubstr(szInfo, 0, 1) == "#") return Transcription_INFO; 053. if (StringSubstr(szInfo, 0, 1) != "[") 054. { 055. Out = szInfo; 056. return Transcription_INFO; 057. } 058. for (int c0 = 0; c0 < StringLen(szInfo); c0++) 059. if (StringGetCharacter(szInfo, c0) > ' ') 060. StringAdd(Out, StringSubstr(szInfo, c0, 1)); 061. 062. return Transcription_DEFINE; 063. } 064. //+------------------------------------------------------------------+ 065. inline bool Configs(const string szInfo) 066. { 067. const string szList[] = { 068. "PATH", 069. "POINTSPERTICK", 070. "VALUEPERPOINTS", 071. "VOLUMEMINIMAL", 072. "LOADMODEL", 073. "ACCOUNT" 074. }; 075. string szRet[]; 076. char cWho; 077. 078. if (StringSplit(szInfo, '=', szRet) == 2) 079. { 080. StringTrimRight(szRet[0]); 081. StringTrimLeft(szRet[1]); 082. for (cWho = 0; cWho < ArraySize(szList); cWho++) if (szList[cWho] == szRet[0]) break; 083. switch (cWho) 084. { 085. case 0: 086. m_GlPrivate.szPath = szRet[1]; 087. return true; 088. case 1: 089. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, StringToDouble(szRet[1])); 090. return true; 091. case 2: 092. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, StringToDouble(szRet[1])); 093. return true; 094. case 3: 095. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, StringToDouble(szRet[1])); 096. return true; 097. case 4: 098. m_GlPrivate.ModelLoading = StringInit(szRet[1]); 099. m_GlPrivate.ModelLoading = ((m_GlPrivate.ModelLoading < 1) && (m_GlPrivate.ModelLoading > 4) ? 1 : m_GlPrivate.ModelLoading); 100. return true; 101. case 5: 102. if (szRet[1] == "HEDGING") m_GlPrivate.AccountHedging = true; 103. else if (szRet[1] == "NETTING") m_GlPrivate.AccountHedging = false; 104. else 105. { 106. Print("Entered account type is not invalid."); 107. return false; 108. } 109. return true; 110. } 111. Print("Variable >>", szRet[0], "<< not defined."); 112. }else 113. Print("Configuration definition >>", szInfo, "<< invalidates."); 114. 115. return false; 116. } 117. //+------------------------------------------------------------------+ 118. inline bool WhatDefine(const string szArg, char &cStage) 119. { 120. const string szList[] = { 121. "[BARS]", 122. "[TICKS]", 123. "[TICKS->BARS]", 124. "[BARS->TICKS]", 125. "[CONFIG]" 126. }; 127. 128. cStage = 1; 129. for (char c0 = 0; c0 < ArraySize(szList); c0++, cStage++) 130. if (szList[c0] == szArg) return true; 131. 132. return false; 133. } 134. //+------------------------------------------------------------------+ 135. inline bool CMD_Array(char &cError, eWhatExec e1) 136. { 137. bool bBarsPrev = false; 138. string szInfo; 139. C_FileBars *pFileBars; 140. C_Array *ptr = NULL; 141. 142. switch (e1) 143. { 144. case eTickReplay : ptr = m_GlPrivate.pTicksToReplay; break; 145. case eTickToBar : ptr = m_GlPrivate.pTicksToBars; break; 146. case eBarToTick : ptr = m_GlPrivate.pBarsToTicks; break; 147. case eBarPrev : ptr = m_GlPrivate.pBarsToPrev; break; 148. } 149. if (ptr != NULL) 150. { 151. for (int c0 = 0; (c0 < INT_MAX) && (cError == 0); c0++) 152. { 153. if ((szInfo = ptr.At(c0, m_GlPrivate.Line)) == "") break; 154. switch (e1) 155. { 156. case eTickReplay : 157. if (LoadTicks(szInfo) == 0) cError = 4; 158. break; 159. case eTickToBar : 160. if (LoadTicks(szInfo, false) == 0) cError = 5; else bBarsPrev = true; 161. break; 162. case eBarToTick : 163. if (!BarsToTicks(szInfo)) cError = 6; 164. break; 165. case eBarPrev : 166. pFileBars = new C_FileBars(szInfo); 167. if ((*pFileBars).LoadPreView() == 0) cError = 3; else bBarsPrev = true; 168. delete pFileBars; 169. break; 170. } 171. } 172. delete ptr; 173. } 174. 175. return bBarsPrev; 176. } 177. //+------------------------------------------------------------------+ 178. public : 179. //+------------------------------------------------------------------+ 180. C_ConfigService() 181. :C_FileTicks() 182. { 183. m_GlPrivate.AccountHedging = false; 184. m_GlPrivate.ModelLoading = 1; 185. } 186. //+------------------------------------------------------------------+ 187. inline const bool TypeAccountIsHedging(void) const 188. { 189. return m_GlPrivate.AccountHedging; 190. } 191. //+------------------------------------------------------------------+ 192. bool SetSymbolReplay(const string szFileConfig) 193. { 194. #define macroFileName ((m_GlPrivate.szPath != NULL ? m_GlPrivate.szPath + "\\" : "") + szInfo) 195. int file; 196. char cError, 197. cStage; 198. string szInfo; 199. bool bBarsPrev; 200. 201. if ((file = FileOpen("Market Replay\\" + szFileConfig, FILE_CSV | FILE_READ | FILE_ANSI)) == INVALID_HANDLE) 202. { 203. Print("Failed to open configuration file [", szFileConfig, "]. Service being terminated..."); 204. return false; 205. } 206. Print("Loading data for playback. Please wait...."); 207. ArrayResize(Ticks.Rate, def_BarsDiary); 208. Ticks.nRate = -1; 209. Ticks.Rate[0].time = 0; 210. cError = cStage = 0; 211. bBarsPrev = false; 212. m_GlPrivate.Line = 1; 213. m_GlPrivate.pTicksToReplay = m_GlPrivate.pTicksToBars = m_GlPrivate.pBarsToTicks = m_GlPrivate.pBarsToPrev = NULL; 214. while ((!FileIsEnding(file)) && (!_StopFlag) && (cError == 0)) 215. { 216. switch (GetDefinition(FileReadString(file), szInfo)) 217. { 218. case Transcription_DEFINE: 219. cError = (WhatDefine(szInfo, cStage) ? 0 : 1); 220. break; 221. case Transcription_INFO: 222. if (szInfo != "") switch (cStage) 223. { 224. case 0: 225. cError = 2; 226. break; 227. case 1: 228. if (m_GlPrivate.pBarsToPrev == NULL) m_GlPrivate.pBarsToPrev = new C_Array(); 229. (*m_GlPrivate.pBarsToPrev).Add(macroFileName, m_GlPrivate.Line); 230. break; 231. case 2: 232. if (m_GlPrivate.pTicksToReplay == NULL) m_GlPrivate.pTicksToReplay = new C_Array(); 233. (*m_GlPrivate.pTicksToReplay).Add(macroFileName, m_GlPrivate.Line); 234. break; 235. case 3: 236. if (m_GlPrivate.pTicksToBars == NULL) m_GlPrivate.pTicksToBars = new C_Array(); 237. (*m_GlPrivate.pTicksToBars).Add(macroFileName, m_GlPrivate.Line); 238. break; 239. case 4: 240. if (m_GlPrivate.pBarsToTicks == NULL) m_GlPrivate.pBarsToTicks = new C_Array(); 241. (*m_GlPrivate.pBarsToTicks).Add(macroFileName, m_GlPrivate.Line); 242. break; 243. case 5: 244. if (!Configs(szInfo)) cError = 7; 245. break; 246. } 247. break; 248. }; 249. m_GlPrivate.Line += (cError > 0 ? 0 : 1); 250. } 251. FileClose(file); 252. CMD_Array(cError, (m_GlPrivate.ModelLoading <= 2 ? eTickReplay : eBarToTick)); 253. CMD_Array(cError, (m_GlPrivate.ModelLoading <= 2 ? eBarToTick : eTickReplay)); 254. bBarsPrev = (CMD_Array(cError, ((m_GlPrivate.ModelLoading & 1) == 1 ? eTickToBar : eBarPrev)) ? true : bBarsPrev); 255. bBarsPrev = (CMD_Array(cError, ((m_GlPrivate.ModelLoading & 1) == 1 ? eBarPrev : eTickToBar)) ? true : bBarsPrev); 256. switch(cError) 257. { 258. case 0: 259. if (GetInfoTicks().nTicks <= 0) //Atualizado ... 260. { 261. Print("There are no ticks to use. Service is being terminated..."); 262. cError = -1; 263. }else if (!bBarsPrev) FirstBarNULL(); 264. break; 265. case 1 : Print("The command on the line ", m_GlPrivate.Line, " not recognized by the system..."); break; 266. case 2 : Print("The system did not expect the contents of the line: ", m_GlPrivate.Line); break; 267. default : Print("Error accessing the file indicated in the line: ", m_GlPrivate.Line); 268. } 269. 270. return (cError == 0 ? !_StopFlag : false); 271. #undef macroFileName 272. } 273. //+------------------------------------------------------------------+ 274. }; 275. //+------------------------------------------------------------------+
Código fonte do arquivo C_ConfigService.mqh
Apesar de terem ocorrido algumas mudanças, algumas mais sutis e outra nem tanto, como as partes que foram riscadas. Você deve notar que, agora não mais iremos acessar os dados dos ticks diretamente. A forma de fazer isto, agora é como pode ser visto na linha 34. Observe que podemos acessar as coisas de uma forma bastante interessante, mas principalmente segura, já que não podemos manipular os dados que estavam configurados no arquivo de script, ou nos dados presentes nos arquivos de tick ou barras. Então definitivamente, agora temos um sistema bem mais seguro e estável. Podendo ser programado, de maneira consideravelmente mais fácil e com bem menos possibilidades de falhas.
Mas além disto, quero chamar a sua atenção, caro leitor, para uma outra coisa. Observe que entre as linhas 23 e 27, as variáveis globais privativas foram removidas. Apesar de que algumas, foram lançadas para dentro da estrutura que se inicia na linha 15. Mas por que desta minha mudança, com relação as variáveis globais e privativas? O motivo, é que não quero, ou melhor, não precisamos que algumas coisas façam parte da configuração geral do serviço de replay/simulação.
Por conta desta mudança vista entre as linhas 23 e 27, diversos pontos no código foram mudados. Mas eles não merecem assim um destaque especial, já que são coisas simples e de fácil compreensão. Mas mesmo assim, observe o constructor da classe, este pode ser visto na linha 180. Note que o código deste constructor está ligeiramente diferente. Mas isto não é a questão primordial, a ponto de que este arquivo de cabeçalho, precise ser comentado. O ponto é entre as linhas 207 e 209. Note que elas estão riscadas.
Antes de corrigir o problema de encapsulamento da classe C_FileTicks, a variável m_Ticks, que estava na cláusula protected, era de fato instanciada, ou melhor dizendo, inicializada, aqui, nestas linhas.
Notaram como a coisa é perigosa, apesar de que o código estava funcionado de forma bastante estável, o mesmo não era de fato seguro, para que viéssemos a criar grandes modificações. Isto foi uma falha minha, que permaneceu até este momento. Mas como preciso fazer com que as coisas funcionem de uma maneira bastante específica. Não é bom deixar informações vazando e serem modificadas em qualquer ponto do código.
Com estas modificações, agora as nossas classes que irão manter os ticks, as barras e configurar o ativo a ser utilizado no replay/simulação, estão completamente seguras, não contando com nenhum vazamento de informações. Ou seja, fechamos definitivamente esta parte, referente ao carregamento e simulação dos dados, assim como a configuração do ativo, a serem utilizados pelo serviço de replay/simulador. Podemos agora focar na questão de fazer com que o serviço volte a funcionar, dando play e pause, assim como permitindo que o usuário posicione o replay/simulação para iniciar em um dado ponto.
Tais coisas já eram feitas antes de tantas modificações. Mas agora iremos fazer com que elas aconteçam de maneira muito mais segura, simples e estável. Assim como a interação entre os elementos que iremos trabalhar no futuro próximo.
Permitindo o usuário dar play e pausar o replay/simulação
Neste tópico, mostrarei como finalmente, fazer o serviço funcionar, obedecendo ao clique do usuário no botão de play e pause. Fazendo assim com que tanto o replay como a simulação aconteçam.
Mas antes, deixe-me mostrar a você, caro leitor, duas pequenas atualizações que aconteceram no modulo do indicador de controle. Estas atualizações visam evitar uma coisa bastante chata que as vezes acontece, quando o sistema está em funcionamento. Por se tratar de algo randômico, é difícil de ser mostrado, já que você pode executar o sistema diversas vezes e nada acontecer de estranho. Mas, de vez em quando, algo bizarro acontece. Então para evitar toda a chateação que nos é causada por conta de uma falha randômica. Fiz algumas mudanças no código do modulo de controle.
Então no arquivo de cabeçalho C_Replay.mqh, atualize as seguintes funções e procedimentos, que podem ser vistas nos fragmentos a seguir. Começando com o seguinte ponto.
71. //+------------------------------------------------------------------+ 72. void SetPlay(bool state) 73. { 74. if (m_Section[ePlay].Btn == NULL) 75. m_Section[ePlay].Btn = new C_DrawImage(GetInfoTerminal().ID, 0, def_PrefixCtrlName + EnumToString(ePlay), def_ColorFilter, "::" + def_ButtonPlay, "::" + def_ButtonPause); 76. m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, ((m_Section[ePlay].state = state) ? 0 : 1)); 77. if (!state) CreateCtrlSlider(); 78. } 79. //+------------------------------------------------------------------+
Fragmento de código do arquivo C_Controls.mqh
Você precisa adicionar a linha 77 ao código original. Desta forma, o modulo do indicador de controle, sempre irá saber o que fazer, e irá mostrar a barra do controle deslizante caso o botão de play esteja visível, ou seja quando você estiver no modo pausado, a barra de controle deslizante irá aparecer no gráfico. As vezes isto não acontecia, mas desta forma, a barra sempre irá aparecer.
Na sequência, é necessário fazer uma outra mudança. Esta deverá ser feita no fragmento mostrado abaixo.
170. //+------------------------------------------------------------------+ 171. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 172. { 173. short x, y; 174. static short iPinPosX = -1, six = -1, sps; 175. uCast_Double info; 176. 177. switch (id) 178. { 179. case (CHARTEVENT_CUSTOM + evCtrlReplayInit): 180. info.dValue = dparam; 181. if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break; 182. x = (short) info._16b[0]; 183. iPinPosX = m_Slider.Minimal = (x > def_MaxPosSlider ? def_MaxPosSlider : (x < iPinPosX ? iPinPosX : x)); 184. SetPlay((short)(info._16b[1]) == SHORT_MAX); 185. break; 186. case CHARTEVENT_OBJECT_DELETE:
Fragmento de código do arquivo C_Controls.mqh
Neste fragmento, você deverá modificar o código original, e nele colocar o que se encontra entre a linhas 180 e 185. A intenção aqui e fazer com que o modulo do indicador de controle fique um pouco mais resistente a um tipo de falha, bastante chata. Que as vezes acontece, onde o evento customizado que dispara a execução da linha 179. Fazendo com que coisas estranhas venham a ocorrer. Uma destas coisas, é a chegada de valores erráticos no índex um do array. Originalmente quando isto acontecia, era informado um erro e o módulo de controle era removido do gráfico, fazendo assim, com que o serviço de replay/simulação, também sofresse um CRASH. Ou seja, o serviço era finalizado, por conta de algo que acontece de forma randômica e em RUN TIME, faz com que um valor errado viesse a aparecer.
Da forma como está agora, caso uma falha aconteça, o modulo do indicador de controle irá imediatamente entrar em modo pausado. O que é melhor do que um CRASH de todo o sistema de replay/simulação. Mas existe um segundo detalhe aqui. Se você não notou, na linha 181, existe um pequeno teste, este garante que, mesmo quando valores erráticos venham a aparecer, iremos somente fazer uso deles, se e somente se, os dois primeiros bytes, dos 8 presentes no valor double, contiverem algo bastante especifico. Então caso tais valores não estejam presentes, o evento irá simplesmente ser ignorado pelo modulo do indicador de controle. Este tipo de coisa, é uma forma de garantir a integridade, ou no mínimo, que os dados tenham vindo de uma fonte especifica, que no caso será o serviço de replay/simulação.
Depois de ter feito estas duas modificações, será preciso fazer uma outra. Mas desta vez no código fonte do indicador. Tal modificação pode ser vista no fragmento que você encontra logo a seguir.
41. //+------------------------------------------------------------------+ 42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 43. { 44. (*control).DispatchMessage(id, lparam, dparam, sparam); 45. if (_LastError >= ERR_USER_ERROR_FIRST + C_Terminal::ERR_Unknown) 46. { 47. Print("Internal failure in the messaging system..."); 48. ChartClose(user00); 49. } 50. (*control).SetBuffer(m_RatesTotal, m_Buff); 51. } 52. //+------------------------------------------------------------------+
Fragmento do código fonte do indicador de controle
As linhas riscadas deverão ser removidas do código fonte. Desta maneira, apague o arquivo executável do módulo de controle, a fim de que o serviço possa forçar uma nova compilação do mesmo, assim que você for tentar uma nova compilação do serviço. Ou se preferir, faça uma nova compilação do módulo de controle, a fim de garantir que a versão mais atualizada seja colocada como sendo um recurso do serviço de replay/simulação.
Agora podemos partir para o código do serviço de replay/simulação. Então vamos promover algumas mudanças no código que existia até o presente momento, desde a última atualização que foi promovida no arquivo de cabeçalho C_Replay.mqh. Estas mudanças foram feitas em um artigo passado. Nela, tudo que era de fato feito, seria a apresentada de alguns dados, que fossem carregados pelo sistema, junto com os módulos de controle e de mouse. Porém, apesar disto, não podíamos fazer nada além de ter uma interação entre o indicador de controle e o indicador de mouse, mas sem necessariamente nos permitir dar play ou pausar o serviço de replay/simulação, a fim de que novos dados fossem apresentados.
Mas aqui iremos mudar isto. Então vamos ver, como ficou a cara do novo arquivo de cabeçalho C_Replay.mqh. O código pode ser visto logo abaixo na integra.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_ConfigService.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_IndicatorControl "Indicators\\Market Replay.ex5" 007. #resource "\\" + def_IndicatorControl 008. //+------------------------------------------------------------------+ 009. #define def_CheckLoopService ((!_StopFlag) && (ChartSymbol(m_Infos.IdReplay) != "")) 010. //+------------------------------------------------------------------+ 011. #define def_ShortNameIndControl "Market Replay Control" 012. //+------------------------------------------------------------------+ 013. class C_Replay : public C_ConfigService 014. { 015. private : 016. struct st00 017. { 018. ushort Position; 019. short Mode; 020. int Handle; 021. }m_IndControl; 022. struct st01 023. { 024. long IdReplay; 025. int CountReplay; 026. double PointsPerTick; 027. MqlTick tick[1]; 028. MqlRates Rate[1]; 029. }m_Infos; 030. //+------------------------------------------------------------------+ 031. inline bool MsgError(string sz0) { Print(sz0); return false; } 032. //+------------------------------------------------------------------+ 033. inline void UpdateIndicatorControl(void) 034. { 035. uCast_Double info; 036. double Buff[]; 037. 038. info.dValue = 0; 039. if (m_IndControl.Handle == INVALID_HANDLE) return; 040. if (CopyBuffer(m_IndControl.Handle, 0, 0, 1, Buff) == 1) 041. info.dValue = Buff[0]; 042. if ((short)(info._16b[0]) != SHORT_MIN) 043. m_IndControl.Mode = (short)info._16b[1]; 044. if (info._16b[0] != m_IndControl.Position) 045. { 046. if (((short)(info._16b[0]) != SHORT_MIN) && ((short)(info._16b[1]) == SHORT_MAX)) 047. m_IndControl.Position = info._16b[0]; 048. info._16b[0] = m_IndControl.Position; 049. info._16b[1] = (ushort)m_IndControl.Mode; 050. info._8b[7] = 'D'; 051. info._8b[6] = 'M'; 052. EventChartCustom(m_Infos.IdReplay, evCtrlReplayInit, 0, info.dValue, ""); 053. } 054. } 055. //+------------------------------------------------------------------+ 056. void SweepAndCloseChart(void) 057. { 058. long id; 059. 060. if ((id = ChartFirst()) > 0) do 061. { 062. if (ChartSymbol(id) == def_SymbolReplay) 063. ChartClose(id); 064. }while ((id = ChartNext(id)) > 0); 065. } 066. //+------------------------------------------------------------------+ 067. inline void CreateBarInReplay(bool bViewTick) 068. { 069. bool bNew; 070. double dSpread; 071. int iRand = rand(); 072. 073. if (BuildBar1Min(m_Infos.CountReplay, m_Infos.Rate[0], bNew)) 074. { 075. m_Infos.tick[0] = GetInfoTicks().Info[m_Infos.CountReplay]; 076. if (GetInfoTicks().ModePlot == PRICE_EXCHANGE) 077. { 078. dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 ); 079. if (m_Infos.tick[0].last > m_Infos.tick[0].ask) 080. { 081. m_Infos.tick[0].ask = m_Infos.tick[0].last; 082. m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread; 083. }else if (m_Infos.tick[0].last < m_Infos.tick[0].bid) 084. { 085. m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread; 086. m_Infos.tick[0].bid = m_Infos.tick[0].last; 087. } 088. } 089. if (bViewTick) CustomTicksAdd(def_SymbolReplay, m_Infos.tick); 090. CustomRatesUpdate(def_SymbolReplay, m_Infos.Rate); 091. } 092. m_Infos.CountReplay++; 093. } 094. //+------------------------------------------------------------------+ 095. void AdjustViewDetails(void) 096. { 097. MqlRates rate[1]; 098. 099. ChartSetInteger(m_Infos.IdReplay, CHART_SHOW_ASK_LINE, GetInfoTicks().ModePlot == PRICE_FOREX); 100. ChartSetInteger(m_Infos.IdReplay, CHART_SHOW_BID_LINE, GetInfoTicks().ModePlot == PRICE_FOREX); 101. ChartSetInteger(m_Infos.IdReplay, CHART_SHOW_LAST_LINE, GetInfoTicks().ModePlot == PRICE_EXCHANGE); 102. m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE); 103. CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, rate); 104. if ((m_Infos.CountReplay == 0) && (GetInfoTicks().ModePlot == PRICE_EXCHANGE)) 105. for (; GetInfoTicks().Info[m_Infos.CountReplay].volume_real == 0; m_Infos.CountReplay++); 106. if (rate[0].close > 0) 107. { 108. if (GetInfoTicks().ModePlot == PRICE_EXCHANGE) 109. m_Infos.tick[0].last = rate[0].close; 110. else 111. { 112. m_Infos.tick[0].bid = rate[0].close; 113. m_Infos.tick[0].ask = rate[0].close + (rate[0].spread * m_Infos.PointsPerTick); 114. } 115. m_Infos.tick[0].time = rate[0].time; 116. m_Infos.tick[0].time_msc = rate[0].time * 1000; 117. }else 118. m_Infos.tick[0] = GetInfoTicks().Info[m_Infos.CountReplay]; 119. CustomTicksAdd(def_SymbolReplay, m_Infos.tick); 120. } 121. //+------------------------------------------------------------------+ 122. public : 123. //+------------------------------------------------------------------+ 124. C_Replay() 125. :C_ConfigService() 126. { 127. Print("************** Market Replay Service **************"); 128. srand(GetTickCount()); 129. SymbolSelect(def_SymbolReplay, false); 130. CustomSymbolDelete(def_SymbolReplay); 131. CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay)); 132. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0); 133. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0); 134. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0); 135. CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation"); 136. CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8); 137. SymbolSelect(def_SymbolReplay, true); 138. m_Infos.CountReplay = 0; 139. m_IndControl.Handle = INVALID_HANDLE; 140. m_IndControl.Mode = 0; 141. m_IndControl.Position = 0; 142. } 143. //+------------------------------------------------------------------+ 144. ~C_Replay() 145. { 146. IndicatorRelease(m_IndControl.Handle); 147. SweepAndCloseChart(); 148. SymbolSelect(def_SymbolReplay, false); 149. CustomSymbolDelete(def_SymbolReplay); 150. Print("Finished replay service..."); 151. } 152. //+------------------------------------------------------------------+ 153. bool OpenChartReplay(const ENUM_TIMEFRAMES arg1, const string szNameTemplate) 154. { 155. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0) 156. return MsgError("Asset configuration is not complete, it remains to declare the size of the ticket."); 157. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0) 158. return MsgError("Asset configuration is not complete, need to declare the ticket value."); 159. if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0) 160. return MsgError("Asset configuration not complete, need to declare the minimum volume."); 161. SweepAndCloseChart(); 162. m_Infos.IdReplay = ChartOpen(def_SymbolReplay, arg1); 163. if (!ChartApplyTemplate(m_Infos.IdReplay, szNameTemplate + ".tpl")) 164. Print("Failed apply template: ", szNameTemplate, ".tpl Using template default.tpl"); 165. else 166. Print("Apply template: ", szNameTemplate, ".tpl"); 167. 168. return true; 169. } 170. //+------------------------------------------------------------------+ 171. bool InitBaseControl(const ushort wait = 1000) 172. { 173. Print("Waiting for Mouse Indicator..."); 174. Sleep(wait); 175. while ((def_CheckLoopService) && (ChartIndicatorGet(m_Infos.IdReplay, 0, "Indicator Mouse Study") == INVALID_HANDLE)) Sleep(200); 176. if (def_CheckLoopService) 177. { 178. AdjustViewDetails(); 179. Print("Waiting for Control Indicator..."); 180. if ((m_IndControl.Handle = iCustom(ChartSymbol(m_Infos.IdReplay), ChartPeriod(m_Infos.IdReplay), "::" + def_IndicatorControl, m_Infos.IdReplay)) == INVALID_HANDLE) return false; 181. ChartIndicatorAdd(m_Infos.IdReplay, 0, m_IndControl.Handle); 182. m_IndControl.Position = 0; 183. m_IndControl.Mode = SHORT_MIN; 184. UpdateIndicatorControl(); 185. } 186. 187. return def_CheckLoopService; 188. } 189. //+------------------------------------------------------------------+ 190. bool LoopEventOnTime(void) 191. { 192. int iPos; 193. 194. while ((def_CheckLoopService) && (m_IndControl.Mode != SHORT_MAX)) 195. { 196. UpdateIndicatorControl(); 197. Sleep(200); 198. } 199. iPos = 0; 200. while ((m_Infos.CountReplay < GetInfoTicks().nTicks) && (def_CheckLoopService)) 201. { 202. if (m_IndControl.Mode == SHORT_MIN) return true; 203. iPos += (int)(m_Infos.CountReplay < (GetInfoTicks().nTicks - 1) ? GetInfoTicks().Info[m_Infos.CountReplay + 1].time_msc - GetInfoTicks().Info[m_Infos.CountReplay].time_msc : 0); 204. CreateBarInReplay(true); 205. while ((iPos > 200) && (def_CheckLoopService)) 206. { 207. Sleep(195); 208. iPos -= 200; 209. m_IndControl.Position = (ushort)((m_Infos.CountReplay * def_MaxPosSlider) / GetInfoTicks().nTicks); 210. UpdateIndicatorControl(); 211. } 212. } 213. 214. return (m_Infos.CountReplay == GetInfoTicks().nTicks); 215. } 216. //+------------------------------------------------------------------+ 217. }; 218. //+------------------------------------------------------------------+ 219. #undef macroRemoveSec 220. #undef def_SymbolReplay 221. #undef def_CheckLoopService 222. //+------------------------------------------------------------------+
Código fonte do arquivo C_Replay.mqh
Já que este código precisou passar por algumas mudanças, explicarei um pouco sobre o que aconteceu, e o que estas mudanças representam de fato. Isto para que o funcionamento do sistema, seja devidamente compreendido. Já que nesta versão que está sendo vista acima, contém alguns problemas que explicarei mais no final deste artigo.
Para começar, decidi colocar as informações em estruturas, visto que isto torna mais simples separar elas, de uma maneira mais lógica. Mas reparem na linha 20, onde declaro o handle para acessar o modulo do indicador de controle. Isto evita um número muito grande de chamadas apenas para capturar este valor, que será utilizado por todo o tempo de vida que o serviço estiver ativo. Então não faz sentido, usar de outra forma, mesmo por que iremos fazer muitos acessos ao módulo de controle. Assim, qualquer economia em termos de tempo, entre as chamadas, irá ser bastante proveitoso. E logo você irá entender o motivo disto.
O procedimento presente entre as linhas 33 e 54, não sofreu grandes mudanças, a não ser o fato de ter sido retirado, o código que obtinha e liberava o manipulador para acessar o buffer do módulo de comando. Fora isto, este procedimento, recebeu apenas uma mudança, e esta é devido ao fragmento modificado no arquivo de cabeçalho C_Replay.mqh.
Note que nas linhas 50 e 51, estou colocando as mesmas informações, que são esperadas pelo evento customizado, dentro do tratador de mensagens da classe C_Replay. Isto garante que a informação repassada pelo serviço, seja de fato aceita pelo módulo de controle.
O próximo procedimento a ser observado, se encontra entre as linhas 67 e 93. Este procedimento é responsável por criar e lançar as informações referentes as barras e ticks para dentro do ativo customizado, ou seja, é neste ponto que atualizamos as informações para que o MetaTrader 5, nos mostre elas no gráfico. Este procedimento é praticamente idêntico ao que existia na última versão do serviço de replay/simulação. E isto lá atrás, isto quando ainda usávamos as variáveis globais de terminal como forma de trocar mensagens entre as aplicações. No entanto, devido ao fato de termos corrigido o vazamento de informações da classe C_FileTicks, somos obrigados a reajustar algumas coisas aqui. Então observe como fazemos para acessar os dados presentes lá na classe C_FileTicks. Um exemplo disto está na linha 75. Apesar de isto funcionar, temos que considerar algo aqui. O pequeno atraso que cada uma destas chamadas causa. Mas irei falar disto depois.
Foi necessário adicionar um outro procedimento, que também existia nas versões bem antigas do serviço. Este procedimento, pode ser visto entre as linhas 95 e 120. O mesmo tem como proposito iniciar a apresentação das linhas de preço, ou de cotação. Se você observar, no último artigo, em que este arquivo de cabeçalho C_Replay.mqh, havia aparecido, não tínhamos este procedimento. Por este motivo, quando o serviço de replay/simulador dizia ao MetaTrader 5, para abrir o gráfico do ativo customizado, não era visível as linhas de cotação. Mas este procedimento corrige justamente isto. Mas diferente do que você pode estar imaginando, este procedimento é chamado apenas uma única vez. Já que durante a fase em que o serviço de encontra em modo de play, que será responsável por atualizar esta apresentação, será o procedimento CreateBarInReplay, que está presente na linha 67. Para ser mais especifico, será justamente a linha 89 deste procedimento.
Continuando no desenrolar das coisas. Chegamos ao constructor da classe. Aqui basicamente o que foi feito, é a adição das linhas responsáveis por inicializar alguns dados. Estas linhas estão entre 138 e 141. Fora isto nada mais aconteceu. No entanto, quando olhamos o destructor da classe, vemos que uma nova linha surgiu. Esta é a linha 146, onde removermos o manipulador. Se bem que isto é meio destacável, já que quando fechamos o gráfico, assim como acontece o encerramento do serviço, manter ou não os dados, não faz muito sentido. Mas de qualquer forma, prefiro garantir que o MetaTrader 5, esteja ciente de que o manipulador não é mais necessário. Por conta disto que tal linha foi adicionada ao código.
Pois bem, se estamos liberando o manipulador, significa que em algum momento o carregamos. De fato, isto acontece no procedimento presente entre as linhas 171 e 188. Um detalhe extra: Na linha 178, fazemos exatamente o que não era feito antes, ou seja, chamamos o procedimento que irá apresentar as linhas de cotação. Mas é na linha 180, que capturamos o manipulador que será usado a fim de podermos conseguir acessar o buffer do indicador de controle. O restante do procedimento continua igual.
E finalmente, chegamos na última função desta classe. Talvez esta seja a função, que no geral mais irá nos dar dor de cabeça. Isto por conta que diferente de todas as demais funções e procedimentos, a serem de fato construídos e programados, onde o tempo de execução não é tal presente, ou seja, a um procedimento pode demorar 1 ou 2 segundos para ser concluído, isto será aceitável dentro do possível. Mas este presente entre as linhas 190 e 215, não nos permite fazer as coisas assim.
No passado, quando ainda estávamos usando as variáveis globais de terminal. Esta mesma função nos deu bastante dor de cabeça para que funcionasse de forma adequada. Tanto que foi necessário tomar diversas medidas, algumas até bem drásticas a ponto de que as coisas funcionassem adequadamente. Mas agora, e estamos usando um outro meio de fazer as coisas, nosso carrasco chamado de LoopEventOnTime, voltou a ressuscitar. Para nos atormentar mais uma vez.
Mas antes de começar o martilho, e uma nova via crucis, vamos ver o que estamos fazendo agora.
Muito bem, entre a linhas 194 e 198, temos um laço. Este não nos causa nenhum aborrecimento, já que a intenção dele é esperar que o usuário venha a interagir com o módulo de controle, usando o módulo de mouse, a fim de liberar o serviço a dar play no replay ou simulação. Mas caso o usuário venha a fechar o gráfico, ou finalizar o serviço, este laço também será encerrado, devido a checagem que estamos fazendo usando a definição def_CheckLoopService. Esta foi declarada lá na linha nove, onde você pode verificar, que estamos sempre olhando, se o gráfico foi fechado, ou o serviço encerrado. Mas de qualquer forma este laço não nos causa nenhum aborrecimento.
Mas o mesmo não pode ser dito, sobre o próximo laço, que se inicia na linha 200 e vai até a linha 212. Você talvez pode achar que este laço é novo, e que por conta disto poderemos vim a ter problemas. No entanto, este laço deriva exatamente do laço em que usávamos as variáveis globais de terminal. Se bem, que este daqui, ainda está consideravelmente mais simples do que aquele laço presente na fase das variáveis globais de terminal. Você pode compara ambos para constatar o que estou dizendo, mas acabará notando que neste ainda faltam algumas informações a serem adicionadas a fim de que o comportamento do serviço de replay/simulador, venha a corresponder ao mesmo comportamento que existia lá.
Mas de qualquer forma, este laço conta com um laço interno. O motivo da existência deste laço interno foi explicado lá atrás. Isto quando mostrei que em certos cenários, ficávamos impedidos de interagir com o sistema, sendo forçados a ter que esperar que um tick viesse a liberar o serviço para nós. Procure ver os primeiros artigos desta série sobre a construção do replay/simulador, para entender a que estou me referindo.
Porém, apenas observando este código, você pode pensar que está tudo perfeito. E que somente está faltando, o código que irá permitir, que possamos ajustar o ponto onde o replay ou simulação, irá ser iniciado. De fato, um pouco de otimismo nunca faz mal. Mas antes de ver o resultado, irei mostrar o código do serviço. Apesar deste não ter sofrido nenhuma mudança deste o último artigo, quero que você de fato veja, e perceba onde está nosso real problema.
01. //+------------------------------------------------------------------+ 02. #property service 03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico" 04. #property copyright "Daniel Jose" 05. #property version "1.60" 06. #property description "Replay-Simulator service for MetaTrade 5 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/12086" 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Service Graphics\C_Replay.mqh> 12. //+------------------------------------------------------------------+ 13. input string user00 = "Mini Dolar.txt"; //Replay Configuration File. 14. input ENUM_TIMEFRAMES user01 = PERIOD_M5; //Initial Graphic Time. 15. input string user02 = "Default"; //Template File Name 16. //+------------------------------------------------------------------+ 17. C_Replay *pReplay; 18. //+------------------------------------------------------------------+ 19. void OnStart() 20. { 21. pReplay = new C_Replay(); 22. 23. UsingReplay(); 24. 25. delete pReplay; 26. } 27. //+------------------------------------------------------------------+ 28. void UsingReplay(void) 29. { 30. if (!(*pReplay).SetSymbolReplay(user00)) return; 31. if (!(*pReplay).OpenChartReplay(user01, user02)) return; 32. if (!(*pReplay).InitBaseControl()) return; 33. Print("Permission granted. Replay service can now be used..."); 34. while ((*pReplay).LoopEventOnTime()); 35. } 36. //+------------------------------------------------------------------+
Código fonte do serviço
Note que não mudou nada, assim tal relevante no código.
Conclusão
Mas para que você não precise de fato, compilar e testar o serviço de replay/simulação. Estou colocando um vídeo para isto. Veja ele com atenção, já que se você observar com calma, irá notar uma drástica queda no desempenho do serviço. Mas por que o desempenho caiu tanto? O motivo é justamente o fato de que, existe uma quantidade maior de chamadas e menos uso de variáveis no código. Mas não é somente isto. Existem algumas outras coisas também envolvidas aqui. E já que isto é algo que demanda tempo e paciência para ser explicado, analisado e testado, irei deixar tais explicações para um próximo artigo.
No entanto, o foco principal será justamente o laço presente na função LoopEventOnTime. De alguma forma, ou maneira, precisaremos fazer com que aquele laço presente, e responsável por criar e permitir que o replay ou simulação aconteça, seja executado de uma maneira mais eficiente. Pois como falei durante a explicação do artigo, existem coisas que estão minando o desempenho do serviço, no momento em que ele mais precisa de desempenho. E já que durante a fase em que era usado variáveis globais de terminal, o desempenho estava e se encontrava dentro de algo aceitável. Isto que é visto no vídeo torna completamente inaceitável qualquer tipo de uso do novo modelo.
Bem, isto seria algo que qualquer um poderia pensar. Mas nos programadores, não apenas temos que solucionar problemas relacionados à forma de efetuar cálculos. Muitas vezes temos que resolver problemas relacionados a desempenho. E como o sistema de classes no geral, tornou ou código muito mais estável e seguro, da forma como se encontra agora. Então nosso real problema é conseguir fazer com que o desempenho, volte a ser o mesmo, ou algo próximo do que tínhamos durante a fase em que usávamos a variáveis globais de terminal. Mas agora usando uma comunicação via eventos e buffers. Desta forma, preparem-se para o próximo artigo. Pois a coisa será bastante interessante.
Vídeo de demonstração
![Caminhe em novos trilhos: Personalize indicadores no MQL5](https://c.mql5.com/2/0/862_14.png)
![Importância da qualidade do gerador de números aleatórios no desempenho dos algoritmos de otimização](https://c.mql5.com/2/73/The_role_of_the_quality_of_the_random_number_generator___LOGO.png)
![Está chegando o novo MetaTrader 5 e MQL5](https://c.mql5.com/2/0/851_25.jpg)
![Gerenciador de risco para operar manualmente](https://c.mql5.com/2/73/Risk_manager_for_manual_trading__LOGO.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso