Biblioteca para desenvolvimento fácil e rápido de programas para a MetaTrader (parte VI): eventos da conta netting
Artyom Trishkin | 9 agosto, 2019
Conteúdo
- Semelhanças e diferenças entre os tipos de conta
- Implementação do tratamento de eventos em uma conta netting
- Teste de desempenho em contas hedging e netting
- Qual é o próximo?
Semelhanças e diferenças entre os tipos de conta
Para monitorar os eventos da conta netting, nós precisamos entender as diferenças entre as contas hedging e netting.
As diferenças dizem respeito à representação das posições. Uma conta hedging nos permite abrir qualquer número de posições em um único símbolo, enquanto uma conta netting permite apenas uma. A conta hedging permite o encerramento de uma posição pelo volume de uma outra posição de direção oposta.
Nesse caso:
- Se o volume da posição oposta for menor que o volume da posição de encerramento, então a posição oposta é encerrada completamente, enquanto a posição de encerramento é eliminada parcialmente,
- Se o volume da posição oposta for maior que o volume da posição de encerramento, então a posição oposta é parcialmente encerrada, enquanto a posição de encerramento é eliminada por completo,
- Se as duas posições tiverem volumes iguais, ambas são encerradas;
- Cada posição tem um ID igual ao ticket da ordem de abertura. Este ID não muda durante toda a vida útil da posição;
- Cada posição tem seu próprio ticket igual ao ticket da ordem que levou à abertura da posição;
- Se nós enviarmos uma solicitação para abrir uma nova posição na direção da posição atual, uma nova posição com um novo ID e ticket será aberta.
Em uma conta netting, o trabalho com uma posição em um símbolo exclui a possibilidade de encerrar uma posição por uma posição oposta. No entanto, em uma situação remotamente semelhante (quando uma ordem de direção oposta é ativada), essa posição pode ser encerrada parcialmente, totalmente ou alterar a sua direção:
- Se o volume de uma ordem oposta ativada for menor que a posição atual, a posição é encerrada parcialmente,
- Se o volume de uma ordem oposta ativada for igual à posição atual, a posição é encerrada por completo,
- Se o volume de uma ordem oposta ativada exceder a posição atual, a direção da posição é alterada (reversão),
- Cada posição tem um ID igual ao ticket da ordem de abertura. Este ID não muda durante toda a vida útil da posição;
- Cada posição tem um ticket igual ao ticket da ordem que levou à reversão da posição. O ticket pode diferir do ID. De certo modo, ele repete os tickets de várias posições em uma conta hedging;
- Se nós enviarmos uma solicitação para abrir uma nova posição na direção da posição atual, o volume da ordem ativada será adicionado ao volume da posição atual. O ticket de posição não é alterado.
Implementação do tratamento de eventos em uma conta netting
Para monitorar os eventos da conta netting, nós simplesmente dividiremos os eventos de posição para manipulação pelos tipos de conta. Isso aumenta a quantidade de código, mas esclarece a lógica devido à separação da funcionalidade. Nós otimizaremos o código e nos livraremos de todas as redundâncias mais tarde — após a depuração e confirmação quanto a estabilidade da operação.
Ao adicionar novas constantes às enumerações de tipo de evento, eu notei que a ordenação às vezes funciona de forma incorreta. A verificação das razões de tal comportamento revelou que os dois principais fatores são as propriedades do evento/ordem que correspondem à ordenação por esse tipo e sua localização na enumeração, independentemente do fato de que cada constante é numerada. Por exemplo, se uma propriedade não for usada para busca, ela deverá ser ignorada e os números corretos deverão ser atribuídos às constantes de enumeração do método de busca. Além disso, as propriedades de evento/ordem não usadas na ordenação também devem ser colocadas no final da lista dos tipos de propriedades. Para calcular o número inicial dos seguintes tipos de propriedade, o número de propriedades não usadas dentro da quantidade dos tipos de propriedades anteriores deve ser subtraído do índice de tipos de propriedade inicial.
Para verificar a criação das enumerações do método de ordenação, uma pequena função foi adicionada ao arquivo de funções de serviço DELib.mqh:
//+------------------------------------------------------------------+ //| Display all sorting enumeration constants in the journal | //+------------------------------------------------------------------+ void EnumNumbersTest() { string enm="ENUM_SORT_ORDERS_MODE"; string t=StringSubstr(enm,5,5)+"BY"; Print("Search of the values of the enumaration ",enm,":"); ENUM_SORT_ORDERS_MODE type=0; while(StringFind(EnumToString(type),t)==0) { Print(enm,"[",type,"]=",EnumToString(type)); if(type>500) break; type++; } Print("\nNumber of members of the ",enm,"=",type); } //+------------------------------------------------------------------+
Para verificar o conteúdo de uma enumeração de tipos para ordenação, ela deve ser inserida manualmente em duas funções do tipo string (eu não encontrei uma maneira de definir automaticamente uma certa enumeração no ENUM_SORT_ORDERS_MODE type = 0; string).
Agora, se nós chamarmos essa função no manipulador OnInit() do EA de teste, todos os nomes das constantes de uma enumeração especificada e os seus índices correspondentes serão exibidos no diário.
Ao verificar as enumerações, eu detectei que elas foram criadas incorretamente. Para corrigir isso, eu modifiquei ligeiramente as enumerações no arquivo Define.mqh.
Eu defini uma ordem diferente de constantes nas enumerações — propriedades não usadas na ordenação são colocadas no final da lista de constante para a enumeração de propriedades do objeto. Além disso, deve ser adicionado as substituições de macro para especificar o número de propriedades não utilizadas para busca e ordenação. Essas substituições de macro devem ser usadas ao calcular os índices de propriedade inicial em enumerações de ordenação levando ao cálculo dos índices corretos de constantes iniciais em enumerações.
Além disso, novos tipos de constantes para os eventos em contas netting e constantes para o armazenamento do número mágico e um símbolo de posição oposta para as contas hedging deve ser adicionado.
Geralmente, ordens e posições devem ser agrupados, de modo que um grupo de ordens e posições selecionadas possa ser tratado simultaneamente. A biblioteca permite implementar isso simplesmente adicionando um ID de grupo à propriedade de ordem abstrata. Isso possibilita organizar ordens e posições com um ID semelhante em uma única lista e trabalhar com um grupo selecionado.
Tal ID foi adicionado para ordenar as propriedades e ordenar listas de classificação.
Abaixo está uma lista completa do Defines.mqh modificado:
//+------------------------------------------------------------------+ //| Defines.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ //--- "Description of a function with the error string number" #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Function description" #define COUNTRY_LANG ("Russian") // Country language #define END_TIME (D'31.12.3000 23:59:59') // End date for requesting account history data #define TIMER_FREQUENCY (16) // Minimal frequency of the library timer in milliseconds #define COLLECTION_PAUSE (250) // Orders and deals collection timer pause in milliseconds #define COLLECTION_COUNTER_STEP (16) // Increment of the orders and deals collection timer counter #define COLLECTION_COUNTER_ID (1) // Orders and deals collection timer counter ID #define COLLECTION_HISTORY_ID (0x7778+1) // Historical collection list ID #define COLLECTION_MARKET_ID (0x7778+2) // Market collection list ID #define COLLECTION_EVENTS_ID (0x7778+3) // Events collection list ID //+------------------------------------------------------------------+ //| Structures | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Search and sorting data | //+------------------------------------------------------------------+ enum ENUM_COMPARER_TYPE { EQUAL, // Equal MORE, // More LESS, // Less NO_EQUAL, // Not equal EQUAL_OR_MORE, // Equal or more EQUAL_OR_LESS // Equal or less }; //+------------------------------------------------------------------+ //| Possible options of sorting by time | //+------------------------------------------------------------------+ enum ENUM_SELECT_BY_TIME { SELECT_BY_TIME_OPEN, // By open time SELECT_BY_TIME_CLOSE, // By close time SELECT_BY_TIME_OPEN_MSC, // By open time in milliseconds SELECT_BY_TIME_CLOSE_MSC, // By close time in milliseconds }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Data for working with orders | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Abstract order type (status) | //+------------------------------------------------------------------+ enum ENUM_ORDER_STATUS { ORDER_STATUS_MARKET_PENDING, // Market pending order ORDER_STATUS_MARKET_ORDER, // Market order ORDER_STATUS_MARKET_POSITION, // Market position ORDER_STATUS_HISTORY_ORDER, // Historical market order ORDER_STATUS_HISTORY_PENDING, // Removed pending order ORDER_STATUS_BALANCE, // Balance operation ORDER_STATUS_CREDIT, // Credit operation ORDER_STATUS_DEAL, // Deal ORDER_STATUS_UNKNOWN // Unknown status }; //+------------------------------------------------------------------+ //| Order, deal, position integer properties | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // Order ticket ORDER_PROP_MAGIC, // Order magic number ORDER_PROP_TIME_OPEN, // Open time (MQL5 Deal time) ORDER_PROP_TIME_CLOSE, // Close time (MQL5 Execution or removal time - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // Open time in milliseconds (MQL5 Deal time in msc) ORDER_PROP_TIME_CLOSE_MSC, // Close time in milliseconds (MQL5 Execution or removal time - ORDER_TIME_DONE_MSC) ORDER_PROP_TIME_EXP, // Order expiration date (for pending orders) ORDER_PROP_STATUS, // Order status (from the ENUM_ORDER_STATUS enumeration) ORDER_PROP_TYPE, // Order/deal type ORDER_PROP_REASON, // Deal/order/position reason or source ORDER_PROP_STATE, // Order state (from the ENUM_ORDER_STATE enumeration) ORDER_PROP_POSITION_ID, // Position ID ORDER_PROP_POSITION_BY_ID, // Opposite position ID ORDER_PROP_DEAL_ORDER_TICKET, // Ticket of the order that triggered a deal ORDER_PROP_DEAL_ENTRY, // Deal direction – IN, OUT or IN/OUT ORDER_PROP_TIME_UPDATE, // Position change time in seconds ORDER_PROP_TIME_UPDATE_MSC, // Position change time in milliseconds ORDER_PROP_TICKET_FROM, // Parent order ticket ORDER_PROP_TICKET_TO, // Derived order ticket ORDER_PROP_PROFIT_PT, // Profit in points ORDER_PROP_CLOSE_BY_SL, // Flag of closing by StopLoss ORDER_PROP_CLOSE_BY_TP, // Flag of closing by TakeProfit ORDER_PROP_GROUP_ID, // Order/position group ID ORDER_PROP_DIRECTION, // Direction-based type (Buy, Sell) }; #define ORDER_PROP_INTEGER_TOTAL (24) // Total number of integer properties #define ORDER_PROP_INTEGER_SKIP (1) // Number of order properties not used in sorting //+------------------------------------------------------------------+ //| Order, deal, position real properties | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_DOUBLE { ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL, // Open price (MQL5 deal price) ORDER_PROP_PRICE_CLOSE, // Close price ORDER_PROP_SL, // StopLoss price ORDER_PROP_TP, // TakeProfit price ORDER_PROP_PROFIT, // Profit ORDER_PROP_COMMISSION, // Commission ORDER_PROP_SWAP, // Swap ORDER_PROP_VOLUME, // Volume ORDER_PROP_VOLUME_CURRENT, // Unexecuted volume ORDER_PROP_PROFIT_FULL, // Profit+commission+swap ORDER_PROP_PRICE_STOP_LIMIT, // Limit order price when StopLimit order is activated }; #define ORDER_PROP_DOUBLE_TOTAL (11) // Total number of real properties //+------------------------------------------------------------------+ //| Order, deal, position string properties | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_STRING { ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Order symbol ORDER_PROP_COMMENT, // Order comment ORDER_PROP_EXT_ID // Order ID in an external trading system }; #define ORDER_PROP_STRING_TOTAL (3) // Total number of string properties //+------------------------------------------------------------------+ //| Possible criteria of orders and deals sorting | //+------------------------------------------------------------------+ #define FIRST_ORD_DBL_PROP (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP) #define FIRST_ORD_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP) enum ENUM_SORT_ORDERS_MODE { //--- Sort by integer properties SORT_BY_ORDER_TICKET = 0, // Sort by order ticket SORT_BY_ORDER_MAGIC = 1, // Sort by order magic number SORT_BY_ORDER_TIME_OPEN = 2, // Sort by order open time SORT_BY_ORDER_TIME_CLOSE = 3, // Sort by order close time SORT_BY_ORDER_TIME_OPEN_MSC = 4, // Sort by order open time in milliseconds SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // Sort by order close time in milliseconds SORT_BY_ORDER_TIME_EXP = 6, // Sort by order expiration date SORT_BY_ORDER_STATUS = 7, // Sort by order status (market order/pending order/deal/balance credit operation) SORT_BY_ORDER_TYPE = 8, // Sort by order type SORT_BY_ORDER_REASON = 9, // Sort by deal/order/position reason/source SORT_BY_ORDER_STATE = 10, // Sort by order state SORT_BY_ORDER_POSITION_ID = 11, // Sort by position ID SORT_BY_ORDER_POSITION_BY_ID = 12, // Sort by opposite position ID SORT_BY_ORDER_DEAL_ORDER = 13, // Sort by order a deal is based on SORT_BY_ORDER_DEAL_ENTRY = 14, // Sort by deal direction – IN, OUT or IN/OUT SORT_BY_ORDER_TIME_UPDATE = 15, // Sort by position change time in seconds SORT_BY_ORDER_TIME_UPDATE_MSC = 16, // Sort by position change time in milliseconds SORT_BY_ORDER_TICKET_FROM = 17, // Sort by parent order ticket SORT_BY_ORDER_TICKET_TO = 18, // Sort by derived order ticket SORT_BY_ORDER_PROFIT_PT = 19, // Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL = 20, // Sort by order closing by StopLoss flag SORT_BY_ORDER_CLOSE_BY_TP = 21, // Sort by order closing by TakeProfit flag SORT_BY_ORDER_GROUP_ID = 22, // Sort by order/position group ID //--- Sort by real properties SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, // Sort by open price SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+1, // Sort by close price SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+2, // Sort by StopLoss price SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+3, // Sort by TakeProfit price SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+4, // Sort by profit SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+5, // Sort by commission SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+6, // Sort by swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+7, // Sort by volume SORT_BY_ORDER_VOLUME_CURRENT = FIRST_ORD_DBL_PROP+8, // Sort by unexecuted volume SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+9, // Sort by profit+commission+swap criterion SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+10, // Sort by Limit order when StopLimit order is activated //--- Sort by string properties SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, // Sort by symbol SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+1, // Sort by comment SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+2 // Sort by order ID in an external trading system }; //+------------------------------------------------------------------+ //| Data for working with account events | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of account trading event flags | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT_FLAGS { TRADE_EVENT_FLAG_NO_EVENT = 0, // No event TRADE_EVENT_FLAG_ORDER_PLASED = 1, // Pending order placed TRADE_EVENT_FLAG_ORDER_REMOVED = 2, // Pending order removed TRADE_EVENT_FLAG_ORDER_ACTIVATED = 4, // Pending order activated by price TRADE_EVENT_FLAG_POSITION_OPENED = 8, // Position opened TRADE_EVENT_FLAG_POSITION_CHANGED= 16, // Position changed TRADE_EVENT_FLAG_POSITION_REVERSE= 32, // Position reversal TRADE_EVENT_FLAG_POSITION_CLOSED = 64, // Position closed TRADE_EVENT_FLAG_ACCOUNT_BALANCE = 128, // Balance operation (clarified by a deal type) TRADE_EVENT_FLAG_PARTIAL = 256, // Partial execution TRADE_EVENT_FLAG_BY_POS = 512, // Executed by opposite position TRADE_EVENT_FLAG_SL = 1024, // Executed by StopLoss TRADE_EVENT_FLAG_TP = 2048 // Executed by TakeProfit }; //+------------------------------------------------------------------+ //| List of possible trading events on the account | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT { TRADE_EVENT_NO_EVENT = 0, // No trading event TRADE_EVENT_PENDING_ORDER_PLASED, // Pending order placed TRADE_EVENT_PENDING_ORDER_REMOVED, // Pending order removed //--- enumeration members matching the ENUM_DEAL_TYPE enumeration members //--- (constant order below should not be changed, no constants should be added/deleted) TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT, // Charging credit (3) TRADE_EVENT_ACCOUNT_CHARGE, // Additional charges TRADE_EVENT_ACCOUNT_CORRECTION, // Correcting entry TRADE_EVENT_ACCOUNT_BONUS, // Charging bonuses TRADE_EVENT_ACCOUNT_COMISSION, // Additional commissions TRADE_EVENT_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a day TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a month TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY, // Agent commission charged at the end of a trading day TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY, // Agent commission charged at the end of a month TRADE_EVENT_ACCOUNT_INTEREST, // Accrual of interest on free funds TRADE_EVENT_BUY_CANCELLED, // Canceled buy deal TRADE_EVENT_SELL_CANCELLED, // Canceled sell deal TRADE_EVENT_DIVIDENT, // Accrual of dividends TRADE_EVENT_DIVIDENT_FRANKED, // Accrual of franked dividend TRADE_EVENT_TAX = DEAL_TAX, // Tax accrual //--- constants related to the DEAL_TYPE_BALANCE deal type from the ENUM_DEAL_TYPE enumeration TRADE_EVENT_ACCOUNT_BALANCE_REFILL = DEAL_TAX+1, // Replenishing account balance TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2, // Withdrawing funds from an account //--- Remaining possible trading events //--- (constant order below can be changed, constants can be added/deleted) TRADE_EVENT_PENDING_ORDER_ACTIVATED = DEAL_TAX+3, // Pending order activated by price TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL, // Pending order partially activated by price TRADE_EVENT_POSITION_OPENED, // Position opened TRADE_EVENT_POSITION_OPENED_PARTIAL, // Position opened partially TRADE_EVENT_POSITION_CLOSED, // Position closed TRADE_EVENT_POSITION_CLOSED_BY_POS, // Position closed by an opposite one TRADE_EVENT_POSITION_CLOSED_BY_SL, // Position closed by StopLoss TRADE_EVENT_POSITION_CLOSED_BY_TP, // Position closed by TakeProfit TRADE_EVENT_POSITION_REVERSED_BY_MARKET, // Position reversal by a new deal (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING, // Position reversal by activating a pending order (netting) TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL, // Position reversal by partial market order execution (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL, // Position reversal by partial pending order activation (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET, // Added volume to a position by a new deal (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL, // Added volume to a position by partial execution of a market order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING, // Added volume to a position by activating a pending order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL, // Added volume to a position by partial activation of a pending order (netting) TRADE_EVENT_POSITION_CLOSED_PARTIAL, // Position closed partially TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS, // Position closed partially by an opposite one TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL, // Position closed partially by StopLoss TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP // Position closed partially by TakeProfit }; //+------------------------------------------------------------------+ //| Event status | //+------------------------------------------------------------------+ enum ENUM_EVENT_STATUS { EVENT_STATUS_MARKET_POSITION, // Market position event (opening, partial opening, partial closing, adding volume, reversal EVENT_STATUS_MARKET_PENDING, // Market pending order event (placing) EVENT_STATUS_HISTORY_PENDING, // Historical pending order event (removal) EVENT_STATUS_HISTORY_POSITION, // Historical position event (closing) EVENT_STATUS_BALANCE, // Balance operation event (accruing balance, withdrawing funds and events from the ENUM_DEAL_TYPE enumeration) }; //+------------------------------------------------------------------+ //| Event reason | //+------------------------------------------------------------------+ enum ENUM_EVENT_REASON { EVENT_REASON_REVERSE, // Position reversal (netting) EVENT_REASON_REVERSE_PARTIALLY, // Position reversal by partial request execution (netting) EVENT_REASON_REVERSE_BY_PENDING, // Position reversal by pending order activation (netting) EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY, // Position reversal in case of a pending order partial execution (netting) //--- All constants related to a position reversal should be located in the above list EVENT_REASON_ACTIVATED_PENDING, // Pending order activation EVENT_REASON_ACTIVATED_PENDING_PARTIALLY, // Pending order partial activation EVENT_REASON_CANCEL, // Cancelation EVENT_REASON_EXPIRED, // Order expiration EVENT_REASON_DONE, // Request executed in full EVENT_REASON_DONE_PARTIALLY, // Request executed partially EVENT_REASON_VOLUME_ADD, // Add volume to a position (netting) EVENT_REASON_VOLUME_ADD_PARTIALLY, // Add volume to a position by a partial request execution (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING, // Add volume to a position when a pending order is activated (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY, // Add volume to a position when a pending order is partially executed (netting) EVENT_REASON_DONE_SL, // Closing by StopLoss EVENT_REASON_DONE_SL_PARTIALLY, // Partial closing by StopLoss EVENT_REASON_DONE_TP, // Closing by TakeProfit EVENT_REASON_DONE_TP_PARTIALLY, // Partial closing by TakeProfit EVENT_REASON_DONE_BY_POS, // Closing by an opposite position EVENT_REASON_DONE_PARTIALLY_BY_POS, // Partial closing by an opposite position EVENT_REASON_DONE_BY_POS_PARTIALLY, // Closing an opposite position by a partial volume EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY, // Partial closing of an opposite position by a partial volume //--- Constants related to DEAL_TYPE_BALANCE deal type from the ENUM_DEAL_TYPE enumeration EVENT_REASON_BALANCE_REFILL, // Refilling the balance EVENT_REASON_BALANCE_WITHDRAWAL, // Withdrawing funds from the account //--- List of constants is relevant to TRADE_EVENT_ACCOUNT_CREDIT from the ENUM_TRADE_EVENT enumeration and shifted to +13 relative to ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3) EVENT_REASON_ACCOUNT_CREDIT, // Accruing credit EVENT_REASON_ACCOUNT_CHARGE, // Additional charges EVENT_REASON_ACCOUNT_CORRECTION, // Correcting entry EVENT_REASON_ACCOUNT_BONUS, // Accruing bonuses EVENT_REASON_ACCOUNT_COMISSION, // Additional commissions EVENT_REASON_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY, // Agent commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY, // Agent commission charged at the end of a month EVENT_REASON_ACCOUNT_INTEREST, // Accruing interest on free funds EVENT_REASON_BUY_CANCELLED, // Canceled buy deal EVENT_REASON_SELL_CANCELLED, // Canceled sell deal EVENT_REASON_DIVIDENT, // Accruing dividends EVENT_REASON_DIVIDENT_FRANKED, // Accruing franked dividends EVENT_REASON_TAX // Tax }; #define REASON_EVENT_SHIFT (EVENT_REASON_ACCOUNT_CREDIT-3) //+------------------------------------------------------------------+ //| Event's integer properties | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_INTEGER { EVENT_PROP_TYPE_EVENT = 0, // Account trading event type (from the ENUM_TRADE_EVENT enumeration) EVENT_PROP_TIME_EVENT, // Event time in milliseconds EVENT_PROP_STATUS_EVENT, // Event status (from the ENUM_EVENT_STATUS enumeration) EVENT_PROP_REASON_EVENT, // Event reason (from the ENUM_EVENT_REASON enumeration) //--- EVENT_PROP_TYPE_DEAL_EVENT, // Deal event type EVENT_PROP_TICKET_DEAL_EVENT, // Deal event ticket EVENT_PROP_TYPE_ORDER_EVENT, // Type of the order, based on which a deal event is opened (the last position order) EVENT_PROP_TICKET_ORDER_EVENT, // Ticket of the order, based on which a deal event is opened (the last position order) //--- EVENT_PROP_TIME_ORDER_POSITION, // Time of the order, based on which the first position deal is opened (the first position order on a hedge account) EVENT_PROP_TYPE_ORDER_POSITION, // Type of the order, based on which the first position deal is opened (the first position order on a hedge account) EVENT_PROP_TICKET_ORDER_POSITION, // Ticket of the order, based on which the first position deal is opened (the first position order on a hedge account) EVENT_PROP_POSITION_ID, // Position ID //--- EVENT_PROP_POSITION_BY_ID, // Opposite position ID EVENT_PROP_MAGIC_ORDER, // Order/deal/position magic number EVENT_PROP_MAGIC_BY_ID, // Opposite position magic number //--- EVENT_PROP_TYPE_ORD_POS_BEFORE, // Position type before direction changed EVENT_PROP_TICKET_ORD_POS_BEFORE, // Position order ticket before direction changed EVENT_PROP_TYPE_ORD_POS_CURRENT, // Current position type EVENT_PROP_TICKET_ORD_POS_CURRENT // Current position order ticket }; #define EVENT_PROP_INTEGER_TOTAL (19) // Total number of integer event properties #define EVENT_PROP_INTEGER_SKIP (4) // Number of event properties not used in sorting event properties //+------------------------------------------------------------------+ //| Event's real properties | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_DOUBLE { EVENT_PROP_PRICE_EVENT = EVENT_PROP_INTEGER_TOTAL, // Price an event occurred at EVENT_PROP_PRICE_OPEN, // Order/deal/position open price EVENT_PROP_PRICE_CLOSE, // Order/deal/position close price EVENT_PROP_PRICE_SL, // StopLoss order/deal/position price EVENT_PROP_PRICE_TP, // TakeProfit order/deal/position price EVENT_PROP_VOLUME_ORDER_INITIAL, // Requested order volume EVENT_PROP_VOLUME_ORDER_EXECUTED, // Executed order volume EVENT_PROP_VOLUME_ORDER_CURRENT, // Remaining order volume EVENT_PROP_VOLUME_POSITION_EXECUTED, // Current executed position volume after a deal EVENT_PROP_PROFIT // Profit }; #define EVENT_PROP_DOUBLE_TOTAL (10) // Total number of event's real properties //+------------------------------------------------------------------+ //| Event's string properties | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_STRING { EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // Order symbol EVENT_PROP_SYMBOL_BY_ID // Opposite position symbol }; #define EVENT_PROP_STRING_TOTAL (2) // Total number of event's string properties //+------------------------------------------------------------------+ //| Possible event sorting criteria | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_INTEGER_SKIP) enum ENUM_SORT_EVENTS_MODE { //--- Sort by integer properties SORT_BY_EVENT_TYPE_EVENT = 0, // Sort by event type SORT_BY_EVENT_TIME_EVENT = 1, // Sort by event time SORT_BY_EVENT_STATUS_EVENT = 2, // Sort by event status (from the ENUM_EVENT_STATUS enumeration) SORT_BY_EVENT_REASON_EVENT = 3, // Sort by event reason (from the ENUM_EVENT_REASON enumeration) SORT_BY_EVENT_TYPE_DEAL_EVENT = 4, // Sort by deal event type SORT_BY_EVENT_TICKET_DEAL_EVENT = 5, // Sort by deal event ticket SORT_BY_EVENT_TYPE_ORDER_EVENT = 6, // Sort by type of an order, based on which a deal event is opened (the last position order) SORT_BY_EVENT_TICKET_ORDER_EVENT = 7, // Sort by a ticket of the order, based on which a deal event is opened (the last position order) SORT_BY_EVENT_TIME_ORDER_POSITION = 8, // Sort by time of the order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TYPE_ORDER_POSITION = 9, // Sort by type of the order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TICKET_ORDER_POSITION = 10, // Sort by a ticket of the order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_POSITION_ID = 11, // Sort by position ID SORT_BY_EVENT_POSITION_BY_ID = 12, // Sort by opposite position ID SORT_BY_EVENT_MAGIC_ORDER = 13, // Sort by order/deal/position magic number SORT_BY_EVENT_MAGIC_BY_ID = 14, // Sort by an opposite position magic number //--- Sort by real properties SORT_BY_EVENT_PRICE_EVENT = FIRST_EVN_DBL_PROP, // Sort by a price an event occurred at SORT_BY_EVENT_PRICE_OPEN = FIRST_EVN_DBL_PROP+1, // Sort by position open price SORT_BY_EVENT_PRICE_CLOSE = FIRST_EVN_DBL_PROP+2, // Sort by position close price SORT_BY_EVENT_PRICE_SL = FIRST_EVN_DBL_PROP+3, // Sort by position's StopLoss price SORT_BY_EVENT_PRICE_TP = FIRST_EVN_DBL_PROP+4, // Sort by position's TakeProfit price SORT_BY_EVENT_VOLUME_ORDER_INITIAL = FIRST_EVN_DBL_PROP+5, // Sort by initial volume SORT_BY_EVENT_VOLUME_ORDER_EXECUTED = FIRST_EVN_DBL_PROP+6, // Sort by the current volume SORT_BY_EVENT_VOLUME_ORDER_CURRENT = FIRST_EVN_DBL_PROP+7, // Sort by remaining volume SORT_BY_EVENT_VOLUME_POSITION_EXECUTED = FIRST_EVN_DBL_PROP+8, // Sort by remaining volume SORT_BY_EVENT_PROFIT = FIRST_EVN_DBL_PROP+9, // Sort by profit //--- Sort by string properties SORT_BY_EVENT_SYMBOL = FIRST_EVN_STR_PROP, // Sort by order/position/deal symbol SORT_BY_EVENT_SYMBOL_BY_ID // Sort by an opposite position symbol }; //+------------------------------------------------------------------+
Como nós adicionamos o ID do grupo de ordens ao assunto do artigo, o objeto de ordem abstrato também deve ser alterado. Vamos adicionar o método que retorna um ID do grupo atribuído às ordens e o método de configuração do valor de um ID do grupo:
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Return (1) ticket, (2) parent order ticket, (3) derived order ticket, (4) magic number, (5) order reason, //--- (6) position ID, (7) opposite position ID, (8) group ID, (9) type, (10) flag of closing by StopLoss, //--- (11) flag of closing by TakeProfit (12) open time, (13) close time, (14) open time in milliseconds, //--- (15) close time in milliseconds, (16) expiration date, (17) state, (18) status, (19) order type by direction long Ticket(void) const { return this.GetProperty(ORDER_PROP_TICKET); } long TicketFrom(void) const { return this.GetProperty(ORDER_PROP_TICKET_FROM); } long TicketTo(void) const { return this.GetProperty(ORDER_PROP_TICKET_TO); } long Magic(void) const { return this.GetProperty(ORDER_PROP_MAGIC); } long Reason(void) const { return this.GetProperty(ORDER_PROP_REASON); } long PositionID(void) const { return this.GetProperty(ORDER_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID); } long GroupID(void) const { return this.GetProperty(ORDER_PROP_GROUP_ID); } long TypeOrder(void) const { return this.GetProperty(ORDER_PROP_TYPE); } bool IsCloseByStopLoss(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL); } bool IsCloseByTakeProfit(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP); } datetime TimeOpen(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN); } datetime TimeClose(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE); } datetime TimeOpenMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC); } datetime TimeCloseMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC); } datetime TimeExpiration(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP); } ENUM_ORDER_STATE State(void) const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE); } ENUM_ORDER_STATUS Status(void) const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS); } ENUM_ORDER_TYPE TypeByDirection(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); } //--- Return (1) open price, (2) close price, (3) profit, (4) commission, (5) swap, (6) volume, //--- (7) unexecuted volume (8) StopLoss and (9) TakeProfit (10) StopLimit order price double PriceOpen(void) const { return this.GetProperty(ORDER_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE); } double Profit(void) const { return this.GetProperty(ORDER_PROP_PROFIT); } double Comission(void) const { return this.GetProperty(ORDER_PROP_COMMISSION); } double Swap(void) const { return this.GetProperty(ORDER_PROP_SWAP); } double Volume(void) const { return this.GetProperty(ORDER_PROP_VOLUME); } double VolumeCurrent(void) const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT); } double StopLoss(void) const { return this.GetProperty(ORDER_PROP_SL); } double TakeProfit(void) const { return this.GetProperty(ORDER_PROP_TP); } double PriceStopLimit(void) const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT); } //--- Return (1) symbol, (2) comment, (3) ID at an exchange string Symbol(void) const { return this.GetProperty(ORDER_PROP_SYMBOL); } string Comment(void) const { return this.GetProperty(ORDER_PROP_COMMENT); } string ExternalID(void) const { return this.GetProperty(ORDER_PROP_EXT_ID); } //--- Get the full order profit double ProfitFull(void) const { return this.Profit()+this.Comission()+this.Swap(); } //--- Get order profit in points int ProfitInPoints(void) const; //--- Set group ID void SetGroupID(long group_id) { this.SetProperty(ORDER_PROP_GROUP_ID,group_id); }
O ID do grupo deve ser definido com o valor zero, por padrão. Para alcançar isto, defina o valor desta propriedade da ordem para zero no construtor fechado da classe COrder:
//+------------------------------------------------------------------+ //| Closed parametric constructor | //+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket) { //--- Save integer properties this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = (long)(ulong)this.OrderOpenTime(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = (long)(ulong)this.OrderCloseTime(); this.m_long_prop[ORDER_PROP_TIME_EXP] = (long)(ulong)this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID(); this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN_MSC] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE_MSC] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = (long)(ulong)this.PositionTimeUpdate(); this.m_long_prop[ORDER_PROP_TIME_UPDATE_MSC] = (long)(ulong)this.PositionTimeUpdateMSC(); //--- Save real properties this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)] = this.OrderPriceStopLimit(); //--- Save string properties this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); //--- Save additional integer properties this.m_long_prop[ORDER_PROP_PROFIT_PT] = this.ProfitInPoints(); this.m_long_prop[ORDER_PROP_TICKET_FROM] = this.OrderTicketFrom(); this.m_long_prop[ORDER_PROP_TICKET_TO] = this.OrderTicketTo(); this.m_long_prop[ORDER_PROP_CLOSE_BY_SL] = this.OrderCloseByStopLoss(); this.m_long_prop[ORDER_PROP_CLOSE_BY_TP] = this.OrderCloseByTakeProfit(); this.m_long_prop[ORDER_PROP_GROUP_ID] = 0; //--- Save additional real properties this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)] = this.ProfitFull(); } //+------------------------------------------------------------------+
Nós devemos adicionar a descrição do ID do grupo para o método que retorna a descrição desta propriedade:
//+------------------------------------------------------------------+ //| Return description of an order's integer property | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) { return ( //--- General properties property==ORDER_PROP_MAGIC ? TextByLanguage("Магик","Magic")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET ? TextByLanguage("Тикет","Ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_FROM ? TextByLanguage("Тикет родительского ордера","Parent order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_TO ? TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN ? TextByLanguage("Время открытия","Time open")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_CLOSE ? TextByLanguage("Время закрытия","Close time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_EXP ? TextByLanguage("Дата экспирации","Expiration date")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : (this.GetProperty(property)==0 ? TextByLanguage(": Не задана",": Not set") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)) ) : property==ORDER_PROP_TYPE ? TextByLanguage("Тип","Type")+": "+this.TypeDescription() : property==ORDER_PROP_DIRECTION ? TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() : property==ORDER_PROP_REASON ? TextByLanguage("Причина","Reason")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetReasonDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ORDER_TICKET ? TextByLanguage("Сделка на основании ордера с тикетом","Deal by order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ENTRY ? TextByLanguage("Направление сделки","Deal entry")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetEntryDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN_MSC ? TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_CLOSE_MSC ? TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_UPDATE ? TextByLanguage("Время изменения позиции","Position change time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0") ) : property==ORDER_PROP_TIME_UPDATE_MSC ? TextByLanguage("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ? TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" : "0") ) : property==ORDER_PROP_STATE ? TextByLanguage("Состояние","Statе")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StateDescription()+"\"" ) : //--- Additional property property==ORDER_PROP_STATUS ? TextByLanguage("Статус","Status")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StatusDescription()+"\"" ) : property==ORDER_PROP_PROFIT_PT ? TextByLanguage("Прибыль в пунктах","Profit in points")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_CLOSE_BY_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_CLOSE_BY_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_GROUP_ID ? TextByLanguage("Идентификатор группы","Group ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
Após essas alterações, você pode atribuir um ID de grupo para qualquer ordem/posição, organizando as ordens e posições em determinados grupos para trabalhar apenas com um grupo em específico. O valor 0 é atribuído por padrão a todas as posições recém abertas e às ordens definidas. No entanto, você pode atribuir outro grupo a qualquer ordem/posição usando o método SetGroupID (group_index). Além disso, você pode descobrir o grupo de qualquer ordem usando o método GroupID().
Vamos voltar a implementar o monitoramento de eventos em uma conta netting.
Para dividir a funcionalidade segundo os tipos de conta, nós vamos adicionar uma variável membro da classe à seção privada da classe de evento abstrata CEvent, que se encontra no arquivo Event.mqh:
protected: ENUM_TRADE_EVENT m_trade_event; // Trading event bool m_is_hedge; // Hedge account flag long m_chart_id; // Control program chart ID int m_digits_acc; // Number of decimal places for the account currency long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Event integer properties double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Event real properties string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Event string properties //--- return the flag presence in the trading event bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; }
Vamos adicionar a declaração dos métodos que retornam
(para uma conta hedging) o número mágico e o símbolo de uma posição oposta,
(por considerar uma inversão de posição em uma conta netting) o tipo da ordem e ticket da posição anterior, o ticket da ordem da posição atual, o tipo de posição e ticket antes de mudar de direção, o tipo da posição e ticket após mudar de direção para a lista de métodos com acesso simplificado da seção pública da classe:
//+------------------------------------------------------------------+ //| Methods of simplified access to event object properties | //+------------------------------------------------------------------+ //--- Return (1) event type, (2) event time in milliseconds, (3) event status, (4) event reason, (5) deal type, (6) deal ticket, //---(7) order type, based on which a deal was executed, (8) position opening order type, (9) position last order ticket, //--- (10) position first order ticket, (11) position ID, (12) opposite position ID, (13) magic number, (14) opposite position magic number, (15) position open time ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } ENUM_DEAL_TYPE TypeDeal(void) const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } ENUM_ORDER_TYPE TypeOrderEvent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } ENUM_ORDER_TYPE TypeFirstOrderPosition(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketFirstOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long MagicCloseBy(void) const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- When changing position direction, return (1) previous position order type, (2) previous position order ticket, //--- (3) current position order type, (4) current position order ticket, //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);} ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); } //--- Return (1) the price the event occurred at, (2) open price, (3) close price, //--- (4) StopLoss price, (5) TakeProfit price, (6) profit, (7) requested order volume, //--- (8) executed order volume, (9) remaining order volume, (10) executed position volume double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeOrderInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL); } double VolumeOrderExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED); } double VolumeOrderCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT); } double VolumePositionExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED); } //--- Return the (1) symbol and (2) opposite position symbol string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+
Os métodos são simples: é retornado a propriedade de evento correspondente para as ordens, é retornado um ticket da ordem que abriu ou alterou uma posição para as posições, já o tipo da posição (pelo tipo da ordem, que acionou ela) é retornado usando a função PositionTypeByOrderType() descrita anteriormente no arquivo de funções de serviço DELib.mqh.
Vamos definir no construtor de classe o armazenamento dos dados no tipo de conta:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Nos métodos de descrição da propriedade do evento, nós vamos adicionar a definição dos métodos que retornam o nome da ordem em que ocorreu um evento de negócio, a primeiro ordem da posição (de abertura), uma ordem, que levou à abertura (netting, hedging) ou alteração (netting) da posição atual, nome do tipo de posição atual, tipo da ordem que levou à abertura da posição anterior e o nome do tipo da posição anterior:
//+------------------------------------------------------------------+ //| Descriptions of order object properties | //+------------------------------------------------------------------+ //--- Get description of an order's (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Return the event's (1) status and (2) type string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Return the name of an (1) event deal order, (2) position's parent order, (3) current position order and the (4) current position //--- Return the name of an (5) order and (6) position before the direction was changed string TypeOrderDealDescription(void) const; string TypeOrderFirstDescription(void) const; string TypeOrderEventDescription(void) const; string TypePositionCurrentDescription(void) const; string TypeOrderPreviousDescription(void) const; string TypePositionPreviousDescription(void) const; //--- Return the name of the deal/order/position reason string ReasonDescription(void) const;
E sua implementação além do corpo da classe também:
//+------------------------------------------------------------------+ //| Return the name of the order/position/deal | //+------------------------------------------------------------------+ string CEvent::TypeOrderDealDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Return the name of the position's first order | //+------------------------------------------------------------------+ string CEvent::TypeOrderFirstDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Return the name of the order that changed the position | //+------------------------------------------------------------------+ string CEvent::TypeOrderEventDescription(void) const { return OrderTypeDescription(this.TypeOrderEvent()); } //+------------------------------------------------------------------+ //| Return the name of the current position | //+------------------------------------------------------------------+ string CEvent::TypePositionCurrentDescription(void) const { return PositionTypeDescription(this.TypePositionCurrent()); } //+------------------------------------------------------------------+ //| Return the name of the order before changing the direction | //+------------------------------------------------------------------+ string CEvent::TypeOrderPreviousDescription(void) const { return OrderTypeDescription(this.TypeOrderPosPrevious()); } //+------------------------------------------------------------------+ //| Return the name of the position before changing the direction | //+------------------------------------------------------------------+ string CEvent::TypePositionPreviousDescription(void) const { return PositionTypeDescription(this.TypePositionPrevious()); } //+------------------------------------------------------------------+
Esses métodos são simples, assim como os que retornam os tipos de ordem e posição. A única diferença está nas funções do arquivo DELIB.mqh que retornam o tipo de ordens e posições como a descrição de seu tipo: PositionTypeDescription() e OrderTypeDescription().
Agora o método ReasonDescription() deve ser melhorado para considerar e retornar as descrições das enumerações recém adicionadas, que são relacionadas aos motivos do evento para a conta netting:
//+------------------------------------------------------------------+ //| Return the name of the deal/order/position reason | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request") : reason==EVENT_REASON_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : reason==EVENT_REASON_VOLUME_ADD_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to position by partially completed request") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by triggered pending order") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by partially triggered pending order") : reason==EVENT_REASON_REVERSE ? TextByLanguage("Разворот позиции","Position reversal") : reason==EVENT_REASON_REVERSE_PARTIALLY ? TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partially completing request") : reason==EVENT_REASON_REVERSE_BY_PENDING ? TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal on a triggered pending order") : reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY ? TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on a partially triggered pending order") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawal from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+
Na quinta parte da descrição da biblioteca, nós já desenvolvemos o método de decodificação de um código de evento de negociação. Vamos recordar sua lógica:
Um código de evento é passado para o método e as flags do código de evento são verificadas. Se o código tiver a flag marcada, o evento de negociação apropriado é definido. Como o código do evento pode ter várias flags, todos as flags possíveis para o evento são verificadas e o tipo de evento é definido a partir de sua combinação. Em seguida, o tipo de evento é adicionado à variável da classe apropriada e é inserido na propriedade do objeto de evento (EVENT_PROP_TYPE_EVENT).
Agora nós só precisamos adicionar os novos flags correspondentes com os possíveis eventos da conta netting no código do evento:
//+------------------------------------------------------------------+ //| Decode the event code and set a trading event | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Pending order placed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending order removed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Position opened (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- If an existing position is changed if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED)) { //--- If the pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial closure flag and set the //--- "position reversal by activation of a pending order" or "position reversal by partial activation of a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_PENDING : TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set a trading event //--- "added volume to a position by activating a pending order" or "added volume to a position by partially activating a pending order" this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- If a position was changed by a market deal else { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial opening flag and set the "position reversal" or "position reversal by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_MARKET : TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set "added volume to a position" or "added volume to a position by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } } //--- If a new position is opened else { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- check the partial opening flag and set "pending order activated" or "pending order partially activated" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- check the partial opening flag and set the "Position opened" or "Position partially opened" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Position closed (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- if a position is closed by StopLoss if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- check the partial closing flag and set the "Position closed by StopLoss" or "Position partially closed by StopLoss" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by TakeProfit else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- check the partial closing flag and set the "Position closed by TakeProfit" or "Position partially closed by TakeProfit" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by an opposite one else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- check the partial closing flag and set the "Position closed by opposite one" or "Position partially closed by opposite one" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If a position is closed else { //--- check the partial closing flag and set the "Position closed" or "Position partially closed" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Balance operation on the account (clarify the event by deal type) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Initialize a trading event this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Take a deal type ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- if a deal is a balance operation if(deal_type==DEAL_TYPE_BALANCE) { //--- check the deal profit and set an event (funds deposit or withdrawal) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- The remaining balance operation types match the ENUM_DEAL_TYPE enumeration starting from DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- set the event this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+
Toda a lógica é bem simples e foi comentada no código. Portanto, eu não vou me debruçar sobre o método <if-else>.
Nós fizemos as mudanças na classe abstrata de eventos. Vamos fornecer sua listagem completa:
//+------------------------------------------------------------------+ //| Event.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Needed for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Object.mqh> #include "\..\..\Services\DELib.mqh" #include "..\..\Collections\HistoryCollection.mqh" #include "..\..\Collections\MarketCollection.mqh" //+------------------------------------------------------------------+ //| Abstract event class | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Event code //--- Return the index of the array the event's (1) double and (2) string properties are located at int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Trading event bool m_is_hedge; // Hedge account flag long m_chart_id; // Control program chart ID int m_digits_acc; // Number of decimal places for the account currency long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Event integer properties double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Event real properties string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Event string properties //--- return the flag presence in the trading event bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- Protected parametric constructor CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- Default constructor CEvent(void){;} //--- Set event's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Return the event's (1) integer, (2) real and (3) string properties from the property array long GetProperty(ENUM_EVENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_EVENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_EVENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the event supporting the property virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_EVENT_PROP_STRING property) { return true; } //--- Set the control program chart ID void SetChartID(const long id) { this.m_chart_id=id; } //--- Decode the event code and set the trading event, (2) return the trading event void SetTypeEvent(void); ENUM_TRADE_EVENT TradeEvent(void) const { return this.m_trade_event; } //--- Send the event to the chart (implementation in the class descendants) virtual void SendEvent(void) {;} //--- Compare CEvent objects by a specified property (to sort the lists by a specified event object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CEvent objects by all properties (to search for equal event objects) bool IsEqual(CEvent* compared_event); //+------------------------------------------------------------------+ //| Methods of simplified access to event object properties | //+------------------------------------------------------------------+ //--- Return (1) event type, (2) event time in milliseconds, (3) event status, (4) event reason, (5) deal type, (6) deal ticket, //--- (7) order type, based on which a deal was executed, (8) position opening order type, (9) position last order ticket, //--- (10) position first order ticket, (11) position ID, (12) opposite position ID, (13) magic number, (14) opposite position'a magic number, (15) position open time ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } ENUM_DEAL_TYPE TypeDeal(void) const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } ENUM_ORDER_TYPE TypeOrderEvent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } ENUM_ORDER_TYPE TypeFirstOrderPosition(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketFirstOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long MagicCloseBy(void) const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- When changing position direction, return (1) previous position order type, (2) previous position order ticket, //--- (3) current position order type, (4) current position order ticket, //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);} ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); } //--- Return (1) the price the event occurred at, (2) open price, (3) close price, //--- (4) StopLoss price, (5) TakeProfit price, (6) profit, (7) requested order volume, //--- (8) executed order volume, (9) remaining order volume, (10) executed position volume double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeOrderInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL); } double VolumeOrderExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED); } double VolumeOrderCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT); } double VolumePositionExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED); } //--- Return a (1) symbol and (2) opposite position symbol string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+ //| Descriptions of order object properties | //+------------------------------------------------------------------+ //--- Return description of the order's (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property); string GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property); string GetPropertyDescription(ENUM_EVENT_PROP_STRING property); //--- Return the event's (1) status and (2) type string StatusDescription(void) const; string TypeEventDescription(void) const; //--- Return the name of an (1) event deal order, (2) position's parent order, (3) current position's order, (4) current position //--- Return the name of an (5) order and (6) position before changing direction string TypeOrderDealDescription(void) const; string TypeOrderFirstDescription(void) const; string TypeOrderEventDescription(void) const; string TypePositionCurrentDescription(void) const; string TypeOrderPreviousDescription(void) const; string TypePositionPreviousDescription(void) const; //--- Return the name of the deal/order/position reason string ReasonDescription(void) const; //--- Display (1) description of order properties (full_prop=true - all properties, false - only supported ones), //--- (2) short event message (implementation in the class descendants) void Print(const bool full_prop=false); virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+ //| Compare CEvent objects by a specified property | //+------------------------------------------------------------------+ int CEvent::Compare(const CObject *node,const int mode=0) const { const CEvent *event_compared=node; //--- compare integer properties of two events if(mode<EVENT_PROP_INTEGER_TOTAL) { long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare integer properties of two objects if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL) { double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- compare string properties of two objects else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL) { string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+ //| Compare CEvent objects by all properties | //+------------------------------------------------------------------+ bool CEvent::IsEqual(CEvent *compared_event) { int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; } //--- return true; } //+------------------------------------------------------------------+ //| Decode the event code and set a trading event | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Pending order placed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending order removed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Position opened (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- If an existing position is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED)) { //--- If the pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial closure flag and set the //--- "position reversal by activation of a pending order" or "position reversal by partial activation of a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_PENDING : TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set a trading event //--- "added volume to a position by activating a pending order" or "added volume to a position by partially activating a pending order" this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- If a position was changed by a market deal else { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial opening flag and set the "position reversal" or "position reversal by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_MARKET : TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set "added volume to a position" or "added volume to a position by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } } //--- If a new position is opened else { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- check the partial opening flag and set "pending order activated" or "pending order partially activated" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- check the partial opening flag and set the "Position opened" or "Position partially opened" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Position closed (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- if a position is closed by StopLoss if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- check the partial closing flag and set the "Position closed by StopLoss" or "Position partially closed by StopLoss" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by TakeProfit else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- check the partial closing flag and set the "Position closed by TakeProfit" or "Position partially closed by TakeProfit" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by an opposite one else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- check the partial closing flag and set the "Position closed by opposite one" or "Position partially closed by opposite one" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If a position is closed else { //--- check the partial closing flag and set the "Position closed" or "Position partially closed" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Balance operation on the account (clarify the event by deal type) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Initialize a trading event this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Take a deal type ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- if a deal is a balance operation if(deal_type==DEAL_TYPE_BALANCE) { //--- check the deal profit and set an event (funds deposit or withdrawal) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- Remaining balance operation types match the ENUM_DEAL_TYPE enumeration starting with DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- set the event this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+ //| Return the description of the event's integer property | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event's type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+" #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_BY_ID ? TextByLanguage("Магический номер встречной позиции","Magic number of opposite position")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORD_POS_BEFORE ? TextByLanguage("Тип ордера позиции до смены направления","Type order of position before changing direction") : property==EVENT_PROP_TICKET_ORD_POS_BEFORE ? TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction") : property==EVENT_PROP_TYPE_ORD_POS_CURRENT ? TextByLanguage("Тип ордера текущей позиции","Type order of current position") : property==EVENT_PROP_TICKET_ORD_POS_CURRENT ? TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position") : EnumToString(property) ); } //+------------------------------------------------------------------+ //| Return the description of the event's real property | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена события","Price at the time of the event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_ORDER_INITIAL ? TextByLanguage("Начальный объём ордера","Initial order volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_EXECUTED ? TextByLanguage("Исполненный объём ордера","Executed order volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_CURRENT ? TextByLanguage("Оставшийся объём ордера","Remaining order volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_POSITION_EXECUTED ? TextByLanguage("Текущий объём позиции","Current position volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : EnumToString(property) ); } //+------------------------------------------------------------------+ //| Return the description of the event's string property | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property) { return ( property==EVENT_PROP_SYMBOL ? TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\"" : TextByLanguage("Символ встречной позиции","Symbol of opposite position")+": \""+this.GetProperty(property)+"\"" ); } //+------------------------------------------------------------------+ //| Return the event status name | //+------------------------------------------------------------------+ string CEvent::StatusDescription(void) const { ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); return ( status==EVENT_STATUS_MARKET_PENDING ? TextByLanguage("Установлен отложенный ордер","Pending order placed") : status==EVENT_STATUS_MARKET_POSITION ? TextByLanguage("Открыта позиция","Position opened") : status==EVENT_STATUS_HISTORY_PENDING ? TextByLanguage("Удален отложенный ордер","Pending order removed") : status==EVENT_STATUS_HISTORY_POSITION ? TextByLanguage("Закрыта позиция","Position closed") : status==EVENT_STATUS_BALANCE ? TextByLanguage("Балансная операция","Balance operation") : TextByLanguage("Неизвестный статус","Unknown status") ); } //+------------------------------------------------------------------+ //| Return the trading event name | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position opened") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position opened partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET ? TextByLanguage("Разворот позиции по рыночному запросу","Position reversal by market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING ? TextByLanguage("Разворот позиции срабатыванием отложенного ордера","Position reversal by a triggered pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET ? TextByLanguage("Добавлен объём к позиции по рыночному запросу","Added volume to position by market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activation of pending order") : event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ? TextByLanguage("Разворот позиции частичным исполнением запроса","Position reversal by partial completion of market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ? TextByLanguage("Разворот позиции частичным срабатыванием отложенного ордера","Position reversal by partially triggered pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ? TextByLanguage("Добавлен объём к позиции частичным исполнением запроса","Added volume to position by partial completion of market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by partially triggering a pending order") : TextByLanguage("Нет торгового события","No trade event") ); } //+------------------------------------------------------------------+ //| Return the name of the order/position/deal | //+------------------------------------------------------------------+ string CEvent::TypeOrderDealDescription(void) const { ENUM_EVENT_STATUS status=this.Status(); return ( status==EVENT_STATUS_MARKET_PENDING || status==EVENT_STATUS_HISTORY_PENDING ? OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT)) : status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ? PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : status==EVENT_STATUS_BALANCE ? DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+ //| Return the name of the position's first order | //+------------------------------------------------------------------+ string CEvent::TypeOrderFirstDescription(void) const { return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION)); } //+------------------------------------------------------------------+ //| Return the name of the order that changed the position | //+------------------------------------------------------------------+ string CEvent::TypeOrderEventDescription(void) const { return OrderTypeDescription(this.TypeOrderEvent()); } //+------------------------------------------------------------------+ //| Return the name of the current position | //+------------------------------------------------------------------+ string CEvent::TypePositionCurrentDescription(void) const { return PositionTypeDescription(this.TypePositionCurrent()); } //+------------------------------------------------------------------+ //| Return the name of the order before changing the direction | //+------------------------------------------------------------------+ string CEvent::TypeOrderPreviousDescription(void) const { return OrderTypeDescription(this.TypeOrderPosPrevious()); } //+------------------------------------------------------------------+ //| Return the name of the position before changing the direction | //+------------------------------------------------------------------+ string CEvent::TypePositionPreviousDescription(void) const { return PositionTypeDescription(this.TypePositionPrevious()); } //+------------------------------------------------------------------+ //| Return the name of the deal/order/position reason | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request") : reason==EVENT_REASON_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : reason==EVENT_REASON_VOLUME_ADD_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to the position by partially completed request") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by triggering pending order") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by triggering pending order partially") : reason==EVENT_REASON_REVERSE ? TextByLanguage("Разворот позиции","Position reversal") : reason==EVENT_REASON_REVERSE_PARTIALLY ? TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partial completion of request") : reason==EVENT_REASON_REVERSE_BY_PENDING ? TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal when triggering pending order") : reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY ? TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on partially triggered pending order") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawal from balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+ //| Display the event properties in the journal | //+------------------------------------------------------------------+ void CEvent::Print(const bool full_prop=false) { ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" ============="); int beg=0, end=EVENT_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=EVENT_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
Uma vez que as diferenças entre as contas hedging e netting são evidentes apenas quando se trabalha com posições, as classes CEventPositionOpen e CEventPositionClose derivadas da classe abstrata CEvent requer um ajuste fino — apenas os métodos de exibição das mensagens de evento para o diário são refinados. Os métodos restantes das classes permaneceram inalterados.
Abra o arquivo EventPositionOpen.mqh e adicione o método privado que cria e retorna uma breve descrição do evento:
//+------------------------------------------------------------------+ //| Position open event | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { private: //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Supported (1) real and (2) integer order properties virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a short event message in the journal, (2) Send an event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+
Vamos escrever sua implementação fora do corpo da classe:
//+------------------------------------------------------------------+ //| Create and return a short event message | //+------------------------------------------------------------------+ string CEventPositionOpen::EventsMessage(void) { //--- number of decimal places in an event symbol quote int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) header, (2) executed order volume, (3) executed position volume, (4) event price, //--- (5) StopLoss price, (6) TakeProfit price, (7) magic number, (6) profit in account currency string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); //--- string text=""; //--- Position reversal if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING) { //--- EURUSD: Buy #xx changed to 0.1 Sell #xx (0.2 SellLimit order #XX) at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx text= ( this.Symbol()+" "+ this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+ TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); } else { //--- Add volume if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID)) { //--- EURUSD: Added 0.1 to Buy #xx (BuyLimit order #XX) at х.ххххх, magic text= ( this.Symbol()+" "+ TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic ); } //--- Open a position else { //--- EURUSD: Opened 0.1 Buy #xx (BuyLimit order #XX) at х.ххххх, sl х.ххххх, tp x.xxxxx, magic text= ( this.Symbol()+" "+ TextByLanguage("Открыт ","Open ")+vol_pos+" "+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic ); } } return head+text; } //+------------------------------------------------------------------+
O método cria variantes das mensagem, dependendo do estado do evento e da presença de certas propriedades do objeto de evento.
Por exemplo, se o StopLoss estiver definido, o cabeçalho "sl" e o seu preço serão adicionados ao texto. Caso contrário, uma sequência vazia é inserida em vez de uma entrada do StopLoss. O mesmo é feito para algumas outras propriedades do evento. Os comentários da listagem de métodos contêm as condições de criação do texto do evento, bem como os exemplos do texto que é retornado pelo método.
O texto criado no método é exibido no diário a partir do método PrintShort(), que por sua vez é chamado a partir do método Refresh() na classe de coleção de eventos através da chamada do método virtual SendEvent() da classe CEvent redefinida aqui na classe CEventPositionOpen.
Abaixo está a listagem completa da classe CEventPositionOpen:
//+------------------------------------------------------------------+ //| EventPositionOpen.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Position opening event | //+------------------------------------------------------------------+ class CEventPositionOpen : public CEvent { private: //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {} //--- Supported (1) real and (2) integer order properties virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| integer property, otherwise, return 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return(property==EVENT_PROP_POSITION_BY_ID ? false : true); } //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Display a brief message about the event in the journal | //+------------------------------------------------------------------+ void CEventPositionOpen::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Send the event to the chart | //+------------------------------------------------------------------+ void CEventPositionOpen::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol()); } //+------------------------------------------------------------------+ //| Create and return a short event message | //+------------------------------------------------------------------+ string CEventPositionOpen::EventsMessage(void) { //--- number of decimal places in an event symbol quote int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) header, (2) executed order volume, (3) executed position volume, (4) event price, //--- (5) StopLoss price, (6) TakeProfit price, (7) magic number, (6) profit in account currency string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); //--- string text=""; //--- Position reversal if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING) { //--- EURUSD: Buy #xx changed to 0.1 Sell #xx [0.2 SellLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx text= ( this.Symbol()+" "+ this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+ TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); } else { //--- Add volume if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID)) { //--- EURUSD: Added 0.1 to Buy #xx [BuyLimit order #XX] at х.ххххх, magic text= ( this.Symbol()+" "+ TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic ); } //--- Open a position else { //--- EURUSD: Opened 0.1 Buy #xx [BuyLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic text= ( this.Symbol()+" "+ TextByLanguage("Открыт ","Open ")+vol_pos+" "+ this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic ); } } return head+text; } //+------------------------------------------------------------------+
De maneira análoga, altere a classe CEventPositionClose:
//+------------------------------------------------------------------+ //| EventPositionClose.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Position opening event | //+------------------------------------------------------------------+ class CEventPositionClose : public CEvent { private: //--- Create and return a short event message string EventsMessage(void); public: //--- Constructor CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {} //--- Supported (1) real and (2) integer order properties virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| integer property, otherwise, return 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { return true; } //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| real property, otherwise, return 'false' | //+------------------------------------------------------------------+ bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { return true; } //+------------------------------------------------------------------+ //| Display a brief message about the event in the journal | //+------------------------------------------------------------------+ void CEventPositionClose::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Send the event to the chart | //+------------------------------------------------------------------+ void CEventPositionClose::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol()); } //+------------------------------------------------------------------+ //| Create and return a short event message | //+------------------------------------------------------------------+ string CEventPositionClose::EventsMessage(void) { //--- number of decimal places in an event symbol quote int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- (1) header, (2) executed order volume, (3) executed position volume, (4) event price, //--- (5) StopLoss price, (6) TakeProfit price, (7) magic number, (6) profit in account currency, (7,8) closure message options string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol())); string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol())); string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits); string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : ""); string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : ""); string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY); string close=TextByLanguage("Закрыт ","Close "); string in_pos=""; //--- if(this.GetProperty(EVENT_PROP_TYPE_EVENT)>TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL) { close=TextByLanguage("Закрыт объём ","Closed volume ")+vol_ord; in_pos=TextByLanguage(" в "," in "); } string opposite= ( this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS) ? TextByLanguage(" встречным "," by opposite ")+this.SymbolCloseBy()+" "+ this.TypeOrderDealDescription()+" #"+(string)this.PositionByID()+(this.MagicCloseBy()> 0 ? "("+(string)this.MagicCloseBy()+" ]" : "") : "" ); //--- EURUSD: Closed 0.1 Sell #xx [0.2 SellLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx string text= ( this.Symbol()+" "+close+in_pos+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+ opposite+" ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit ); return head+text; } //+------------------------------------------------------------------+
Todas as classes de objeto do evento foram alteradas de acordo com as novas tarefas para trabalhar em contas netting.
Agora vamos lidar com a classe de coleção de eventos CEventCollection.
Anteriormente, o método CreateNewEvent() (descrito na quinta parte) apresentava uma variável local para armazenar o código de evento de negociação.
Vamos torná-lo membro da classe privada removendo-o do novo método de criação de evento e declarando-o na seção privada da classe. Além disso, inclua as declarações dos métodos necessários para a criação do novo evento para os tipos de conta hedging e netting, o método para retornar a lista de todas os negócios InOut pelo ID da posição e o método para obter um objeto da posição de mercado pelo seu ID.
//+------------------------------------------------------------------+ //| Collection of account events | //+------------------------------------------------------------------+ class CEventsCollection : public CListObj { private: CListObj m_list_events; // List of events bool m_is_hedge; // Hedge account flag long m_chart_id; // Control program chart ID int m_trade_event_code; // Trading event code ENUM_TRADE_EVENT m_trade_event; // Account trading event CEvent m_event_instance; // Event object for searching by property //--- Create a trading event depending on the order status void CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market); //--- Create an event for a (1) hedging account, (2) netting account void NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); void NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); //--- Select and return the list of market pending orders CArrayObj* GetListMarketPendings(CArrayObj* list); //--- Select from the list and return the list of historical (1) removed pending orders, (2) deals, (3) all closing orders CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListCloseByOrders(CArrayObj* list); //--- Return the list of (1) all position orders by its ID, (2) all deal positions by its ID //--- (3) all market entry deals by position ID, (4) all market exit deals by position ID, //--- (5) all position reversal deals by position ID CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInOutByPosID(CArrayObj* list,const ulong position_id); //--- Return the total volume of all deals (1) IN, (2) OUT of the position by its ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Return the (1) first, (2) last and (3) closing order from the list of all position orders, //--- (4) an order by ticket, (5) market position by ID, //--- (6) the last and (7) penultimate InOut deal by position ID COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket); COrder* GetPositionByID(CArrayObj* list,const ulong position_id); //--- Return the flag of the event object presence in the event list bool IsPresentEventInList(CEvent* compared_event); public: //--- Select events from the collection with time within the range from begin_time to end_time CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Return the full event collection list "as is" CArrayObj *GetList(void) { return &this.m_list_events; } //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Update the list of events void Refresh(CArrayObj* list_history, CArrayObj* list_market, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals); //--- Set the control program chart ID void SetChartID(const long id) { this.m_chart_id=id; } //--- Return the last trading event on the account ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- Reset the last trading event void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- Constructor CEventsCollection(void); }; //+------------------------------------------------------------------+
Redefine o código do evento de negociação na lista de inicialização do construtor da classe:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT) { this.m_list_events.Clear(); this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT); this.m_list_events.Type(COLLECTION_EVENTS_ID); this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
Uma conta netting pode ter várias ações de negociação. Ela pode ter apenas uma posição passando por alterações em um único símbolo. Elas podem incluir a alteração do volume em caso de encerramento parcial acionado pela ativação de uma ordem oposta com um volume menor, bem como a adição de um volume à posição quando as ordens na mesma direção são acionadas.
As mudanças mais interessantes, no entanto, acontecem quando há o acionamento das ordens opostas contendo um volume maior que a posição. Nesse caso, um novo ticket é atribuído à posição. O ticket corresponde a uma ordem acionada e o tipo da posição é alterado para o seu inverso (inversão de posição). O ID da posição permanece inalterado e é igual ao ticket da primeira ordem que acionou a posição na conta.
Nós precisamos monitorar todas as alterações da direção da posição ao longo da sua vida útil para (1) exibir corretamente as entradas de reversão de posição no diário e (2) ter a capacidade de obter os dados sobre os eventos de reversão da posição em nossos programas. Para conseguir isso, nós precisamos ter acesso a todos os seus negócios com o método de alteração da posição DEAL_ENTRY_INOUT da enumeração ENUM_DEAL_ENTRY.
Nesse caso, nós precisamos apenas organizar esses negócios sequencialmente pelo tempo de sua ocorrência e pegar o negócio necessitado. O acordo em si apresenta todas as propriedades da ordem que o acionaram.
Assim, se nós tivermos uma ordem de negociação, nós podemos receber um ticket para uma posição com uma direção alterada, bem como o tipo de uma ordem, que acionou uma reversão de posição, juntamente com novos níveis de StopLoss, TakeProfit, etc. Tudo o que nós precisamos para obter essa funcionalidade é criar uma lista de todos os negócios InOut pelo seu ID, o que é muito fácil de usar usando a biblioteca que nós desenvolvemos.Vamos considerar o método para receber todos os negócios InOut de uma posição pelo seu ID:
//+------------------------------------------------------------------+ //| Return the list of all reversal deals (IN_OUT) | //| by a position ID | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListAllDealsInOutByPosID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id); list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_INOUT,EQUAL); return list_deals; } //+------------------------------------------------------------------+
Verifique o tipo da lista passada para o método. Se não for uma coleção do histórico de ordens e negócios, avise sobre o erro e retorne NULL.
Nós precisamos de todas essas verificações de listas nas classes para detectar nossos próprios erros. Eles devem ser removidos após a depuração para não sobrecarregar os cálculos com verificações desnecessárias.
Em seguida, nós recebemos a lista de negócios pelo ID da posição (o método foi considerado no artigo anterior) ordene a lista obtida pelo método de alteração da posição InOut e retorne a lista final.
Para receber os dados em uma posição aberta ou definir sua ausência, crie um método recebendo um objeto da posição de mercado pelo seu ID:
//+------------------------------------------------------------------+ //| Return a position by ID | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetPositionByID(CArrayObj *list,const ulong position_id) { if(list.Type()!=COLLECTION_MARKET_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of the market collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_POSITION_ID,position_id,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; COrder* order=list_orders.At(0); return(order!=NULL ? order : NULL); } //+------------------------------------------------------------------+
O método é simples, assim como outros métodos similares da biblioteca. Verifique o tipo da lista selecionada. Se não for uma lista de coleção de ordens e posições de mercado, avise sobre o erro e retorne NULL.
Em seguida, pegue apenas os objetos de posições ativas da lista passada para o método e ordene pelo ID da posição passada ao método.
Se falhar na obtenção da lista ou não tiver objetos, retorne NULL— não há posição solicitada.
Em seguida, recebemos da lista um único objeto de posição de mercado (só pode haver uma posição com um ID especificado no mercado) e retornamos o próprio objeto ou NULL no caso do retorno terminar em um erro.
O método de criação de um novo objeto de evento CreateNewEvent() foi descrito no artigo anterior.
Aqui eu mostrarei apenas as alterações implementadas.
A seguinte variável local foi removida do método
int trade_event_code
Ele se tornou um membro da classe que nós criamos na seção privada.
A lógica do método permanece a mesma, mas agora também apresenta os métodos necessários para lidar com o tipo de conta que nós estamos trabalhando. Se for hedging, é chamado o método de criação de um novo evento para uma conta hedging. Caso contrário, é usado o método de criação de um novo evento para uma conta netting:
//+------------------------------------------------------------------+ //| Create a trading event depending on the order status | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market) { this.m_trade_event_code=TRADE_EVENT_FLAG_NO_EVENT; ENUM_ORDER_STATUS status=order.Status(); //--- Pending order placed if(status==ORDER_STATUS_MARKET_PENDING) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; CEvent* event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Event order type event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Event order type event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Order ticket event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Position order type before changing direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Order time event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Order price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Order close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss order price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit order price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Pending order removed if(status==ORDER_STATUS_HISTORY_PENDING) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED; CEvent* event=new CEventOrderRemoved(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL : order.State()==ORDER_STATE_EXPIRED ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON reason) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Event order type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Type of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket of the order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Time of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Order open price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Order close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss order price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit order price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not present on the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the last trading event value event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already in the list, remove the new event object and display the debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Position opened (__MQL4__) if(status==ORDER_STATUS_MARKET_POSITION) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; CEvent* event=new CEventPositionOpen(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Type of the order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Type of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket of the order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Order/deal/position open price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Order/deal/position close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss position price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit position price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume()); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Set the control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not present in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send the message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove the new event object and display the debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- New deal (__MQL5__) if(status==ORDER_STATUS_DEAL) { //--- New balance operation if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL) { this.m_trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE; CEvent* event=new CEventBalanceOperation(this.m_trade_event_code,order.Ticket()); if(event!=NULL) { ENUM_EVENT_REASON reason= ( (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) : (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT) ); event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // Type of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Order/deal/position open price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceOpen()); // Order/deal/position close price event.SetProperty(EVENT_PROP_PRICE_SL,0); // StopLoss deal price event.SetProperty(EVENT_PROP_PRICE_TP,0); // TakeProfit deal price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested deal volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()); // Executed deal volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,0); // Remaining (unexecuted) deal volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume()); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { //--- Send a message about the event and set the value of the last trading event this.m_list_events.InsertSort(event); event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- If this is not a balance operation else { if(this.m_is_hedge) this.NewDealEventHedge(order,list_history,list_market); else this.NewDealEventNetto(order,list_history,list_market); } } } //+------------------------------------------------------------------+
O método de criação de um novo evento para uma conta hedging:
//+------------------------------------------------------------------+ //| Create a hedging account event | //+------------------------------------------------------------------+ void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market) { //--- Market entry if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int reason=EVENT_REASON_DONE; //--- Search for all position deals in the direction of its opening and calculate their overall volume double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); //--- Take the deal order and the last position order from the list of all position orders ulong order_ticket=deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); COrder* order_first=this.GetHistoryOrderByTicket(list_history,order_ticket); COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID()); //--- Get an open position by ticket COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); //--- If there is no last order, the first and last position orders coincide if(order_last==NULL) order_last=order_first; if(order_first!=NULL) { //--- If the order volume is opened partially, this is a partial execution if(this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_DONE_PARTIALLY; } //--- If an opening order is a pending one, the pending order is activated if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- If an order is executed partially, set the partial order execution as an event reason reason= (this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume() ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time (position open time) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Type of the order that triggered an event deal (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Type of the order that triggered a position deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // Opposite position ID //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Opposite position symbol //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/podition magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price (position open price) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Order open price (position's opening order price) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Order close price (position's last order close price) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss price (position's StopLoss order price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit price (position's TakeProfit order price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_first.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,(order_first.Volume()-order_first.VolumeCurrent())); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_first.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Exit the market else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- Take the first and last position orders from the list of all position orders COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID()); COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID()); //--- Get an open position by ticket COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); if(order_first!=NULL && order_last!=NULL) { //--- Search for all position deals in the directions of its opening and closing, and count their total volume double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID()); //--- Calculate the current volume of the closed position int dgl=(int)DigitsLots(deal.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- If the order volume is closed partially, this is a partial execution if(volume_current>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- If the closing order is executed partially, set the closing order partial execution as an event reason if(order_last.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- If the closing flag is set to StopLoss for a position's closing order, then closing is performed by StopLoss //--- If a StopLoss order is executed partially, set partial StopLoss order execution as the event reason if(order_last.IsCloseByStopLoss()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- If the closing flag is set to TakeProfit for a position's closing order, then closing is performed by TakeProfit //--- If a TakeProfit order is executed partially, set partial TakeProfit order execution as the event reason else if(order_last.IsCloseByTakeProfit()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time (position open time) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Type of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder()); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket()); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID()); // Opposite position ID //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_last.Symbol()); // Opposite position symbol //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/podition magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price (position open price) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Order open price (position's opening order price) event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose()); // Order close price (position's last order close price) event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss price (position's StopLoss order price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit price (position's TakeProfit order price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order_last.Volume()-order_last.VolumeCurrent()); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_last.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Remaining (current) position volume //--- event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //--- Opposite position else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE_BY_POS; //--- Take the first and closing position orders from the list of all position orders COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID()); COrder* order_close=this.GetCloseByOrderFromList(list_history,deal.PositionID()); //--- Get an open position by ID COrder* position=this.GetPositionByID(list_market,order_first.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); if(order_first!=NULL && order_close!=NULL) { //--- Add the flag of closing by an opposite position this.m_trade_event_code+=TRADE_EVENT_FLAG_BY_POS; //--- Take the first order of the closing position Print(DFUN,"PositionByID=",order_close.PositionByID()); CArrayObj* list_close_by=this.GetListAllOrdersByPosID(list_history,order_close.PositionByID()); COrder* order_close_by=list_close_by.At(0); if(order_close_by==NULL) return; //--- Search for all closed position deals in the direction of its opening and closing and count their total volume double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID()); double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID());//+order_close.Volume(); //--- Calculate the current volume of the closed position int dgl=(int)DigitsLots(deal.Symbol()); double volume_current=::NormalizeDouble(volume_in-volume_out,dgl); //--- Search for all opposite position deals in the directions of its opening and closing and calculate their total volume double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID()); double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID()); //--- Calculate the current volume of the opposite position double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl); //--- If the closed position volume is closed partially, this is a partial closing if(volume_current>0 || order_close.VolumeCurrent()>0) { //--- Add the partial closing flag this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- If the opposite position is closed partially, there is a partial closing by the part of the opposite position volume reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS); } //--- If the position volume is closed in full and there is a partial execution by the opposite one, there is a closing by the part of the opposite position volume else { if(volume_opp_current>0) { reason=EVENT_REASON_DONE_BY_POS_PARTIALLY; } } CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder()); // Type of the order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket()); // Ticket of the order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC()); // Time of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder()); // Type of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket()); // Ticket of the order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID()); // Opposite position ID //--- event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_close_by.Magic()); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket()); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_close_by.Symbol()); // Opposite position symbol //--- event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen()); // Order/deal/position open price event.SetProperty(EVENT_PROP_PRICE_CLOSE,deal.PriceClose()); // Order/deal/position close price event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss()); // StopLoss price (Position order StopLoss price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit()); // TakeProfit price (Position order TakeProfit price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,::NormalizeDouble(volume_in,dgl));// Initial volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,deal.Volume()); // Closed volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,volume_current); // Remaining (current) volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Remaining (current) volume event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove the new event object and display the debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } } //+------------------------------------------------------------------+
O método é bastante grande, embora todas as ações sejam semelhantes e descritas nos comentários de listagem de métodos. Eu acredito que o código do método não deve causar nenhum problema.
O método de criação de um novo evento para uma conta netting tem a mesma lógica:
//+------------------------------------------------------------------+ //| Create an event for a netting account | //+------------------------------------------------------------------+ void CEventsCollection::NewDealEventNetto(COrder *deal,CArrayObj *list_history,CArrayObj *list_market) { //--- Prepare position history data //--- Lists of all deals and position direction changes CArrayObj* list_deals=this.GetListAllDealsByPosID(list_history,deal.PositionID()); CArrayObj* list_changes=this.GetListAllDealsInOutByPosID(list_history,deal.PositionID()); if(list_deals==NULL || list_changes==NULL) return; list_deals.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); list_changes.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); if(!list_changes.InsertSort(list_deals.At(0))) return; //--- Orders of the first and last position deals CArrayObj* list_tmp=this.GetListAllOrdersByPosID(list_history,deal.PositionID()); COrder* order_first_deal=list_tmp.At(0); list_tmp=CSelect::ByOrderProperty(list_tmp,ORDER_PROP_TICKET,deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET),EQUAL); COrder* order_last_deal=list_tmp.At(list_tmp.Total()-1); if(order_first_deal==NULL || order_last_deal==NULL) return; //--- Type and tickets of the first and last position deals' orders ENUM_ORDER_TYPE type_order_first_deal=(ENUM_ORDER_TYPE)order_first_deal.TypeOrder(); ENUM_ORDER_TYPE type_order_last_deal=(ENUM_ORDER_TYPE)order_last_deal.TypeOrder(); ulong ticket_order_first_deal=order_first_deal.Ticket(); ulong ticket_order_last_deal=order_last_deal.Ticket(); //--- Current and previous positions COrder* position_current=list_changes.At(list_changes.Total()-1); COrder* position_previous=(list_changes.Total()>1 ? list_changes.At(list_changes.Total()-2) : position_current); if(position_current==NULL || position_previous==NULL) return; ENUM_ORDER_TYPE type_position_current=(ENUM_ORDER_TYPE)position_current.TypeOrder(); ulong ticket_position_current=position_current.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); ENUM_ORDER_TYPE type_position_previous=(ENUM_ORDER_TYPE)position_previous.TypeOrder(); ulong ticket_position_previous=position_previous.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET); //--- Get an open position by the ticket and write its volume COrder* position=this.GetPositionByID(list_market,deal.PositionID()); double vol_position=(position!=NULL ? position.Volume() : 0); //--- Executed order volume double vol_order_done=order_last_deal.Volume()-order_last_deal.VolumeCurrent(); //--- Remaining (unexecuted) order volume double vol_order_current=order_last_deal.VolumeCurrent(); //--- Enter the market if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED; int num_deals=list_deals.Total(); int reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD : EVENT_REASON_DONE); //--- If this is not the first deal in the position, add the position change flag if(num_deals>1) { this.m_trade_event_code+=TRADE_EVENT_FLAG_POSITION_CHANGED; } //--- If the order volume is opened partially, this means a partial execution if(order_last_deal.VolumeCurrent()>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- If this is not the first position deal, the volume is added by partial execution, otherwise - partial opening reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD_PARTIALLY : EVENT_REASON_DONE_PARTIALLY); } //--- If an opening order is a pending one, the pending order is activated if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- If this is not the first position deal if(num_deals>1) { //--- If the order is executed partially, set adding the volume to the position by pending order partial execution as an event reason, //--- otherwise, the volume is added to the position by executing a pending order reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY : EVENT_REASON_VOLUME_ADD_BY_PENDING ); } //--- If this is a new position else { //--- If the order is executed partially, set pending order partial execution as an event reason, //--- otherwise, the position is opened by activating a pending order reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : EVENT_REASON_ACTIVATED_PENDING ); } } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Event deal parameters event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time (position open time) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price (position open price) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Opposite position symbol event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID //--- Event order parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Order close price (position's last order close price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Opposite position magic number //--- Position parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Type of an order that triggered the first position deal event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Ticket of the order that triggered the first position deal (the first position order) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Position first order open price event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // StopLoss price (position order StopLoss price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // TakeProfit price (position order TakeProfit price) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Position type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Current position order ticket event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Executed position volume //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Position reversal else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED+TRADE_EVENT_FLAG_POSITION_CHANGED+TRADE_EVENT_FLAG_POSITION_REVERSE; int reason=EVENT_REASON_REVERSE; //--- If not the entire order volume is opened, this is a partial execution if(order_last_deal.VolumeCurrent()>0) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; reason=EVENT_REASON_REVERSE_PARTIALLY; } //--- If an opening order is a pending one, the pending order is activated if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY) { this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED; //--- If the order is executed partially, set the position reversal by a pending order partial execution as the event reason reason= (order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY : EVENT_REASON_REVERSE_BY_PENDING ); } CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Event deal parameters event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time (position open time) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price (position open price) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Opposite position symbol event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID //--- Event order parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Order close price (position's last order close price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic()); // Opposite position magic number //--- Position parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Type of the order that triggered the first position deal event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Ticket of the order that triggered the first position deal (the first position order) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Position first order open price event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // StopLoss price (position order StopLoss price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // TakeProfit price (position order TakeProfit price) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Current position order ticket event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Executed position volume //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } //--- Exit the market else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT) { this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; int reason=EVENT_REASON_DONE; //--- If the position with the ID is still in the market, this means partial execution if(this.GetPositionByID(list_market,deal.PositionID())!=NULL) { this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- If a closing order is executed partially, set partial execution of a closing order as an event reason if(order_last_deal.VolumeCurrent()>0) { reason=EVENT_REASON_DONE_PARTIALLY; } //--- If the closing flag is set to StopLoss for a position's closing order, then closing is performed by StopLoss //--- If a StopLoss order is executed partially, set partial StopLoss order execution as the event reason if(order_last_deal.IsCloseByStopLoss()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_SL; reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL); } //--- If the closing flag is set to TakeProfit for a position's closing order, then closing is performed by TakeProfit //--- If a TakeProfit order is executed partially, set partial TakeProfit order execution as the event reason else if(order_last_deal.IsCloseByTakeProfit()) { this.m_trade_event_code+=TRADE_EVENT_FLAG_TP; reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP); } //--- CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID()); if(event!=NULL) { //--- Event deal parameters event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC()); // Event time (position open time) event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder()); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen()); // Event price (position open price) event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol()); // Opposite position symbol event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID()); // Position ID //--- Event order parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal); // Ticket of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID()); // Opposite position ID event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose()); // Order close price (position's last order close price) event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last_deal.Magic()); // Opposite position magic number //--- Position parameters event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal); // Type of the order that triggered the first position deal event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal); // Ticket of the order that triggered the first position deal (the first position order) event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC()); // Time of the order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen()); // Position first order open price event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss()); // StopLoss price (position order StopLoss price) event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit()); // TakeProfit price (position order TakeProfit price) event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous); // Position type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // Position order ticket before changing the direction event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // Current position order ticket event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position); // Executed position volume //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list.")); delete event; } } } } //+------------------------------------------------------------------+
Os métodos CreateNewEvent(), NewDealEventHedge() e NewDealEventNetto() possuem uma lógica e ações idênticas. Então, seria razoável combiná-los. Mas até agora nós fizemos como foi mostrado acima (numa base "do simples ao complexo"). Como eu já mencionei, os códigos das classes e seus métodos devem ser otimizados posteriormente.
Nós implementamos as alterações na classe de coleções de eventos para trabalhar com os tipos de contas hedging e netting. A listagem completa da classe é fornecida nos arquivos da biblioteca anexados abaixo. O código é bem volumoso.
Teste de desempenho em contas hedging e netting
Para verificar as alterações implementadas, crie um EA de teste baseado no do artigo anterior.
Salve ele no diretório \MQL5\Experts\TestDoEasy\Part06 com o nome de TestDoEasyPart06.mq5.
Remova as linhas que verificam o tipo de conta no manipulador OnInit() do EA:
int OnInit() { //--- Check account type if(!engine.IsHedge()) { Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge")); return INIT_FAILED; } //--- set global variables
Em vez disso, adicione a chamada da função de verificação da validade da criação de enumerações para busca e ordenação pelas propriedades do objeto:
int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- set global variables
Para encerrar uma posição parcialmente, uma conta netting requer a colocação de uma posição que seja oposta à direção da posição existente e tenha o volume suficiente para acontecer o encerramento parcial. Portanto, nós precisamos fazer pequenas correções na função do manipulador de evento de pressionamento de botão PressButtonEvents().
Para encerrar parcialmente uma posição de Compra:
//--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calculate the closed volume and close the half of the Buy position by the ticket if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } }
Verifique a conta. Se hedging, encerramos parte da posição, caso contrário (se netting) — enviamos uma ordem de abertura de posição de Venda com um volume igual à metade do volume atual da posição de Compra.
Para encerrar parte de uma posição de Venda:
//--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calculate the closed volume and close the half of the Sell position by the ticket if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } }
Verifique a conta. Se hedging, encerramos parte da posição, caso contrário (se netting) — enviamos uma ordem de Compra com um volume igual à metade do volume atual da posição de venda.
Estas são as alterações necessárias que devem ser implementadas para fazer o EA funcionar em uma conta netting.
A listagem completa do EA de teste:
//+------------------------------------------------------------------+ //| TestDoEasyPart06.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <DoEasy\Engine.mqh> #include <Trade\Trade.mqh> //--- enums enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL }; #define TOTAL_BUTT (17) //--- struct SDataButt { string name; string text; }; //--- input variables input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift //--- global variables CEngine engine; CTrade trade; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- set global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; //--- create buttons if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- setting trade parameters trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- delete objects ObjectsDeleteAll(0,prefix); Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- static ENUM_TRADE_EVENT last_event=WRONG_VALUE; if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); int total=ObjectsTotal(0); for(int i=0;i<total;i++) { string obj_name=ObjectName(0,i); if(StringFind(obj_name,prefix+"BUTT_")<0) continue; PressButtonEvents(obj_name); } } if(engine.LastTradeEvent()!=last_event) { Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent())); last_event=engine.LastTradeEvent(); } } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+ //| Cria o painel de botões | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ? w+2 : 0); if(i==TOTAL_BUTT-3) x=cx; y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+ //| Create the button | //+------------------------------------------------------------------+ bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8) { if(ObjectFind(0,name)<0) { if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) { Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); return false; } ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n"); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray); return true; } return false; } //+------------------------------------------------------------------+ //| Return the button status | //+------------------------------------------------------------------+ bool ButtonState(const string name) { return (bool)ObjectGetInteger(0,name,OBJPROP_STATE); } //+------------------------------------------------------------------+ //| Set the button status | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); } //+------------------------------------------------------------------+ //| Transform enumeration into the button text | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"buy","Buy"); StringReplace(txt,"sell","Sell"); StringReplace(txt,"_limit"," Limit"); StringReplace(txt,"_stop"," Stop"); StringReplace(txt,"close_","Close "); StringReplace(txt,"2"," 1/2"); StringReplace(txt,"_by_"," by "); StringReplace(txt,"profit_","Profit "); StringReplace(txt,"delete_","Delete "); return txt; } //+------------------------------------------------------------------+ //| Handle pressing the buttons | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { //--- Convert button name into its string ID string button=StringSubstr(button_name,StringLen(prefix)); //--- If the button is pressed if(ButtonState(button_name)) { //--- If the BUTT_BUY button is pressed: Open Buy position if(button==EnumToString(BUTT_BUY)) { //--- Get the correct StopLoss and TakeProfit prices relative to StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- Open Buy position trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp); } //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- Set BuyLimit order trade.BuyLimit(lot,price_set,Symbol(),sl,tp); } //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- Set BuyStop order trade.BuyStop(lot,price_set,Symbol(),sl,tp); } //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- Get the correct BuyStop order placement price relative to StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- Calculate BuyLimit order price relative to BuyStop level considering StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- Set BuyStopLimit order trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- If the BUTT_SELL button is pressed: Open Sell position else if(button==EnumToString(BUTT_SELL)) { //--- Get the correct StopLoss and TakeProfit prices relative to StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- Open Sell position trade.Sell(lot,Symbol(),0,sl,tp); } //--- Se o botão BUTT_SELL_LIMIT for pressionado: Coloca uma else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- Set SellLimit order trade.SellLimit(lot,price_set,Symbol(),sl,tp); } //--- If the BUTT_SELL_STOP button is pressed: Set SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- Get correct order placement relative to StopLevel double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- Set SellStop order trade.SellStop(lot,price_set,Symbol(),sl,tp); } //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- Get the correct SellStop order price relative to StopLevel double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- Calculate SellLimit order price relative to SellStop level considering StopLevel double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- Get correct StopLoss and TakeProfit prices relative to the order placement level considering StopLevel double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- Set SellStopLimit order trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Get the Buy position ticket and close the position by the ticket trade.PositionClose(position.Ticket()); } } } //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calculate the closed volume and close the half of the Buy position by the ticket if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- Get the list of all open positions CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Get the list of all open positions CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); if(position_buy!=NULL && position_sell!=NULL) { //--- Close the Buy position by the opposite Sell one trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket()); } } } //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Get the Sell position ticket and close the position by the ticket trade.PositionClose(position.Ticket()); } } } //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- Calculate the closed volume and close the half of the Sell position by the ticket if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- Get the list of all open positions CArrayObj* list_sell=engine.GetListMarketPosition(); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Get the list of all open positions CArrayObj* list_buy=engine.GetListMarketPosition(); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- Encerra a posição de Venda pela de Compra trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket()); } } } //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In the loop from the position with the least profit for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- close each position by its ticket trade.PositionClose(position.Ticket()); } } } //--- If the BUTT_DELETE_PENDING button is pressed: Remove the first pending order else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Get the list of all orders CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Sort the list by placement time list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC); int total=list.Total(); //--- In the loop from the position with the most amount of time for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- delete the order by its ticket trade.OrderDelete(order.Ticket()); } } } //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- If the program is launched in the tester if(MQLInfoInteger(MQL_TESTER)) { //--- Emulate funds withdrawal TesterWithdrawal(withdrawal); } } //--- Wait for 1/10 of a second Sleep(100); //--- "Unpress" the button and redraw the chart ButtonState(button_name,false); ChartRedraw(); } } //+------------------------------------------------------------------+
Compile o EA, inicie-o em uma conta hedging e tente os botões:
Mensagens curtas sobre os eventos da conta são exibidas no diário, enquanto o comentário no gráfico descreve o último evento que ocorreu na conta.
Agora vamos mudar para a conta netting e iniciar o teste:
Nesse caso, o diário contém entradas relacionadas aos eventos de posição que são possíveis apenas em uma conta netting — agora, novas posições são abertas, o EA trabalha com uma única posição. No entanto, os tickets atribuídos a ele são diferentes. Isso pode ser visto no começo — após a inversão da posição de Sell #2 para Buy #3.
Qual é o próximo?
Em seguida, nós implementaremos a ativação do monitoramento de ordens StopLimit e prepararemos a funcionalidade para monitorar as ordens e posições modificadas.
Todos os arquivos da versão atual da biblioteca estão anexados abaixo, juntamente com os arquivos do EA de teste para você testar e fazer o download.
Deixe suas perguntas, comentários e sugestões nos comentários.
Artigos anteriores da série:
Parte 1. Conceito, gerenciamento de dados.
Parte 2. Coleção do histórico de ordens e negócios.
Parte 3 Coleção de ordens e posições de mercado, busca e ordenação.
Parte 4 Eventos de negociação. Conceito.
Parte 5. Classes e coleção de eventos de negociação. Envio de eventos para o programa.