English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Trading bidirezionale e copertura delle posizioni in MetaTrader 5 utilizzando l'API HedgeTerminal, parte 2

Trading bidirezionale e copertura delle posizioni in MetaTrader 5 utilizzando l'API HedgeTerminal, parte 2

MetaTrader 5Sistemi di trading | 12 gennaio 2022, 11:48
559 0
Vasiliy Sokolov
Vasiliy Sokolov

Sommario


Introduzione

Questo articolo è una continuazione della prima parte "Trading bidirezionale e copertura delle posizioni in MetaTrader 5 Utilizzo del pannello HedgeTerminal, parte 1". Nella seconda parte, discuteremo l'integrazione di Expert Advisor e altri programmi MQL5 con la libreria HedgeTerminalAPI. Leggi questo articolo per imparare a lavorare con la libreria. Ti aiuterà a creare Expert Advisor di trading bidirezionali pur continuando a lavorare in un ambiente di trading comodo e semplice.

Oltre alla descrizione della libreria, l'articolo tocca i fondamenti del trading asincrono e della programmazione multi-thread. Queste descrizioni sono fornite nella terza e nella quarta sezione di questo articolo. Pertanto, questo materiale sarà utile per i trader che non sono interessati al trading bidirezionale, ma che vorrebbero scoprire qualcosa di nuovo sulla programmazione asincrona e multi-thread.

Il materiale presentato di seguito è destinato a trader algoritmici esperti che conoscono il linguaggio di programmazione MQL5. Se non conosci MQL5, leggi la prima parte dell'articolo, che contiene semplici diagrammi e disegni che spiegano il principio generale della libreria e del pannello HedgeTerminal.


Capitolo 1. Interazione degli Expert Advisor con HedgeTerminal e il suo pannello

1.1. Installazione di HedgeTermianlAPI. La prima volta che si avvia la Libreria

Il processo di installazione di HedgeTerminalAPI differisce dall'installazione del pannello visivo HT, poiché la libreria non può essere eseguita da sola in MetaTrader 5. Sarà invece necessario sviluppare uno speciale Expert Advisor per chiamare la funzione HedgeTerminalInstall() dalla libreria. Questa funzione imposterà uno speciale file di intestazione Prototypes.mqh che descrive le funzioni disponibili in HedgeTerminalAPI.

Una libreria viene installata su un computer in tre passaggi:

Fase 1. Scarica la libreria HedgeTerminalAPI sul tuo computer. Posizione della libreria relativa al tuo terminale: \MQL5\Experts\Market\HedgeTerminalApi.ex5.

Fase 2. Crea un nuovo Expert Advisor nella procedura guidata MQL5 utilizzando un modello standard. La procedura guidata MQL5 genera il seguente codice sorgente:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }

Fase 3. Sarà necessaria solo una funzione dell'Expert Advisor risultante - OnInit() e la direttiva export che descrive la funzione di installazione speciale HedgeTerminalInstall() esportata dalla libreria HedgeTerminalApi. Esegui questa funzione direttamente nella funzione OnInit(). Il codice sorgente contrassegnato in giallo esegue queste operazioni:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#import "HedgeTerminalAPI.ex5"
   void HedgeTerminalInstall(void);
#import

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   HedgeTerminalInstall();
   ExpertRemove();   
//---
   return(INIT_SUCCEEDED);
  }

Fase 4. Le tue ulteriori azioni dipendono dal fatto che hai acquistato o meno la libreria. Se l’hai acquistata, puoi eseguire l'Expert Advisor in tempo reale direttamente sul grafico. Questo avvierà il programma di installazione standard dell'intera linea di prodotti di HedgeTerminal. Puoi facilmente completare questo passaggio seguendo le istruzioni descritte nelle sezioni 2.1 e 2.2 dell'articolo "Trading bidirezionale e copertura delle posizioni in MetaTrader 5 Utilizzando il pannello HedgeTerminal, parte 1". La procedura guidata di installazione installa tutti i file richiesti, incluso il file di intestazione e l’Expert Advisor test sul tuo computer.

Se non hai acquistato la libreria e desideri solo testarla, l'operazione EA in tempo reale non sarà disponibile, ma puoi testare l'API eseguendo l'EA nel tester di strategia. In questo caso il programma di installazione non verrà eseguito. In modalità test, HedgeTermianalAPI funziona in modalità utente singolo, quindi non necessita di file installati in modalità normale. Significa che non è necessario configurare nient'altro.

Non appena viene eseguito il test EA, viene creata la cartella \HedgeTerminal nella cartella comune del terminale. Il percorso normale della directory comune dei terminali MetaTrader è c:\Users\<Username>\AppData\Roaming\MetaQuotes\Terminal\Common\Files\HedgeTerminal\, dove <Username> è il nome dell’account del computer. La cartella \HedgeTerminal contiene già i file \MQL5\Include\Prototypes.mqh e \MQL5\Experts\Chaos2.mq5. Copia questi file nelle stesse directory del tuo terminale: file Prototypes.mqh in \MetaTrader5\MQL5\Include, e il file Chaos2.mq5 in \MetaTrader5\MQL5\Experts.

File Prototypes.mqh è un file di intestazione contenente la descrizione delle funzioni esportate dalla libreria HedgeTerminalAPI. Il loro scopo e le descrizioni sono contenuti nei loro commenti.

Il file Chaos2.mq5 contiene un esempio di EA descritto nella sezione "La funzione SendTradeRequest e la struttura HedgeTradeRequest spiegate con l'esempio di Chaos II EA". In questo modo puoi capire visivamente come funziona HedgeTerminalAPI e come sviluppare un Expert Advisor utilizzando la tecnologia di virtualizzazione di HedgeTerminal.

I file copiati sono disponibili per i tuoi EA. Quindi devi solo includere il file di intestazione nel codice sorgente di Expert Advisor per iniziare a utilizzare la libreria. Ecco un esempio:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   int transTotal = TransactionsTotal();
   printf((string)transTotal);
  }

Il codice sopra ottiene il numero totale di posizioni attive e visualizza il numero nella scheda "Experts" del terminale MetaTrader 5.

È importante capire che HedgeTerminal viene effettivamente inizializzato alla prima chiamata di una delle sue funzioni. Questa inizializzazione si chiama lazy initialization (inizializzazione pigra). Pertanto, la prima chiamata di una delle sue funzioni può richiedere molto tempo. Se vuoi una risposta veloce durante la prima esecuzione, devi inizializzare HT in anticipo, ad esempio puoi chiamare la funzione TransactionTotal() nel blocco di OnInit().

Con l'inizializzazione lazy puoi omettere l'inizializzazione esplicita dell'Expert Advisor. Ciò semplifica notevolmente il lavoro con HedgeTerminal e rende superflua la preconfigurazione.


1.2. Integrazione di Expert Advisor con HedgeTerminal Panel

Se disponi del pannello visivo di HedgeTerminal e della versione completa della libreria, che può essere eseguita in tempo reale, puoi integrare i tuoi Expert Advisor con il pannello, in modo che anche tutte le operazioni di trading da loro eseguite vengano visualizzate nel pannello. In generale, l'integrazione non è visibile. Se utilizzi le funzioni HedgeTermianalAPI, le azioni eseguite dai robot vengono visualizzate automaticamente sul pannello. Tuttavia, puoi espandere la visuale indicando il nome EA in ogni transazione impegnata. Puoi farlo decommentando la riga sottostante nel file Settings.xml:

<Column ID="Magic" Name="Magic" Width="100"/>

Questo tag si trova nelle sezioni <Active-Position><History-Position>.

I commenti vengono rimossi e i tag vengono inclusi nell'elaborazione. Dopo il riavvio del pannello, nelle tabelle delle posizioni attive e dello storico apparirà una nuova colonna di "Magic". La colonna contiene il numero magico dell'Expert Advisor, a cui appartiene la posizione.

Se vuoi mostrare il nome dell’EA invece del suo numero magico, aggiungi il nome al file alias ExpertAliases.xml. Ad esempio, se il numero magico di un EA è 123847 e vuoi visualizzare il suo nome come "ExPro 1.1", aggiungi il seguente tag al file:

<Expert Magic="123847" Name="ExPro 1.1"></Expert>

Se è fatto correttamente, il nome del EA verrà visualizzato al posto del suo numero magico nella colonna appropriata:

Fig. 1. Visualizzazione del nome del EA invece del numero Magico

Fig. 1. Fig. 1. Visualizzazione del nome del EA invece del numero Magico

Nota che il pannello e gli Expert Advisor comunicano in tempo reale. Ciò significa che se chiudi la posizione di un EA direttamente sul pannello, l'EA lo saprà alla la prossima chiamata della funzione TransactionsTotal(). E viceversa: dopo che l'Expert Advisor chiude la sua posizione, scompare immediatamente dalla scheda delle posizioni attive.


1.3. Principi generali del funzionamento di HedgeTerminalAPI

Oltre alle posizioni bidirezionali, HedgeTerminal funziona anche con altre tipologie di trading, come ordini in sospeso, deal(accordi) e operazioni dei broker. HedgeTerminal tratta tutti questi tipi come un singolo gruppo di transazioni. Un deal, un ordine in sospeso, una posizione bidirezionale: sono tutte transazioni. Tuttavia, una transazione non può esistere da sola. In termini di programmazione orientata agli oggetti, una transazione può essere introdotta come una classe base astratta, dalla quale vengono ereditate tutte le possibili entità di trading, come i deal e le posizioni bidirezionali. A questo proposito tutte le funzioni di HedgeTerminalAPI possono essere suddivise in diversi gruppi:

  1. Funzioni di ricerca e selezione delle transazioni. La firma comune delle funzioni e il loro funzionamento coincidono quasi completamente con le funzioni OrderSend() e OrderSelect() in MetaTrader 4;
  2. Funzioni per ottenere le proprietà di una transazione selezionata. Ogni transazione ha un insieme specifico di proprietà e funzioni specifiche per la selezione delle proprietà. La firma comune delle funzioni e il modo in cui funzionano assomigliano alle funzioni del sistema MetaTrader 5 nel modo in cui accedono a posizioni, transazioni e ordini (come OrderGetDouble() o HistoryDealGetInteger());
  3. HedgeTerminalAPI utilizza una sola funzione di trading: SendTradeRequest(). Questa funzione permette di chiudere una posizione bidirezionale o parte di essa. La stessa funzione viene utilizzata per modificare stop loss, take profit o commento in uscita. L'utilizzo della funzione è simile a OrderSend() in MetaTrader 5;
  4. La funzione per ottenere errori comuni GetHedgeError(), funzioni per l'analisi dettagliata delle azioni di trading di HedgeTerminal: TotalActionsTask() e GetActionResult(). Utilizzati anche per il rilevamento degli errori. Non ci sono analoghi in MetaTrader 4 o MetaTrader 5.

Lavorare con quasi tutte le funzioni è simile all'utilizzo delle funzioni di sistema MetaTrader 4 e MetaTrader 5. Di norma, l'input della funzione è un identificatore (valore di enumerazione) e la funzione restituisce un valore che gli corrisponde.

Per ogni funzione sono disponibili enumerazioni specifiche. La firma di chiamata comune è la seguente:

<value> = Function(<identifier>);

Consideriamo un esempio di come ottenere un identificatore di posizione univoco. Ecco come appare la linea in MetaTrader 5:

ulong id = PositionGetInteger(POSITION_IDENTIFIER);

In HedgeTerminal, la ricezione di un identificatore univoco di una posizione bidirezionale è la seguente:

ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)

I principi generali delle funzioni sono gli stessi. Solo i tipi di enumerazioni sono diversi.


1.4. Selezione delle transazioni

La selezione di una transazione sta passando attraverso l'elenco delle transazioni, che è simile alla ricerca di ordini in MetaTrader 4. Tuttavia, in MetaTrader 4 vengono cercati solo gli ordini, mentre in HedgeTerminal è possibile trovare qualsiasi transazione, ad esempio un ordine in sospeso o una posizione di copertura (hedging). Pertanto, ogni transazione deve essere prima selezionata utilizzando la funzione TransactionSelect() e quindi il suo tipo deve essere identificato tramite TransactionType().

Finora sono disponibili due elenchi di transazioni: transazioni attive e cronologiche. La lista da applicare è definita in base al modificatore ENUM_MODE_TRADES. È simile al modificatore MODE_TRADES in MetaTrader 4.

L'algoritmo di ricerca e selezione delle transazioni è il seguente:

1: for(int i=TransactionsTotal(MODE_TRADES)-1; i>=0; i--)
2:     {
3:      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))continue;
4:      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
5:      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
6:      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
7:      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN)continue;
8:      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)
9:     }

Il codice scorre l'elenco delle transazioni attive nel ciclo for (riga 1). Prima di procedere con la transazione, selezionala utilizzando TransactionSelect() (riga 3). Da queste transazioni vengono selezionate solo le posizioni bidirezionali (riga 4). Se il numero magico della posizione e il suo simbolo non corrispondono al numero magico dell'EA corrente e del simbolo su cui è in esecuzione, HT passa alla posizione successiva (righe 5 e 6). Quindi definisce l'identificatore di posizione univoco (riga 8).

Particolare attenzione dovrebbe essere prestata alla riga 7. Le posizioni selezionate devono essere verificate in termini di possibilità di modifica. Se la posizione è già in fase di modifica, non può essere modificata nel thread corrente, sebbene sia possibile ottenere una delle sue proprietà. Se la posizione è bloccata, meglio attendere che venga rilasciata per accedere alle sue proprietà o riprovare a modificarla. La proprietà HEDGE_POSITION_STATE viene utilizzata per scoprire se è possibile modificare la posizione.

Il modificatore POSITION_STATE_FROZEN indica che la posizione è "congelata" e non può essere modificata. Il modificatore POSITION_STATE_ACTIVE mostra che una posizione è attiva e può essere modificata. Questi modificatori sono elencati nell'enumerazione ENUM_HEDGE_POSITION_STATE, che è documentata nella sezione appropriata.

Se è necessaria una ricerca nelle transazioni storiche, il modificatore MODE_TRADES nelle funzioni TransactionTotal() e TransactionSelect() deve essere sostituito da MODE_HISTORY.

Una transazione in HedgeTerminal può essere annidata in un'altra. Questo è molto diverso dal concetto di MetaTrader 5 dove non c'è nidificazione. Ad esempio, la posizione storica bidirezionale in HedgeTerminal è costituita da due ordini, ciascuno dei quali include un insieme arbitrario di operazioni. La nidificazione può essere rappresentata come segue:

Fig. 2. Transazioni annidate

Fig. 2. Transazioni annidate

La nidificazione delle transazioni è chiaramente visibile nel pannello visivo di HedgeTerminal.

La schermata seguente mostra i dettagli di una posizione di MagicEx 1.3:

Fig. 3. Transazioni nidificate nel pannello HedgeTerminal

Fig. 3. Transazioni nidificate nel pannello HedgeTerminal

Puoi accedere alle proprietà di un particolare ordine o deal nella posizione bidirezionale.

Per fare questo:

  1. Seleziona una transazione storica e assicurati che sia una posizione bidirezionale;
  2. Seleziona uno degli ordini di questa posizione usando HedgeOrderSelect();
  3. Ottieni una delle proprietà dell'ordine selezionato: il numero di offerte che contiene;
  4. Seleziona una delle offerte appartenenti all'ordine cercando tra tutte le offerte;
  5. Ottieni la proprietà dell'offerta richiesta.

Si noti che dopo che la transazione è stata selezionata, le sue proprietà specifiche diventano disponibili per essa. Ad esempio, se la transazione è un ordine, dopo la selezione tramite HedgeOrderSelect(), puoi trovare il numero di offerte per essa (HedgeOrderGetInter(HEDGE_ORDER_DEALS_TOTAL)) o il prezzo medio ponderato di entrata (HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED)).

Scopriamo il prezzo dell'affare #1197610, che è segnato in rosso sullo screenshot. Questo accordo fa parte della posizione bidirezionale di MagicEx 1.3 EA.

Attraverso il codice seguente, l'EA può accedere alla sua posizione e a questo accordo:

#include <Prototypes.mqh>

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
    {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // Select transaction #i;
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;                 // If transaction is not position - continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;  // If position is not main - continue;
      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID);   // Get id for closed order;
      if(id!=5917888)continue;                                             // If id of position != 5917888 - continue;
      printf("1: -> Select position #"+(string)id);                        // Print position id;
      if(!HedgeOrderSelect(ORDER_SELECTED_CLOSED))continue;                // Select closed order or continue;    
      ulong order_id = HedgeOrderGetInteger(HEDGE_ORDER_ID);               // Get id closed order;
      printf("2: ----> Select order #" + (string)order_id);                // Print id closed order;
      int deals_total = (int)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);// Get deals total in selected order;
      for(int deal_index = deals_total-1; deal_index >= 0; deal_index--)   // Search deal #1197610...
        {
         if(!HedgeDealSelect(deal_index))continue;                         // Select deal by index or continue;
         ulong deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);               // Get id for current deal;
         if(deal_id != 1197610)continue;                                   // Select deal #1197610;
         double price = HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED);     // Get price executed;
         printf("3: --------> Select deal #"+(string)deal_id+              // Print price excecuted;
              ". Executed price = "+DoubleToString(price,0));
        }
     }
  }

Dopo l'esecuzione del codice, nella scheda Expert del terminale MetaTrader 5 verrà creata la seguente voce:

2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      3: --------> Select deal #1197610. Executed price = 4735
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      2: ----> Select order #6389111
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      1: -> Select position #5917888

L'EA seleziona prima la posizione #5917888, quindi seleziona l'ordine #6389111 all'interno della posizione. Una volta selezionato l'ordine, l'EA inizia a cercare il numero di offerta 1197610. Quando il deal viene trovato, l'EA ottiene il suo prezzo di esecuzione e aggiunge il prezzo nel journal.


1.5. Come ottenere codici di errore utilizzando GetHedgeError()

Durante l'utilizzo dell'ambiente HedgeTerminal possono verificarsi errori e situazioni impreviste. In questi casi vengono utilizzate le funzioni di acquisizione e analisi degli errori.

Il caso più semplice in cui ricevi un errore è quando ti dimentichi di selezionare una transazione utilizzando la funzione TransactionSelect(). In questo caso la funzione TransactionType() restituirà il modificatore TRANS_NOT_DEFINED.

Per capire dove sta il problema, dobbiamo ottenere il modificatore dell'ultimo errore. Il modificatore ci dirà che ora la transazione è stata selezionata. Il seguente codice fa questo:

for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
  {
   //if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // forgot to select;
   ENUM_TRANS_TYPE type = TransactionType();
   if(type == TRANS_NOT_DEFINED)
   {
      ENUM_HEDGE_ERR error = GetHedgeError();
      printf("Error, transaction type not defined. Reason: " + EnumToString(error));
   }
  }

Questo è il messaggio risultante:

Error, transaction type not defined. Reason: HEDGE_ERR_TRANS_NOTSELECTED

L'ID dell’errore suggerisce che abbiamo dimenticato di selezionare una transazione prima di provare a ottenerne il tipo.

Tutti i possibili errori sono elencati nella struttura ENUM_HEDGE_ERR.


1.6. Analisi dettagliata del trading e identificazione degli errori utilizzando TotalActionsTask() e GetActionResult()

Oltre agli errori che si verificano nel processo di lavoro con l'ambiente HedgeTerminal, possono verificarsi errori di trading come risultato della chiamata SendTradeRequest(). Questi tipi di errori sono più difficili da affrontare. Un'attività eseguita da SendTradeRequest() può contenere più attività di trading (sottoattività). Ad esempio, per modificare il commento in uscita in una posizione attiva protetta da un livello di stop loss, è necessario effettuare due azioni di trading:

  1. Annullare l'ordine di stop in sospeso che rappresenta il livello di stop;
  2. Inserisci un nuovo ordine di stop in sospeso con un nuovo commento al posto dell'ordine precedente.

Se il nuovo ordine di stop si attiva, il suo commento verrà visualizzato come commento di chiusura della posizione. Questo è un modo corretto.

Tuttavia, l'attività può essere eseguita in parte. Supponiamo che l'ordine in sospeso venga annullato con successo, ma l'inserimento di un nuovo ordine non va a buon fine (per qualsiasi motivo). In questo caso, la posizione verrà lasciata senza il livello di stop loss. Per essere in grado di gestire questo errore, l'EA dovrà chiamare un registro delle attività speciali e cercare in esso per trovare la sottoattività che non è riuscita.

Questo viene fatto utilizzando due funzioni: TotalActionsTask() restituisce il numero totale di azioni di trading (sottoattività) all'interno di questa attività; e GetActionResult() accetta l'indice delle sottoattività e ne restituisce il tipo e il risultato dell'esecuzione. Poiché tutte le operazioni di trading vengono eseguite utilizzando gli strumenti MetaTrader 5 standard, il risultato della loro performance corrisponde al codice di ritorno del server di trading.

In generale, l'algoritmo di ricerca del motivo dell'errore è il seguente:

  1. Ottenere il numero totale di attività secondarie nell'attività utilizzando TotalActionsTask();
  2. Ricerca in tutte le sottoattività nel ciclo for. Determinazione del tipo di ciascuna sottoattività e del relativo risultato.

Supponiamo che l'ordine di arresto con un nuovo commento non possa essere inserito perché il prezzo di esecuzione dell'ordine è troppo vicino al livello di prezzo corrente.

Il codice dell’esempio seguente mostra come l'EA potrebbe trovare il motivo di questo errore:

#include <Prototypes.mqh> 

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
//detect active position
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;
      ENUM_TRANS_TYPE type=TransactionType();
      if(type==TRANS_NOT_DEFINED)
        {
         ENUM_HEDGE_ERR error=GetHedgeError();
         printf("Error, transaction not defined. Reason: "+EnumToString(error));
        }
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
      HedgeTradeRequest request;
      request.action=REQUEST_MODIFY_COMMENT;
      request.exit_comment="My new comment";
      if(!SendTradeRequest(request)) // Is error?
        {
         for(uint action=0; action < TotalActionsTask(); action++)
           {
            ENUM_TARGET_TYPE typeAction;
            int retcode=0;
            GetActionResult(action, typeAction, retcode);
            printf("Action#" + (string)action + ": " + EnumToString(type) +(string)retcode);
           }
        }
     }
  }

Dopo l'esecuzione del codice apparirà il seguente messaggio:

Action #0 TARGET_DELETE_PENDING_ORDER 10009 (TRADE_RETCODE_PLACED)
Action #1 TARGET_SET_PENDING_ORDER 10015 (TRADE_RETCODE_INVALID_PRICE)

Confrontando i numeri con i modificatori standard dei codici di ritorno del trade server, scopriamo che l'ordine in sospeso è stato rimosso con successo, ma l'inserimento di un nuovo ordine non è riuscito. Il trade server ha restituito l’errore 10015 (prezzo errato), che potrebbe significare che il prezzo corrente è troppo vicino al livello di stop.

Sapendo questo, l'EA può assumere il controllo dei livelli di arresto. Per fare ciò, l'EA dovrà solo chiudere questa posizione utilizzando la stessa funzione SendTradeRequest().


1.7. Monitoraggio dello stato di esecuzione dell'attività di trading

Ogni attività di trading può consistere in un numero qualsiasi di attività secondarie che devono essere eseguite in sequenza.

Nella modalità asincrona, un'attività può essere eseguita in più passaggi del codice. Ci possono essere anche casi in cui l'attività può "bloccarsi". Pertanto è necessario il controllo dell’EA sull'esecuzione dell'attività. Quando si chiama la funzione HedgePositionGetInteger() con il modificatore HEDGE_POSITION_TASK_STATUS, essa restituisce l'enumerazione del tipo ENUM_TASK_STATUS contenente lo stato del task di posizione corrente.

Ad esempio, se qualcosa va storto dopo aver inviato un ordine per chiudere una posizione, e la posizione non viene chiusa, è necessario ottenere lo stato dell'attività.

L'esempio seguente mostra il codice che un Expert Advisor asincrono può eseguire per analizzare lo stato dell'attività per la posizione:

ENUM_TASK_STATUS status=HedgePositionGetInteger(HEDGE_POSITION_TASK_STATUS);
switch(status)
  {
   case TASK_STATUS_COMPLETE:
      printf("Task complete!");
      break;
   case TASK_STATUS_EXECUTING:
      printf("Task executing. Waiting...");
      Sleep(200);
      break;
   case TASK_STATUS_FAILED:
      printf("Filed executing task. Print logs...");
      for(int i=0; i<TotalActionsTask(); i++)
        {
         ENUM_TARGET_TYPE type;
         uint retcode;
         GetActionResult(i,type,retcode);
         printf("#"+i+" "+EnumToString(type)+" "+retcode);
        }
      break;
   case TASK_STATUS_WAITING:
      printf("task will soon start.");
      break;
  }

Si noti che l'esecuzione di alcune attività complesse richiede più iterazioni.

Nella modalità asincrona, i prossimi eventi che segnalano cambiamenti nell'ambiente di trading avviano una nuova iterazione. Pertanto, tutte le iterazioni vengono eseguite senza indugio, una dopo l'altra, a seguito delle risposte ricevute dal server di trading. L'esecuzione dell'attività differisce nella modalità sincrona.

Il metodo sincrono utilizza l'emulatore dell'operazione sincrona, grazie al quale gli utenti possono eseguire anche attività composite in un unico passaggio. L'emulatore utilizza time lag (ritardi). Ad esempio, dopo l'avvio dell'esecuzione di un'attività secondaria, l'emulatore non restituisce il thread di esecuzione all'EA. Invece, aspetta un po' di tempo aspettandosi che l'ambiente di trading cambi. Successivamente, rilegge di nuovo l'ambiente di trading. Se comprende che la sottoattività è stata completata con successo, avvia le sottoattività successive.

Questo processo riduce in qualche modo le prestazioni complessive, poiché richiede un tempo di attesa. Ma trasforma l'esecuzione di compiti anche complessi in un'operazione sequenziale piuttosto semplice eseguita in una singola chiamata di funzione. Pertanto, non è quasi mai necessario analizzare il registro di esecuzione dell'attività nel metodo sincrono.


1.8. Come modificare e chiudere posizioni bidirezionali

Le posizioni bidirezionali vengono modificate e chiuse utilizzando la funzione SendTradeRequest(). Solo tre opzioni possono essere applicate a una posizione attiva:

  1. Una posizione può essere chiusa completamente o parzialmente;
  2. Stop Loss e Take Profit di una posizione possono essere modificati
  3. Il commento in uscita di una posizione può essere modificato.

La posizione storica non può essere modificata. Simile alla funzione OrderSend() in MetaTrader 5, SendTradeRequest() utilizza una query precompilata sotto forma di una struttura di trading HedgeTraderRequest. Leggi la documentazione per ulteriori dettagli sulla funzione SendTradeRequest() e sulla struttura HedgeTraderRequest. L'esempio che mostra la modifica e la chiusura della posizione è disponibile nella sezione su Chaos II EA.


1.9. Come impostare le proprietà di HedgeTerminal da un Exper Advisor

HedgeTerminal possiede una serie di proprietà, come la frequenza di aggiornamento, il numero di secondi di attesa per una risposta dal server e altre.

Tutte queste proprietà sono definite in Settings.xml. Quando un EA è in esecuzione in tempo reale, la libreria legge le proprietà dal file e imposta i parametri interni appropriati. Quando l'EA viene testato su un grafico, il file Settings.xml non viene utilizzato. Tuttavia, in alcune situazioni potrebbe essere necessario modificare singolarmente le proprietà del EA, indipendentemente dal fatto che sia in esecuzione su un grafico o nel tester di strategia.

Questo viene fatto attraverso lo speciale set di funzioni HedgePropertySet… La versione attuale presenta un solo prototipo di questo set:

enum ENUM_HEDGE_PROP_INTEGER
{
   HEDGE_PROP_TIMEOUT,
};

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, int value)

Ad esempio, per impostare il timeout affinché la libreria attenda una risposta del server, scrivi quanto segue:

bool res = HedgePropertySetInteger(HEDGE_PROP_TIMEOUT, 30);

Se la risposta del server non viene ricevuta entro 30 secondi dall'invio di una richiesta asincrona, la posizione bloccata verrà rilasciata.


1.10. Modalità di funzionamento sincrono e asincrono

HedgeTerminal e la sua API svolgono attività di trading in modo completamente asincrono.

Tuttavia, questa modalità richiede una logica più complessa degli EA. Per nascondere questa complessità, HedgeTerminalAPI include uno speciale emulatore di operazioni sincrone, che consente agli EA sviluppati con il metodo sincrono convenzionale, di comunicare con algoritmi asincroni di HedgeTerminalAPI. Questa interazione viene rivelata al momento della modifica e chiusura della posizione bidirezionale tramite SendTradeRequest(). Questa funzione consente di eseguire un'attività commerciale in modalità sincrona o asincrona. Da impostazione predefinita, tutte le azioni di trading vengono eseguite in modo sincrono tramite l'emulatore di operazioni sincrone. Tuttavia, se una richiesta di scambio (struttura HedgeTradeRequest) contiene un flag asynch_mode = true specificato in modo esplicito, l'attività di trading verrà eseguita in modalità asincrona.

Nella modalità asincrona, le attività vengono eseguite indipendentemente dal thread principale. L'implementazione dell'interazione tra un EA asincrono e gli algoritmi asincroni di HedgeTerminal non è ancora completa.

L'emulatore sincrono è molto semplice. Avvia le attività secondarie in sequenza, quindi attende un po' di tempo fino a quando l'ambiente di trading in MetaTrader 5 non cambia. L'emulatore analizza queste modifiche e determina lo stato dell'attività corrente. Se l'esecuzione dell'attività ha esito positivo, l'emulatore passa a quella successiva.

L'emulatore sincrono provoca lievi ritardi nell'esecuzione degli ordini di trading. Ciò è dovuto al fatto che l'ambiente di trading in MetaTrader 5 impiega del tempo per riflettere le attività di trading eseguite. La necessità di accedere all'ambiente è principalmente connessa al fatto che HedgeTermianlAPI non può accedere agli eventi che arrivano al gestore OnTradeTransaction() nella modalità di emulazione thread sincrona.

I problemi di interazione tra thread asincroni, nonché tra thread asincroni e sincroni tramite emulazione sono troppo complicati e non hanno soluzioni ovvie.


1.11. Gestione delle proprietà di posizione bidirezionale tramite l'esempio di uno script

Nello script sottostante, la funzione TransactionSelect() ricerca tutte le transazioni disponibili nell'elenco delle transazioni attive.

Ogni transazione è selezionata dall'elenco. Se la transazione è una posizione, alcune delle sue proprietà sono accessibili e quindi stampate. Oltre alle proprietà delle posizioni, vengono stampate anche le proprietà degli ordini e dei deal all'interno della posizione. Un ordine e una transazione vengono prima selezionati utilizzando rispettivamente HedgeOrderSelect() e HedgeDealSelect().

Tutte le proprietà della posizione, i suoi ordini e affari sono combinati e stampati come un'unica riga utilizzando la funzione di sistema printf.

//+------------------------------------------------------------------+
//|                                           sample_using_htapi.mq5 |
//|         Copyright 2014, Vasiliy Sokolov, Russia, St.-Petersburg. |
//|                              https://login.mql5.com/ru/users/c-4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

// Include prototypes function of HedgeTerminalAPI library.
#include <Prototypes.mqh> 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnStart()
  {
   // Search all transaction in list transaction...
   for(int i=TransactionsTotal(); i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))                           // Selecting from active transactions
        {
         ENUM_HEDGE_ERR error=GetHedgeError();                                      // Get reason if selecting has failed
         printf("Error selecting transaction # "+(string)i+". Reason: "+            // Print reason
                EnumToString(error));
         ResetHedgeError();                                                         // Reset error
         continue;                                                                  // Go to next transaction
        }
      // Only for hedge positions
      if(TransactionType()==TRANS_HEDGE_POSITION) 
        {
         // --- Position captions --- //
         ENUM_TRANS_DIRECTION direction=(ENUM_TRANS_DIRECTION)                      // Get direction caption
                              HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
         double price_entry = HedgeOrderGetDouble(HEDGE_ORDER_PRICE_EXECUTED);      // Get volume of positions
         string symbol = HedgePositionGetString(HEDGE_POSITION_SYMBOL);             // Get symbol of position
         // --- Order captions --- //
         if(!HedgeOrderSelect(ORDER_SELECTED_INIT))continue;                        // Selecting init order in position
         double slippage = HedgeOrderGetDouble(HEDGE_ORDER_SLIPPAGE);               // Get some slippage was
         uint deals_total = (uint)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);    // Get deals total
         // --- Deals captions --- //
         double commissions=0.0;
         ulong deal_id=0;
         //Search all deals in list deals...
         for(uint d_index=0; d_index<deals_total; d_index++)                        
           {
            if(!HedgeDealSelect(d_index))continue;                                  // Selecting deal by its index
            deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);                           // Get deal id
            commissions += HedgeDealGetDouble(HEDGE_DEAL_COMMISSION);               // Count commissions
           }
         int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
         printf("Position #" + (string)i + ": DIR " + EnumToString(direction) +     // Print result line
         "; PRICE ENTRY " + DoubleToString(price_entry, digits) + 
         "; INIT SLIPPAGE " + DoubleToString(slippage, 2) + "; LAST DEAL ID " +
         (string)deal_id + "; COMMISSIONS SUM " + DoubleToString(commissions, 2));
        }
     }
  }

1.12. La funzione SendTradeRequest() e la struttura HedgeTradeRequest spiegate con l'esempio di Chaos II EA

Ad esempio, sviluppiamo un trading robot basato sulle tattiche di trading proposte da Bill Williams nel suo libro Trading Chaos. Seconda edizione.

Non seguiremo tutte le sue raccomandazioni, ma semplificheremo lo schema omettendo Alligator indicatore e alcune altre condizioni. La scelta di questa strategia nasce da diverse considerazioni. La principale è che questa strategia include tattiche di mantenimento della posizione composite. A volte è necessario chiudere una parte di una posizione e spostare lo stop loss sul pareggio.

Quando si passa al pareggio, il livello di stop dovrebbe essere trascinato dopo il prezzo. La seconda considerazione è che questa tattica è abbastanza conosciuta e gli indicatori sviluppati per essa sono inclusi nel pacchetto di consegna standard MetaTrader 5. Modifichiamo e semplifichiamo leggermente le regole, per evitare che la complessa logica dell'Expert Advisor ostacoli il suo obiettivo primario, cioè quello di mostrare un esempio di interazione di EA con la libreria HedgeTerminalAPI. La logica dell'EA utilizza la maggior parte delle funzioni di trading di HedgeTerminalAPI. Questo è un buon test per la libreria.

Cominciamo dalla reversal bar (barra di inversione). Una barra di inversione rialzista è una barra con il prezzo di chiusura nel terzo superiore, il cui Basso è quello più basso per le ultime N barre. Una barra di inversione ribassista è una barra con il prezzo di chiusura nel terzo inferiore, il cui massimo è quello più alto per le ultime N barre. N è un parametro scelto casualmente, può essere impostato durante l'avvio dell'Expert Advisor. Questo differisce dalla classica strategia "Chaos 2".

Dopo aver definito la barra di inversione, vengono inseriti due ordini in sospeso. Per una barra rialzista, gli ordini vengono piazzati sopra il suo massimo, una barra ribassista, appena sotto il suo minimo. Se questi due ordini non si attivano durante le barre OldPending, il segnale viene considerato obsoleto e gli ordini vengono annullati. I valori di OldPending e N vengono impostati dall'utente prima di avviare l'EA sul grafico.

Gli ordini si innescano e si trasformano in due posizioni bidirezionali. L'EA li distingue dai numeri nei commenti, rispettivamente "# 1" e "# 2". Questa non è una soluzione molto elegante, ma va bene a scopo dimostrativo. Una volta che gli ordini si attivano, viene posizionato uno stop loss al massimo (per una barra ribassista) o al minimo (se la barra è rialzista) della barra di inversione.

La prima posizione ha target stretti. Il suo take profit è impostato in modo tale che, in caso di trigger, il profitto della posizione sarebbe pari alla perdita assoluta di uno stop loss attivato. Ad esempio, se una posizione long viene aperta a un prezzo di 1,0000 e il suo stop loss è a livello di 0,9000, il livello di take profit sarebbe 1.0000 + (1.0000 - 0.9000) = 1.1000. L'EA esce dalla posizione allo stop loss o al take profit.

La seconda posizione è a lungo termine. Il suo stop loss è seguito dal prezzo. Lo stop si sposta dopo il frattale di Bill Williams appena formato. Per una posizione long, lo stop viene spostato in base ai frattali inferiori, mentre i frattali superiori vengono utilizzati per una posizione short. L'EA esce dalla posizione solo allo stop loss.

Il grafico seguente illustra questa strategia:

Fig. 4. La rappresentazione delle posizioni bidirezionali del EA Chaos 2 sul grafico dei prezzi

Fig. 4. Fig. 4. La rappresentazione delle posizioni bidirezionali del EA Chaos 2 sul grafico dei prezzi

Le barre di inversione sono contrassegnate da una cornice rossa. Il periodo N su questo grafico è uguale a 2. Il momento più opportuno è scelto per questa strategia. Le posizioni corte sono mostrate come una linea tratteggiata blu, le posizioni lunghe sono rappresentate dal verde. Come si può vedere, le posizioni long e short possono esistere contemporaneamente anche in una strategia relativamente semplice. Presta attenzione al periodo dal 5 all'8 gennaio 2014.

Questo è un punto di svolta per la tendenza al ribasso dell'AUDCAD. Il 4 gennaio è stato ricevuto un segnale dalla barra di inversione rialzista e il 5 gennaio sono state aperte due posizioni long. Allo stesso tempo, c'erano ancora tre posizioni short i cui stop sono stati trascinati seguendo il trend (linea rossa tratteggiata). Quindi, il 7 gennaio, è scattato lo stop per le posizioni short, quindi sul mercato sono rimaste solo le posizioni long.

Sarebbe difficile monitorare i cambiamenti su una posizione netta, poiché il volume netto non terrà conto del numero di posizioni effettivamente mantenute dall'EA. HedgeTerminal consente agli EA di monitorare le proprie posizioni individuali indipendentemente dall'attuale posizione netta, rendendo possibile ottenere questi grafici e sviluppare strategie simili.

Di seguito è riportato il codice che implementa questa strategia.

Non ho intenzionalmente utilizzato la programmazione orientata agli oggetti, per adattare il codice ai principianti:

//+------------------------------------------------------------------+
//|                                                       Chaos2.mq5 |
//|     Copyright 2014, Vasiliy Sokolov specially for HedgeTerminal. |
//|                                          St.-Petersburg, Russia. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Prototypes.mqh>           // Include prototypes function of HedgeTerminalAPI library

//+------------------------------------------------------------------+
//| Input parameters.                                                |
//+------------------------------------------------------------------+
input uint N=2;                     // Period of extermum/minimum
input uint OldPending=3;            // Old pending

//+------------------------------------------------------------------+
//| Private variables of expert advisor.                             |
//+------------------------------------------------------------------+
ulong Magic = 2314;                 // Magic number of expert
datetime lastTime = 0;              // Remembered last time for function DetectNewBar
int hFractals = INVALID_HANDLE;     // Handle of indicator 'Fractals'. See: 'https://www.mql5.com/it/docs/indicators/ifractals'
//+------------------------------------------------------------------+
//| Type of bar by Bill Wiallams strategy.                           |
//+------------------------------------------------------------------+
enum ENUM_BAR_TYPE
  {
   BAR_TYPE_ORDINARY,               // Ordinary bar. 
   BAR_TYPE_BEARISH,                // This bar close in the upper third and it's minimum is lowest at N period
   BAR_TYPE_BULLISH,                // This bar close in the lower third and it's maximum is highest at N period
  };
//+------------------------------------------------------------------+
//| Type of Extremum.                                                |
//+------------------------------------------------------------------+
enum ENUM_TYPE_EXTREMUM
  {
   TYPE_EXTREMUM_HIGHEST,           // Extremum from highest prices
   TYPE_EXTREMUM_LOWEST             // Extremum from lowest prices
  };
//+------------------------------------------------------------------+
//| Type of position.                                                |
//+------------------------------------------------------------------+
enum ENUM_ENTRY_TYPE
  {
   ENTRY_BUY1,                      // Buy position with short target
   ENTRY_BUY2,                      // Buy position with long target
   ENTRY_SELL1,                     // Sell position with short target
   ENTRY_SELL2,                     // Sell position with long target
   ENTRY_BAD_COMMENT                // My position, but wrong comment
  };
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create indicator 'Fractals' ---//
   hFractals=iFractals(Symbol(),NULL);
   if(hFractals==INVALID_HANDLE)
      printf("Warning! Indicator 'Fractals' not does not create. Reason: "+
             (string)GetLastError());
//--- Corection magic by timeframe ---//
   int minPeriod=PeriodSeconds()/60;
   string strMagic=(string)Magic+(string)minPeriod;
   Magic=StringToInteger(strMagic);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete indicator 'Fractals' ---//
   if(hFractals!=INVALID_HANDLE)
      IndicatorRelease(hFractals);
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Run logic only open new bar. ---//
   int totals=SupportPositions();
   if(NewBarDetect()==true)
     {
      MqlRates rates[];
      CopyRates(Symbol(),NULL,1,1,rates);
      MqlRates prevBar=rates[0];
      //--- Set new pendings order ---//
      double closeRate=GetCloseRate(prevBar);
      if(closeRate<=30 && BarIsExtremum(1,N,TYPE_EXTREMUM_HIGHEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BEARISH);
        }
      else if(closeRate>=70 && BarIsExtremum(1,N,TYPE_EXTREMUM_LOWEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BULLISH);
        }
      DeleteOldPendingOrders(OldPending);
     }
//---
  }
//+------------------------------------------------------------------+
//| Analyze open positions and modify it if needed.                  |
//+------------------------------------------------------------------+
int SupportPositions()
  {
//---
   int count=0;
   //--- Analize active positions... ---//
   for(int i=0; i<TransactionsTotal(); i++) // Get total positions.
     {
      //--- Select main active positions ---//
      if(!TransactionSelect(i, SELECT_BY_POS, MODE_TRADES))continue;             // Select active transactions
      if(TransactionType() != TRANS_HEDGE_POSITION)continue;                     // Select hedge positions only
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)                 // Select main positions by magic
      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN) // If position is frozen - continue
         continue;                                                               // Let's try to get access to positions later
      count++;
      //--- What position do we choose?... ---//
      ENUM_ENTRY_TYPE type=IdentifySelectPosition();
      bool modify=false;
      double sl = 0.0;
      double tp = 0.0;
      switch(type)
        {
         case ENTRY_BUY1:
         case ENTRY_SELL1:
           {
            //--- Check sl, tp levels and modify it if need. ---//
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            sl=GetStopLossLevel();
            if(!DoubleEquals(sl,currentStop))
               modify=true;
            tp=GetTakeProfitLevel();
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            //--- Close by take-profit if price more tp level
            bool isBuyTp=tp<bid && !DoubleEquals(tp,0.0) && type==ENTRY_BUY1;
            bool isSellTp=tp>ask && type==ENTRY_SELL1;
            if(isBuyTp || isSellTp)
              {
               HedgeTradeRequest request;
               request.action=REQUEST_CLOSE_POSITION;
               request.exit_comment="Close by TP from expert";
               request.close_type=CLOSE_AS_TAKE_PROFIT;
               if(!SendTradeRequest(request))
                 {
                  ENUM_HEDGE_ERR error=GetHedgeError();
                  string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
                  printf("Close position by tp failed. Reason: "+EnumToString(error)+" "+logs);
                  if(error==HEDGE_ERR_TASK_FAILED)
                     PrintTaskLog();
                  ResetHedgeError();
                 }
               else break;
              }
            double currentTakeProfit=HedgePositionGetDouble(HEDGE_POSITION_TP);
            if(!DoubleEquals(tp,currentTakeProfit))
               modify=true;
            break;
           }
         case ENTRY_BUY2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            if(sl>currentStop)
               modify=true;
            break;
           }
         case ENTRY_SELL2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            bool usingSL=HedgePositionGetInteger(HEDGE_POSITION_USING_SL);
            if(sl<currentStop || !usingSL)
               modify=true;
            break;
           }
        }
      //--- if  need modify sl, tp levels - modify it. ---//
      if(modify)
        {
         HedgeTradeRequest request;
         request.action=REQUEST_MODIFY_SLTP;
         request.sl = sl;
         request.tp = tp;
         if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
            request.exit_comment="Exit by T/P level";
         else
            request.exit_comment="Exit by trailing S/L";
         if(!SendTradeRequest(request))
           {
            ENUM_HEDGE_ERR error=GetHedgeError();
            string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
            printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
            if(error==HEDGE_ERR_TASK_FAILED)
               PrintTaskLog();
            ResetHedgeError();
           }
         else break;
        }
     }
   return count;
//---
  }
//+------------------------------------------------------------------+
//| Return stop-loss level for selected position.                    |
//| RESULT                                                           |
//|   Stop-loss level                                                |
//+------------------------------------------------------------------+
double GetStopLossLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   double fractals[];
   double sl=0.0;
   MqlRates ReversalBar;

   if(!LoadReversalBar(ReversalBar))
     {
      printf("Reversal bar load failed.");
      return sl;
     }
   //--- What position do we choose?... ---//
   switch(IdentifySelectPosition())
     {
      case ENTRY_SELL2:
        {
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,UPPER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]<sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_ASK);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]>price+freeze)
                     sl=NormalizeDouble(fractals[i]+point,Digits());
                 }
              }
            break;
           }
        }
      case ENTRY_SELL1:
         sl=ReversalBar.high+point;
         break;
      case ENTRY_BUY2:
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,LOWER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]>sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_BID);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]<price-freeze)
                     sl=NormalizeDouble(fractals[i]-point,Digits());
                 }
              }
            break;
           }
      case ENTRY_BUY1:
         sl=ReversalBar.low-point;
     }
   sl=NormalizeDouble(sl,Digits());
   return sl;
//---
  }
//+------------------------------------------------------------------+
//| Return Take-Profit level for selected position.                  |
//| RESULT                                                           |
//|   Take-profit level                                              |
//+------------------------------------------------------------------+
double GetTakeProfitLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   ENUM_ENTRY_TYPE type=IdentifySelectPosition();
   double tp=0.0;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
     {
      if(!HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
         return tp;
      double sl=HedgePositionGetDouble(HEDGE_POSITION_SL);
      double openPrice=HedgePositionGetDouble(HEDGE_POSITION_PRICE_OPEN);
      double deltaStopLoss=MathAbs(NormalizeDouble(openPrice-sl,Digits()));
      if(type==ENTRY_BUY1)
         tp=openPrice+deltaStopLoss;
      if(type==ENTRY_SELL1)
         tp=openPrice-deltaStopLoss;
      return tp;
     }
   else
      return 0.0;
//---
  }
//+------------------------------------------------------------------+
//| Identify what position type is select.                           |
//| RESULT                                                           |
//|   Return type position. See ENUM_ENTRY_TYPE                      |
//+------------------------------------------------------------------+
ENUM_ENTRY_TYPE IdentifySelectPosition()
  {
//---   
   string comment=HedgePositionGetString(HEDGE_POSITION_ENTRY_COMMENT);
   int pos=StringLen(comment)-2;
   string subStr=StringSubstr(comment,pos);
   ENUM_TRANS_DIRECTION posDir=(ENUM_TRANS_DIRECTION)HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
   if(subStr=="#0")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY1;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL1;
     }
   else if(subStr=="#1")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY2;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL2;
     }
   return ENTRY_BAD_COMMENT;
//---
  }
//+------------------------------------------------------------------+
//| Set pending orders under or over bar by index_bar.               |
//| INPUT PARAMETERS                                                 |
//|   index_bar - index of bar.                                      |
//|   barType - type of bar. See enum ENUM_BAR_TYPE.                 |
//| RESULT                                                           |
//|   True if new order successfully set, othewise false.            | 
//+------------------------------------------------------------------+
bool SetNewPendingOrder(int index_bar,ENUM_BAR_TYPE barType)
  {
//---
   MqlRates rates[1];
   CopyRates(Symbol(),NULL,index_bar,1,rates);
   MqlTradeRequest request={0};
   request.volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   double vol=request.volume;
   request.symbol = Symbol();
   request.action = TRADE_ACTION_PENDING;
   request.type_filling=ORDER_FILLING_FOK;
   request.type_time=ORDER_TIME_GTC;
   request.magic=Magic;
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   string comment="";
   if(barType==BAR_TYPE_BEARISH)
     {
      request.price=rates[0].low-point;
      comment="Entry sell by bearish bar";
      request.type=ORDER_TYPE_SELL_STOP;
     }
   else if(barType==BAR_TYPE_BULLISH)
     {
      request.price=rates[0].high+point;
      comment="Entry buy by bullish bar";
      request.type=ORDER_TYPE_BUY_STOP;
     }
   MqlTradeResult result={0};
//--- Send pending order twice...
   for(int i=0; i<2; i++)
     {
      request.comment=comment+" #"+(string)i;       // Detect order by comment;
      if(!OrderSend(request,result))
        {
         printf("Trade error #"+(string)result.retcode+" "+
                result.comment);
         return false;
        }
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Delete old pending orders. If pending order set older that       |
//| n_bars ago pending orders will be removed.                       |
//| INPUT PARAMETERS                                                 |
//|   period - count bar.                                            |
//+------------------------------------------------------------------+
void DeleteOldPendingOrders(int n_bars)
  {
//---
   for(int i=0; i<OrdersTotal(); i++)
     {
      ulong ticket = OrderGetTicket(i);            // Get ticket of order by index.
      if(!OrderSelect(ticket))                     // Continue if not selected.
         continue;
      if(Magic!=OrderGetInteger(ORDER_MAGIC))      // Continue if magic is not main.
         continue;
      if(OrderGetString(ORDER_SYMBOL)!=Symbol())   // Continue if symbol is not main.
         continue;
      //--- Count time elipsed ---//
      datetime timeSetup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
      int secElapsed=(int)(TimeCurrent()-timeSetup);
      //--- delete old pending order ---//
      if(secElapsed>=PeriodSeconds() *n_bars)
        {
         MqlTradeRequest request={0};
         MqlTradeResult result={0};
         request.action= TRADE_ACTION_REMOVE;
         request.order = ticket;
         if(!OrderSend(request,result))
            printf("Delete pending order failed. Reason #"+(string)result.retcode+" "+result.comment);
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Detect new bar.                                                  |
//+------------------------------------------------------------------+
bool NewBarDetect(void)
  {
//---
   datetime timeArray[1];
   CopyTime(Symbol(),NULL,0,1,timeArray);
   if(lastTime!=timeArray[0])
     {
      lastTime=timeArray[0];
      return true;
     }
   return false;
//---
  }
//+------------------------------------------------------------------+
//| Get close rate. Type bar defined in trade chaos strategy         |
//| and equal enum 'ENUM_TYPE_BAR'.                                  |
//| INPUT PARAMETERS                                                 |
//|   index - index of bars series. for example:                     |
//|   '0' - is current bar. 1 - previous bar.                        |
//| RESULT                                                           |
//|   Type of ENUM_TYPE_BAR.                                         | 
//+------------------------------------------------------------------+
double GetCloseRate(const MqlRates &bar)
  {
//---
   double highLowDelta = bar.high-bar.low;      // Calculate diaposon bar.
   double lowCloseDelta = bar.close - bar.low;  // Calculate Close - Low delta.
   double percentClose=0.0;
   if(!DoubleEquals(lowCloseDelta, 0.0))                    // Division by zero protected.   
      percentClose = lowCloseDelta/highLowDelta*100.0;      // Calculate percent 'lowCloseDelta' of 'highLowDelta'.
   return percentClose;
//---
  }
//+------------------------------------------------------------------+
//| If bar by index is extremum - return true, otherwise             |
//| return false.                                                    |
//| INPUT PARAMETERS                                                 |
//|   index - index of bar.                                          |
//|   period - Number of bars prior to the extremum.                 |
//|   type - Type of extremum. See ENUM_TYPE_EXTREMUM TYPE enum.     |
//| RESULT                                                           |
//|   True - if bar is extremum, otherwise false.                    | 
//+------------------------------------------------------------------+
bool BarIsExtremum(const int index,const int period,ENUM_TYPE_EXTREMUM type)
  {
//--- Copy rates --- //
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,index,N+1,rates);
//--- Search extremum --- //
   for(int i=1; i<ArraySize(rates); i++)
     {
      //--- Reset comment if you want include volume analize. ---//
      //if(rates[0].tick_volume<rates[i].tick_volume)
      //   return false;
      if(type==TYPE_EXTREMUM_HIGHEST && 
         rates[0].high<rates[i].high)
         return false;
      if(type==TYPE_EXTREMUM_LOWEST && 
         rates[0].low>rates[i].low)
         return false;
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }
//+------------------------------------------------------------------+
//| Load reversal bar. The current position must be selected.        |
//| OUTPUT PARAMETERS                                                |
//|   bar - MqlRates bar.
//+------------------------------------------------------------------+  
bool LoadReversalBar(MqlRates &bar)
  {
//---
   datetime time=(datetime)(HedgePositionGetInteger(HEDGE_POSITION_ENTRY_TIME_SETUP_MSC)/1000+1);
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,time,2,rates);
   int size=ArraySize(rates);
   if(size==0)return false;
   bar=rates[size-1];
   return true;
//---   
  }
//+------------------------------------------------------------------+
//| Compares two double numbers.                                     |
//| RESULT                                                           |
//|   True if two double numbers equal, otherwise false.             |
//+------------------------------------------------------------------+
bool DoubleEquals(const double a,const double b)
  {
//---
   return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)));
//---
  }

Di seguito è riportata una breve descrizione di come funziona questo codice. L'EA viene chiamato su ogni tick. Esso analizza la barra precedente utilizzando la funzione BarIsExtremum(): se è ribassista o rialzista, inserisce due ordini in sospeso (funzione SetNewPendingOrder()). Una volta attivati, gli ordini in sospeso vengono convertiti in posizioni. L'EA imposta quindi stop loss e take profit per le posizioni.

Sfortunatamente, questi livelli non possono essere inseriti insieme agli ordini in sospeso, perché non esiste ancora una posizione reale. I livelli vengono impostati tramite la funzione SupportPositions(). Per operare correttamente, abbiamo bisogno di conoscere la posizione nella quale dovrebbe essere collocato il take profit e la posizione che dovrebbe essere seguita seguendo i frattali. Questo piazzamento delle posizioni viene eseguito dalla funzione IdentifySelectPosition(). Essa analizza il commento della posizione di inizio, e se contiene la stringa "#1", viene impostato un target stretto; se contiene "# 2", viene applicato il trailing stop.

Per modificare una posizione bidirezionale aperta, o per chiuderla, viene creata una richiesta di scambio speciale, che viene poi inviata alla funzione SendTradeRequest() per l'esecuzione:

...
if(modify)
  {
   HedgeTradeRequest request;
   request.action=REQUEST_MODIFY_SLTP;
   request.sl = sl;
   request.tp = tp;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
      request.exit_comment="Exit by T/P level";
   else
      request.exit_comment="Exit by trailing S/L";
   if(!SendTradeRequest(request))
     {
      ENUM_HEDGE_ERR error=GetHedgeError();
      string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
      printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
      if(error==HEDGE_ERR_TASK_FAILED)
         PrintTaskLog();
      ResetHedgeError();
     }
   else break;
  }
...

Fai attenzione alla gestione degli errori.

Se l'invio fallisce e la funzione restituisce false, è necessario ottenere l'ultimo codice di errore utilizzando la funzione GetHedgeError(). In alcuni casi l'esecuzione di un ordine di trading non viene nemmeno avviata. Se la posizione non è stata preselezionata, la query viene eseguita in modo errato e la sua esecuzione risulta impossibile.

Se un ordine non viene eseguito, è inutile analizzare il log della sua implementazione, è sufficiente ottenere un codice di errore.

Tuttavia, se la query è corretta, ma l'ordine non è stato eseguito per qualche motivo, verrà restituito l'errore HEDGE_ERR_TASK_FAILED. In questo caso è necessario analizzare il log di esecuzione dell'ordine effettuando una ricerca nel log. Questo viene fatto tramite la funzione speciale PrintTaskLog():

//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }

Questi messaggi consentono di identificare il motivo dell'errore e di risolverlo.

Illustriamo ora la visualizzazione del EA Chaos2 e le sue posizioni in HedgeTerminal in tempo reale. L'EA è in esecuzione sul grafico M1:

Fig. 5. La rappresentazione delle posizioni bidirezionali del EA Chaos 2 nel pannello HedgeTerminal

Fig. 5. La rappresentazione delle posizioni bidirezionali del EA Chaos 2 nel pannello HedgeTerminal

Come si vede, anche le posizioni bidirezionali di un EA possono coesistere perfettamente.


1.13. "Simboli duplicati" e virtualizzazione da parte del broker

Quando è stato uscito MetaTrader 5, alcuni broker hanno iniziato a fornire i cosiddetti simboli duplicati. Le loro quotazioni sono uguali agli strumenti originali, ma di regola hanno un suffisso, come "_m" o "_1". Sono stati introdotti per consentire ai trader di avere posizioni bidirezionali praticamente sullo stesso simbolo.

Tuttavia, tali simboli sono quasi inutili per i trader algoritmici che utilizzano i robot. Ecco perché: Supponiamo di dover scrivere l'EA "Chaos II" senza la libreria HedgeTerminalAPI. Al suo posto, avremmo alcuni simboli duplicati. Come possiamo fare? Supponiamo che tutte le operazioni di vendita siano state aperte su un singolo strumento, come EURUSD, e tutte le operazioni di acquisto su un altro, ad esempio EURUSD_m1.

Ma cosa accadrebbe se, al momento dell'apertura della posizione, uno dei simboli fosse già scambiato da un altro robot o dal trader? Anche se tali simboli fossero sempre liberi, il problema non sarebbe risolto per questo robot, che potrebbe avere contemporaneamente più posizioni nella stessa direzione.

Lo screenshot qui sopra mostra tre posizioni di vendita e ce ne possono essere anche di più. Le posizioni hanno diversi livelli di stop protettivo, motivo per cui non possono essere combinate in un'unica posizione netta. La soluzione è aprire una nuova posizione per un nuovo simbolo duplicato. Ma questi simboli possono non essere sufficienti, perché un robot ha bisogno di sei strumenti duplicati (tre in ogni direzione di trading). Se due robot funzionano in tempi diversi, sono necessari 12 simboli.

Nessuno dei broker fornisce così tanti simboli duplicati. Ma anche se ci fosse una quantità illimitata di tali simboli, e fossero sempre gratuiti, sarebbe necessaria una complessa scomposizione dell'algoritmo. Il robot dovrebbe passare attraverso tutti i simboli disponibili alla ricerca di duplicati e delle proprie posizioni. Questo creerebbe più problemi di quanti ne potrebbe risolvere. 

Ci sono ancora più difficoltà con i simboli duplicati. Ecco un breve elenco di ulteriori problemi derivanti dal loro utilizzo:

  • Paghi per ogni simbolo duplicato sotto forma di swap negativi, perché gli swap per il blocco o per il blocco parziale sono sempre negativi. Questo è il caso quando mantieni due posizioni bidirezionali su due strumenti diversi.
  • Non tutti i broker forniscono simboli duplicati. Una strategia sviluppata per un broker che fornisce simboli duplicati non funzionerà con un broker che fornisce un solo strumento. La differenza nei nomi dei simboli è un'altra potenziale fonte di problemi.
  • La creazione di un simbolo duplicato non è sempre possibile. Su mercati trasparenti soggetti a rigide normative, qualsiasi transazione è un documento finanziario. La posizione netta è lo standard de facto in tali mercati e quindi la creazione di simboli individuali non è possibile. Ad esempio, nessun broker che fornisce simboli duplicati potrà mai apparire su Moscow Exchange MOEX. In mercati regolamentati meno rigidi i broker possono creare qualsiasi simbolo per i loro clienti.
  • Gli strumenti duplicati sono inefficaci quando si fa trading con i robot. Le ragioni della loro inefficacia sono state rivelate nell'esempio precedente di EA Chaos 2.

Un simbolo duplicato è essenzialmente una virtualizzazione da parte del broker. HedgeTerminal utilizza la virtualizzazione da parte del client.

In entrambi i casi utilizziamo la virtualizzazione in quanto tale. Cambia la rappresentazione effettiva degli obblighi del trader. Con la virtualizzazione, una posizione può trasformarsi in due posizioni. Non c'è nessun problema quando si verifica sul lato client, perché i client possono rappresentare ciò che vogliono. Ma se la virtualizzazione viene eseguita dal broker, le organizzazioni di regolamentazione e di licenza potrebbero avere domande su come le informazioni fornite si riferiscano alle informazioni effettive. La seconda difficoltà è che ciò richiede di avere due API in una: un insieme di funzioni e modificatori per l'uso in modalità rete e un altro per la modalità bidirezionale.

Molti trader algoritmici hanno trovato il proprio modo per unire le negoziazioni all’interno di un'unica posizione. Molti di questi metodi funzionano bene e ci sono articoli che descrivono questi metodi. Tuttavia, la virtualizzazione delle posizioni è una procedura più complicata di quanto possa sembrare. In HedgeTerminal, gli algoritmi associati alla virtualizzazione delle posizioni occupano circa 20.000 righe del codice sorgente. Inoltre, HedgeTerminal implementa solo funzioni di base. Creare una quantità simile di codice nel tuo EA solo per accompagnare le posizioni bidirezionali richiederebbe troppe risorse.


Capitolo 2. Manuale API HedgeTerminal

2.1. Funzioni di selezione delle transazioni

Funzione TransactionsTotal()

La funzione restituisce il numero totale di transazioni nell'elenco delle transazioni. Questa è la funzione di base per la ricerca tra le transazioni disponibili (vedi l'esempio nella sezione 1.4 e 1.11 di questo articolo).

int TransactionsTotal(ENUM_MODE_TRADES pool = MODE_TRADES);

Parametri

  • [in] pool=MODE_TRADES – Specifica l'identificatore dell'origine dati per la selezione. Può essere uno dei valori dell'enumerazione ENUM_MODE_TRADES.

Return Value

La funzione restituisce il numero totale di transazioni nell'elenco delle transazioni.


Funzione TransactionType()

La funzione restituisce il tipo di transazione selezionata.

ENUM_TRANS_TYPE TransactionType(void);

Return Value

Tipo di ritorno. Il valore può essere uno dei valori ENUM_TRANS_TYPE.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione TransactionSelect()

Questa funzione seleziona una transazione per poter effettuare ulteriori manipolazioni. La funzione seleziona una transazione in base al suo indice o identificatore univoco nell'elenco delle transazioni.

bool TransactionSelect(int index,
     ENUM_MODE_SELECT select = SELECT_BY_POS,
     ENUM_MODE_TRADES pool=MODE_TRADES
     );

Parametri

  • [in] index – L'indice dell'ordine nell'elenco degli ordini o un identificatore univoco della transazione a seconda del parametro 'select'.
  • [in] select=SELECT_BY_POS – Identificatore del tipo 'indice' del parametro. Il valore può essere uno dei valori ENUM_MODE_SELECT.
  • [in] pool=MODE_TRADES – Specifica l'identificatore dell'origine dati per la selezione. Può essere uno dei valori dell'enumerazione ENUM_MODE_TRADES.

Return Value

Restituisce vero se una transazione è stata selezionata con successo o falso in caso contrario. Per ottenere i dettagli dell'errore, chiama GetHedgeError().

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".

Nota

Se una transazione viene selezionata in base al suo indice, la complessità dell'operazione corrisponde a O(1). Se una transazione viene selezionata in base al suo identificatore univoco, la complessità dell'operazione tende asintoticamente a O(log2(n)).


Funzione HedgeOrderSelect()

La funzione seleziona uno degli ordini inclusi nella posizione bidirezionale. La posizione bidirezionale, che include l'ordine richiesto, deve essere preselezionata utilizzando TransactionSelect ().

bool HedgeOrderSelect(ENUM_HEDGE_ORDER_SELECTED_TYPE type);

Parametri

Return Value

Restituisce vero se un ordine è stato selezionato con successo o falso in caso contrario. Per ottenere i dettagli dell'errore, chiama GetHedgeError().

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione HedgeDealSelect()

La funzione seleziona uno dei deal che hanno eseguito l'ordine. L'ordine la cui parte è l'operazione selezionata deve essere preselezionato utilizzando la funzione HedgeOrderSelect().

bool HedgeDealSelect(int index);

Parametri

  • [in] iindex – L'indice dell'operazione da selezionare dall'elenco delle operazioni che hanno eseguito l'ordine. Per scoprire il numero totale di operazioni all'interno di un ordine, chiama la proprietà dell'ordine appropriata utilizzando la funzione HedgeOrderGetInteger(). Per il parametro, utilizzare il modificatore ENUM_HEDGE_ORDER_PROP_INTEGER uguale al valore HEDGE_ORDER_DEALS_TOTAL.

Return Value

Restituisce vero se un deal è stato selezionato con successo o falso in caso contrario. Per ottenere i dettagli dell'errore, chiama GetHedgeError().

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


2.2. Funzioni per ottenere le proprietà di una transazione selezionata

Funzione HedgePositionGetInteger()

La funzione restituisce la proprietà di una posizione bidirezionale selezionata. La proprietà può essere di tipo int, long, datetime o bool a seconda del tipo di proprietà richiesta. La posizione bidirezionale deve essere preselezionata utilizzando la funzione TransactionSelect().

ulong HedgePositionGetInteger(ENUM_HEDGE_POSITION_PROP_INTEGER property);

Parametri

  • [in] property – Identificatore della proprietà della posizione bidirezionale. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_DEAL_PROP_INTEGER.

Return Value

Valore del tipo ulong. Per un ulteriore utilizzo del valore, è necessario eseguire il cast del suo tipo in modo esplicito sul tipo della proprietà richiesta.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione HedgePositionGetDouble()

La funzione restituisce la proprietà di una posizione bidirezionale selezionata. Il tipo della proprietà di ritorno è double. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_POSITION_PROP_DOUBLE. La posizione bidirezionale deve essere preselezionata utilizzando TransactionSelect().

ulong HedgePositionGetDouble(ENUM_HEDGE_POSITION_PROP_DOUBLE property);

Parametri

  • [in] property – Identificatore della proprietà della posizione bidirezionale. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_DEAL_PROP_DOUBLE.

Return Value

Un valore di tipo double.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione HedgePositionGetString()

La funzione restituisce la proprietà di una posizione bidirezionale selezionata. La proprietà è di tipo string. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_POSITION_PROP_STRING. La posizione bidirezionale deve essere preselezionata utilizzando TransactionSelect().

ulong HedgePositionGetString(ENUM_HEDGE_POSITION_PROP_STRING property);

Parametri

  • [in] property – Identificatore della proprietà della posizione bidirezionale. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_POSITION_PROP_STRING.

Return Value

Un valore di tipo string.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione HedgeOrderGetInteger()

La funzione restituisce la proprietà dell'ordine selezionato, che fa parte della posizione bidirezionale. La proprietà può essere di tipo int, long, datetime o bool. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_ORDER_PROP_INTEGER. L'ordine deve essere preselezionato utilizzando la funzione HedgeOrderSelect().

ulong HedgeOrderGetInteger(ENUM_HEDGE_ORDER_PROP_INTEGER property);

Parametri

  • [in] property – Identificatore della proprietà dell'ordine, che fa parte della posizione bidirezionale. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_ORDER_PROP_INTEGER.

Return Value

Valore del tipo ulong. Per un ulteriore uso del valore, il suo tipo deve essere esplicitamente castato al tipo della proprietà richiesta.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Function HedgeOrderGetDouble()

La funzione restituisce la proprietà dell'ordine selezionato, che fa parte della posizione bidirezionale. La proprietà richiesta è di tipo double. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_ORDER_PROP_DOUBLE. L'ordine deve essere preselezionato utilizzando la funzione HedgeOrderSelect().

double HedgeOrderGetDouble(ENUM_HEDGE_ORDER_PROP_DOUBLE property);

Parametri

  • [in] property – Identificatore della proprietà dell'ordine, che fa parte della posizione bidirezionale. Il valore può essere uno qualsiasi dei valori di enumerazione ENUM_HEDGE_ORDER_PROP_DOUBLE.

Return Value

Un valore di tipo double.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione HedgeDealGetInteger()

La funzione restituisce la proprietà dell'operazione selezionata, che fa parte dell'ordine eseguito. La proprietà può essere di tipo int, long, datetime o bool. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_DEAL_PROP_INTEGER. L'operazione deve essere preselezionata utilizzando la funzione HedgeDealSelect().

ulong HedgeOrderGetInteger(ENUM_HEDGE_DEAL_PROP_INTEGER property);

Parametri

  • [in] property – Identificatore della proprietà dell'operazione selezionata inclusa nell'ordine eseguito. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_DEAL_PROP_INTEGER.

Return Value

Valore del tipo ulong. Per un ulteriore utilizzo del valore, il suo tipo deve essere esplicitamente castato al tipo della proprietà richiesta.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Function HedgeDealGetDouble()

La funzione restituisce la proprietà dell'operazione selezionata, che fa parte dell'ordine eseguito. La proprietà può essere di tipo double. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_DEAL_PROP_DOUBLE. L'operazione deve essere preselezionata utilizzando la funzione HedgeDealSelect().

ulong HedgeOrderGetDouble(ENUM_HEDGE_DEAL_PROP_DOUBLE property);

Parametri

  • [in] property – Identificatore della proprietà dell'operazione selezionata inclusa nell'ordine eseguito. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_DEAL_PROP_DOUBLE.

Return Value

Un valore di tipo double.

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


2.3. Funzioni per impostare e ottenere le proprietà di HedgeTerminal da Expert Advisor

Funzione HedgePropertySetInteger()

La funzione imposta una delle proprietà HedgeTerminal. La proprietà può essere di tipo int, long, datetime o bool. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_PROP_INTEGER.

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, long value);

Parametri

  • [in] property: identificatore della proprietà che deve essere impostata per HedgeTerminal. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_PROP_INTEGER.

Return Value

Un valore di tipo bool. Se la proprietà è stata impostata correttamente, la funzione restituisce true, altrimenti restituisce false.

Esempio di utilizzo

Nell'esempio, la funzione viene utilizzata per impostare il tempo di blocco della posizione durante l'invio di una richiesta asincrona. Se la risposta del server non viene ricevuta entro 30 secondi dall'invio di una richiesta asincrona, la posizione bloccata verrà sbloccata.

void SetTimeOut()
  {
   bool res=HedgePropertySetInteger(HEDGE_PROP_TIMEOUT,30);
   if(res)
      printf("The property is set successfully");
   else
      printf("Property is not set");
  }

Function HedgePropertyGetInteger()

La funzione ottiene una delle proprietà HedgeTerminal. La proprietà può essere di tipo int, long, datetime o bool. Il tipo di proprietà viene specificato tramite l'enumerazione ENUM_HEDGE_PROP_INTEGER.

long HedgePropertyGetInteger(ENUM_HEDGE_PROP_INTEGER property);

Parametri

  • [in] property – Identificatore della proprietà che dovrebbe essere ricevuta da HedgeTerminal. Il valore può essere uno dei valori di enumerazione ENUM_HEDGE_PROP_INTEGER.

Return Value

[in] Valore di tipo long.

Esempio di utilizzo

La funzione riceve il tempo di blocco della posizione durante l'invio di una richiesta asincrona e lo mostra nel terminale.

void GetTimeOut()
  {
   int seconds=HedgePropertyGetInteger(HEDGE_PROP_TIMEOUT);
   printf("Timeout is "+(string) seconds);
  }

2.4. Funzioni per ottenere e gestire i codici di errore

Funzione GetHedgeError()

La funzione restituisce l'identificatore dell'errore, ottenuto dall'ultima azione. L'identificatore di errore corrisponde all'enumerazione ENUM_HEDGE_ERR.

ENUM_HEDGE_ERR GetHedgeError(void);

Return Value

Position ID. Il valore può essere qualsiasi tipo di enumerazione ENUM_HEDGE_ERR.

Nota

Dopo la chiamata, la funzione GetHedgeError() non reimposta l'ID di errore. Per reimpostare l'ID errore utilizzare la funzione ResetHedgeError().

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione ResetHedgeError()

La funzione reimposta l'identificatore dell'ultimo errore ricevuto. Dopo la sua chiamata, l'identificatore ENUM_HEDGE_ERR restituito da GetHedgeError() sarà uguale a HEDGE_ERR_NOT_ERROR.

void ResetHedgeError(void);

Esempio di utilizzo

Vedi l'esempio di utilizzo della funzione nella sezione 1.11 di questo articolo: "Gestione delle proprietà di posizione bidirezionale spiegata attraverso l'esempio di uno script".


Funzione TotalActionsTask()

Una volta selezionata la posizione utilizzando la funzione HedgePositionSelect(), essa può essere modificata utilizzando la funzione SendTradeRequest(). Ad esempio, può essere chiusa o il suo commento in uscita può essere modificato. Questa modifica viene eseguita da un'attività di trading speciale. Ogni attività può consistere in diverse attività di trading (sottoattività). Un'attività potrebbe incorrere in un errore. In questo caso potrebbe essere necessario analizzare il risultato di tutte le sottoattività incluse nell'attività per vedere quale tipo di sottoattività non è riuscito.

La funzione TotalActionTask() restituisce il numero di sottoattività contenute nell'ultima attività di trading eseguita per la posizione selezionata. Conoscendo il numero totale di attività secondarie, è possibile cercare in tutte le attività secondarie in base al loro indice e analizzare i risultati dell'esecuzione utilizzando la funzione GetActionResult() e quindi scoprire le circostanze dell'errore.

uint TotalActionsTask(void);

Return Value

Restituisce il numero totale di sottoattività all'interno dell'attività.

Esempio di utilizzo

Vedi l'esempio di utilizzo nella sezione 1.6 di questo articolo: "Analisi dettagliata del trading e identificazione degli errori utilizzando TotalActionsTask() e GetActionResult()".


Funzione GetActionResult()

La funzione prende l'indice della sottoattività all'interno dell'attività (vedi TotalActionTask()). Restituisce il tipo della sottoattività e i risultati dell'esecuzione tramite parametri di riferimento. Il tipo della sottoattività è definito dall'enumerazione ENUM_TARGET_TYPE. Il risultato dell'esecuzione della sottoattività corrisponde ai codici di ritorno del server di trading MetaTrader 5.

void GetActionResult(uint index, ENUM_TARGET_TYPE& target_type, uint& retcode);

Parametri

  • [in] index – L'indice della sottoattività nell'elenco delle sottoattività.
  • [out] target_type – Tipo di sottoattività. Il valore può essere uno dei valori di enumerazione ENUM_TARGET_TYPE.
  • [out] retcode – Codice di ritorno del server di trading ricevuto durante l'esecuzione dell'attività secondaria.

Esempio di utilizzo

Vedi l'esempio di utilizzo nella sezione 1.6 di questo articolo: "Analisi dettagliata del trading e identificazione degli errori utilizzando TotalActionsTask() e GetActionResult()".


2.5. Trading

Funzione SendTradeRequest()

La funzione invia una richiesta per modificare la posizione bidirezionale selezionata in HedgeTerminalAPI. Il risultato dell'esecuzione della funzione è una delle tre azioni:

  1. Chiusura di una posizione o di una parte del suo volume;
  2. Modifica dei livelli di Stop Loss e Take Profit;
  3. Modifica del commento in uscita.

Il tipo di azione e i relativi parametri sono specificati nella struttura HedgeTradeRequest, che viene passata per riferimento come parametro. Prima della chiamata alla funzione, la posizione bidirezionale deve essere preselezionata utilizzando la funzione TransactionSelect().

bool SendTradeRequest(HedgeTradeRequest& request);

Parametri

[in] request – La struttura della richiesta di modifica della posizione bidirezionale. Si prega di vedere la descrizione della struttura e la spiegazione dei suoi campi nella descrizione della struttura HedgeTradeRequest.

Return Value

Restituisce vero, se la richiesta di modifica della posizione è stata eseguita correttamente. Restituisce false in caso contrario. In caso di errore di esecuzione della richiesta, utilizzare le funzioni TotalActionsTask() e GetActionResult() per trovare l'errore e le sue ragioni.

Nota

Nella modalità asincrona di invio della richiesta, il flag di ritorno contiene true se un'attività è stata inserita e avviata con successo. Tuttavia, dobbiamo ricordare che anche in caso di avvio positivo di un'attività, la sua esecuzione non può essere garantita. Pertanto questo flag non può essere utilizzato per controllare il completamento dell'attività in modalità asincrona. Nella modalità sincrona, un'attività viene avviata ed eseguita in un singolo thread, quindi nella modalità sincrona è possibile controllare il risultato dell'esecuzione della richiesta di negoziazione utilizzando questo flag.


Struttura della richiesta di trading HedgeTradeRequest()

Le posizioni bidirezionali in HedgeTerminal vengono chiuse e modificate tramite una chiamata della funzione SendTradeRequest(), in cui la richiesta di scambio viene utilizzata come argomento. La richiesta è rappresentata da una apposita struttura predefinita HedgeTradeRequest, che contiene tutti i campi necessari per chiudere o modificare la posizione selezionata:

struct HedgeTradeRequest
  {
   ENUM_REQUEST_TYPE action;             // type of action
   double            volume;             // volume of position
   ENUM_CLOSE_TYPE   close_type;         // Marker of closing order
   double            sl;                 // stop-loss level
   double            tp;                 // take-profit level
   string            exit_comment;       // outgoing comment
   uint              retcode;            // last retcode in executed operation
   bool              asynch_mode;        // true if the closure is performed asynchronously, otherwise false
   ulong             deviation;          // deviation in step price
                     HedgeTradeRequest() // default params
     {
      action=REQUEST_CLOSE_POSITION;
      asynch_mode=false;
      volume=0.0;
      sl = 0.0;
      tp = 0.0;
      retcode=0;
      deviation=3;
     }
  };

Descrizione Campi

CampoDescrizione
 azione Il tipo dell'azione richiesta con la posizione. Il valore può essere uno qualsiasi dei valori di enumerazione ENUM_REQUEST_TYPE
 volume Il volume da chiudere. Può essere inferiore al volume della posizione attualmente attiva. Se il volume è zero, la posizione attiva verrà chiusa completamente.
 sl Il livello di stop loss da posizionare per la posizione attiva.
 tp Il livello di take profit da posizionare per la posizione attiva.
 exit_comment  Il commento in uscita per la posizione attiva.
 retcode Codice risultato dell'ultima operazione eseguita.
 asynch_mode True se viene utilizzata la modalità asincrona per l'invio delle richieste, false in caso contrario.
 deviazione Scostamento massimo dal prezzo dell'usato.


2.6. Enumerazioni per lavorare con le funzioni di selezione delle transazioni

ENUM_TRANS_TYPE

Tutte le transazioni disponibili per l'analisi, inclusi gli ordini pendenti e le posizioni bidirezionali, sono nell'elenco delle transazioni attive e storiche.

L'enumerazione ENUM_TRANS_TYPE contiene il tipo di ciascuna transazione selezionata. Questa enumerazione viene restituita dalla funzione TransactionType(). Di seguito sono riportati i campi di enumerazione e le relative descrizioni:

CampoDescrizione
 TRANS_NOT_DEFINED La transazione non è selezionata dalla funzione TransactionSelect() o il suo tipo non è definito.
 TRANS_HEDGE_POSITION La transazione è una posizione bidirezionale.
 TRANS_BROKERAGE_DEAL  La transazione è un deal di un broker (operazione sul conto). Ad esempio, l'aggiunta di denaro sul conto o la correzione.
 TRANS_PENDING_ORDER La transazione è un ordine in sospeso.
 TRANS_SWAP_POS La transazione è uno swap addebitato per una posizione netta.


ENUM_MODE_SELECT

L'enumerazione definisce il tipo del parametro di indice impostato nella funzione TransactionSelect().

CampoDescrizione
 SELECT_BY_POS Il parametro index viene utilizzato per passare l'indice della transazione nell'elenco.
 SELECT_BY_TICKET Il numero del ticket viene passato nel parametro index.


ENUM_MODE_TRADES

L'enum definisce l'origine dati, da cui viene selezionata una transazione utilizzando TransactionSelect().

CampoDescrizione
 MODE_TRADES La transazione viene selezionata dalle transazioni attive.
 MODE_HISTORY La transazione è selezionata dalle transazioni storiche.

2.7. Enumerazioni per lavorare con le funzioni che ottengono le proprietà delle transazioni

Enumerazione ENUM_TRANS_DIRECTION

Ogni transazione, indipendentemente dal fatto che si tratti di un accordo o di una posizione bidirezionale, ha una direzione di mercato.

Questa direzione di mercato è definita dall'enumerazione ENUM_TRANS_DIRECTION. Di seguito sono riportati i suoi campi e le loro descrizioni:

CampoDescrizione
 TRANS_NDEF La direzione di una transazione non è definita. Ad esempio, le transazioni del broker sul conto non hanno una direzione di mercato e sono dotate di questo modificatore.
 TRANS_LONG Indica che la transazione (ordine o posizione bidirezionale) è una transazione di acquisto.
 TRANS_SHORT  Indica che la transazione (ordine o posizione bidirezionale) è una transazione di vendita.


Enumerazione ENUM_HEDGE_POSITION_STATUS

L'enumerazione contiene lo stato di una posizione bidirezionale.

CampoDescrizione
 HEDGE_POSITION_ACTIVE  Una posizione attiva Le posizioni attive vengono visualizzate nella scheda Attivo del pannello HedgeTerminal.
 HEDGE_POSITION_HISTORY  Una posizione storica. Le posizioni storiche vengono visualizzate nella scheda Cronologia del pannello HedgeTerminal.


Enumerazione ENUM_HEDGE_POSITION_STATE

L'enumerazione contiene lo stato di una posizione bidirezionale.

CampoDescrizione
 POSITION_STATE_ACTIVE La posizione selezionata è attiva e può essere modificata utilizzando HedgeTradeRequest.
 POSITION_STATE_FROZEN  La posizione selezionata è bloccata e non può essere modificata. Se viene ricevuto questo modificatore, si dovrebbe attendere che la posizione venga sbloccata.


Enumerazione ENUM_HEDGE_POSITION_PROP_INTEGER

L'enumerazione imposta il tipo della proprietà restituita da HedgePositionGetInteger().

CampoDescrizione
 HEDGE_POSITION_ENTRY_TIME_SETUP_MSC Il tempo in millisecondi dal 01.01.1970, quando è stato effettuato l'ordine di avvio della posizione bidirezionale.
 HEDGE_POSITION_ENTRY_TIME_EXECUTED_MSC  Il tempo in millisecondi dal 01.01.1970, quando è stato eseguito l'ordine di avvio della posizione bidirezionale (tempo di apertura della posizione).
 HEDGE_POSITION_EXIT_TIME_SETUP_MSC Il tempo in millisecondi dal 01.01.1970, quando è stato effettuato l'ordine per chiudere la posizione bidirezionale.
 HEDGE_POSITION_EXIT_TIME_EXECUTED_MSC Il tempo in millisecondi dal 01.01.1970, quando è stato eseguito l'ordine di chiusura della posizione bidirezionale (tempo di chiusura della posizione).
 HEDGE_POSITION_TYPE Il tipo di posizione bidirezionale. Uguale al tipo dell'ordine di inizio. Contiene uno dei valori dell'enumerazione di sistema ENUM_ORDER_TYPE.
 HEDGE_POSITION_DIRECTION Direzione di posizione. Definito dall'enumerazione ENUM_TRANS_DIRECTION.
 HEDGE_POSITION_MAGIC Il numero magico dell'Expert Advisor a cui appartiene la posizione selezionata. Un valore pari a zero indica che la posizione è stata aperta manualmente.
 HEDGE_POSITION_CLOSE_TYPE Il marker dell'ordine che chiude la posizione. Definito da ENUM_CLOSE_TYPE.
 HEDGE_POSITION_ID Position ID. Uguale all'identificatore dell'ordine di inizio.
 HEDGE_POSITION_ENTRY_ORDER_ID L'identificatore dell'ordine di inizio.
 HEDGE_POSITION_EXIT_ORDER_ID L'identificatore di un ordine di chiusura per una posizione storica.
 HEDGE_POSITION_STATUS Stato della posizione. Definito da ENUM_HEDGE_POSITION_STATUS.
 HEDGE_POSITION_STATE Stato di posizione. Definito da ENUM_HEDGE_POSITION_STATE
 HEDGE_POSITION_USING_SL La bandiera dell'uso di stop loss. Se viene utilizzato uno stop loss, la funzione HedgePositionGetInteger() restituisce true, altrimenti false.
 HEDGE_POSITION_USING_TP Il flag di un livello di take profit usato. Se viene utilizzato un take profit, HedgePositionGetInteger() restituisce true, altrimenti restituisce false.
 HEDGE_POSITION_TASK_STATUS Lo stato dell'attività che viene eseguita per la posizione selezionata. La posizione può essere in fase di modifica. Questo modificatore viene utilizzato per tenere traccia delle modifiche in questa posizione. Lo stato della posizione è definito da ENUM_TASK_STATUS.
 HEDGE_POSITION_ACTIONS_TOTAL Restituisce il numero totale di sottoattività avviate per modificare questa posizione.

 

Enumerazione ENUM_HEDGE_POSITION_PROP_DOUBLE

L'enumerazione imposta il tipo della proprietà restituita dalla funzione HedgePositionGetDouble().

CampoDescrizione
 HEDGE_POSITION_VOLUME Il volume della posizione bidirezionale.
 HEDGE_POSITION_PRICE_OPEN Il prezzo medio ponderato di apertura di una posizione.
 HEDGE_POSITION_PRICE_CLOSED Il prezzo medio ponderato di chiusura di una posizione.
 HEDGE_POSITION_PRICE_CURRENT Il prezzo corrente di una posizione attiva. Per una posizione storica, questo modificatore restituisce il prezzo di chiusura della posizione.
 HEDGE_POSITION_SL Il livello di stop loss. Zero se non viene utilizzato lo stop loss.
 HEDGE_POSITION_TP Il livello di take profit. Zero se non viene utilizzato il take profit.
 HEDGE_POSITION_COMMISSION L'importo della commissione pagata per la posizione.
 HEDGE_POSITION_SLIPPAGE Slippage in punti.
 HEDGE_POSITION_PROFIT_CURRENCY  Utile o perdita della posizione. Il valore è specificato nella valuta del deposito.
 HEDGE_POSITION_PROFIT_POINTS Utile o perdita della posizione. Il valore è specificato nei punti del simbolo finanziario della posizione.

Nota

Lo slippage HEDGE_POSITION_SLIPPAGE è calcolato come la differenza in punti tra il miglior deal di ingresso della posizione e il prezzo medio ponderato di ingresso.

 

Enumerazione ENUM_HEDGE_POSITION_PROP_STRING

L'enumerazione imposta il tipo della proprietà restituita dalla funzione HedgePositionGetString().

CampoDescrizione
 HEDGE_POSITION_SYMBOL Il simbolo della posizione corrente.
 HEDGE_POSITION_ENTRY_COMMENT Il commento in arrivo di una posizione.
 HEDGE_POSITION_EXIT_COMMENT Il commento in uscita di una posizione.

 

Enumerazione ENUM_HEDGE_ORDER_STATUS

L'enumerazione contiene il tipo di ordine.

CampoDescrizione
 HEDGE_ORDER_PENDING  L'ordine è in sospeso ed è disponibile nella scheda Trade di MetaTrader 5.
 HEDGE_ORDER_HISTORY L'ordine è storico ed è disponibile nella cronologia degli ordini in MetaTrader 5.

Enumerazione ENUM_HEDGE_ORDER_SELECTED_TYPE

L'enumerazione definisce il tipo dell'ordine selezionato dalla funzione HedgeOrderSelect().

CampoValore
 ORDER_SELECTED_INIT L'ordine avvia una posizione bidirezionale.
 ORDER_SELECTED_CLOSED  L'ordine chiude una posizione bidirezionale.
 ORDER_SELECTED_SL L'ordine funge da livello di stop loss.


Enumerazione ENUM_HEDGE_ORDER_PROP_INTEGER

L'enumerazione imposta il tipo della proprietà restituita dalla funzione HedgePositionGetInteger().

CampoDescrizione
 HEDGE_ORDER_ID Un identificatore d'ordine univoco.
 HEDGE_ORDER_STATUS Stato dell'ordine Il valore può essere uno dei valori dell'enumerazione ENUM_HEDGE_ORDER_STATUS.
 HEDGE_ORDER_DEALS_TOTAL Il numero totale di deal che hanno completato l'ordine. Il valore è zero per gli ordini in sospeso.
 HEDGE_ORDER_TIME_SETUP_MSC Tempo di immissione dell'ordine in sospeso in millisecondi dal 01.01.1970.
 HEDGE_ORDER_TIME_EXECUTED_MSC Il tempo di esecuzione di un ordine eseguito in millisecondi dal 01.01.1970.
 HEDGE_ORDER_TIME_CANCELED_MSC Il tempo di cancellazione di un ordine eseguito in millisecondi dal 01.01.1970.

Nota

L'ora di esecuzione dell'ordine HEDGE_ORDER_TIME_EXECUTED_MSC è uguale all'ora dell'ultima transazione. 


Enumerazione ENUM_HEDGE_ORDER_PROP_DOUBLE

L'enumerazione impone il tipo della proprietà restituita dalla funzione HedgeOrderGetDouble().

CampoDescrizione
 HEDGE_ORDER_VOLUME_SETUP Il volume dell'ordine specificato nell'ordine.
 HEDGE_ORDER_VOLUME_EXECUTED Volume eseguito dell'ordine. Se un ordine è in sospeso, il volume eseguito è zero.
 HEDGE_ORDER_VOLUME_REJECTED Il volume dell'ordine che non è stato possibile eseguire. Pari alla differenza tra il volume iniziale e il volume eseguito.
 HEDGE_ORDER_PRICE_SETUP Prezzo di inserimento dell'ordine.
 HEDGE_ORDER_PRICE_EXECUTED Il prezzo di esecuzione medio ponderato di un ordine.
 HEDGE_ORDER_COMMISSION L'importo della commissione pagata al broker per l'esecuzione dell'ordine. Specificato nella valuta di deposito.
 HEDGE_ORDER_SLIPPAGE Slippage dell'ordine.

Nota

Lo slippage HEDGE_ORDER_SLIPPAGE è calcolato come differenza in punti tra il miglior deal eseguito e il prezzo medio ponderato di entrata dell'ordine.


Enumerazione ENUM_HEDGE_DEAL_PROP_INTEGER

L'enumerazione imposta il tipo della proprietà restituita da HedgeDealGetInteger().

CampoDescrizione
 HEDGE_DEAL_ID Un identificatore di offerta univoco.
 HEDGE_DEAL_TIME_EXECUTED_MSC Tempo di esecuzione dell'operazione in millisecondi dal 01.01.1970


Enumerazione ENUM_HEDGE_DEAL_PROP_DOUBLE

L'enumerazione imposta il tipo della proprietà restituita da HedgeDealGetDouble().

CampoDescrizione
 HEDGE_DEAL_VOLUME_EXECUTED Volume di un affare.
 HEDGE_DEAL_PRICE_EXECUTED Modalità di esecuzione affare
 HEDGE_DEAL_COMMISSION L'importo della commissione pagata al broker per l'esecuzione dell'operazione. Specificato nella valuta di deposito.

 

2.8. Enumerazioni per impostare e ottenere le proprietà di HedgeTerminal

Enumerazione ENUM_HEDGE_PROP_INTEGER

L'enumerazione imposta il tipo di proprietà che si desidera ottenere o impostare in HedgeTerminal.

CampoDescrizione
 HEDGE_PROP_TIMEOUT Tempo, in secondi, durante il quale HedgeTerminal attende una risposta dal server prima di sbloccare una posizione in fase di modifica.


2.9. Enumerazioni per lavorare con le funzioni di gestione dei codici di errore

Enumerazione ENUM_TASK_STATUS

Ogni posizione bidirezionale può essere in fase di modifica. Una posizione viene modificata tramite un'attività di trading.

Ogni attività di trading in esecuzione ha il suo stato di esecuzione definito in ENUM_TASK_STATUS. Di seguito sono riportati i suoi campi e le loro descrizioni:

CampoDescrizione
 TASK_STATUS_WAITING Nessuna attività corrente o l'attività è in attesa.
 TASK_STATUS_EXECUTING L'attività di trading è attualmente in esecuzione.
 TASK_STATUS_COMPLETE L'attività di trading per la posizione è stata completata con successo.
 TASK_STATUS_FAILED L'attività di trading per la posizione ha riscontrato un errore.


Enumerazione ENUM_HEDGE_ERR

L'enumerazione contiene l'ID dell'errore che può essere restituito da GetHedgeError().

CampoDescrizione
 HEDGE_ERR_NOT_ERROR Nessun errore.
 HEDGE_ERR_TASK_FAILED L'attività per la posizione selezionata non è riuscita.
 HEDGE_ERR_TRANS_NOTFIND Transazione non trovata.
 HEDGE_ERR_WRONG_INDEX Indice errato.
 HEDGE_ERR_WRONG_VOLUME Volume non corretto.
 HEDGE_ERR_TRANS_NOTSELECTED  La transazione non è stata preselezionata utilizzando TransactionSelect().
 HEDGE_ERR_WRONG_PARAMETER Uno dei parametri non è corretto.
 HEDGE_ERR_POS_FROZEN La posizione bidirezionale è attualmente in fase di modifica e non è possibile modificarla ulteriormente. Attendi che la posizione venga rilasciata.
 HEDGE_ERR_POS_NO_CHANGES La richiesta di scambio non ha modifiche.

 

Enumerazione ENUM_TARGET_TYPE

L'enumerazione definisce il tipo dell'attività selezionata dalla funzione GetActionResult().

CampoDescrizione
 TARGET_NDEF La sottoattività non è definita.
 TARGET_CREATE_TASK La sottoattività è in fase di creazione. Questo tipo è utilizzato nelle logiche interne di HedgeTerminalAPI.
 TARGET_DELETE_PENDING_ORDER Eliminazione di un ordine in sospeso.
 TARGET_SET_PENDING_ORDER Inserimento di un ordine in sospeso.
 TARGET_MODIFY_PENDING_ORDER  Modifica del prezzo dell'ordine in sospeso.
 TARGET_TRADE_BY_MARKET Svolgere operazioni di Trading


2.10. Enumerazioni per lavorare con le funzioni di gestione dei codici di errore

Enumerazione ENUM_REQUEST_TYPE

L'enumerazione descrive l'azione di HedgeTerminal applicata alla posizione bidirezionale.

CampoDescrizione
 REQUEST_CLOSE_POSITION Chiude la posizione. Se il campo volume della struttura HedgeTradeRequest contiene un volume inferiore a quello corrente, verrà chiusa solo una parte della posizione. In questo caso, la parte della posizione chiusa corrisponde al valore del campo volume.
 REQUEST_MODIFY_SLTP Imposta o modifica i livelli esistenti di stop loss e take profit.
 REQUEST_MODIFY_COMMENT Modifica il commento in uscita di una posizione attiva.


Enumerazione ENUM_CLOSE_TYPE

L'enumerazione definisce un indicatore speciale per l'ordine di chiusura della posizione bidirezionale. Il marker indica il motivo della chiusura della posizione. Può essere uno dei seguenti motivi:

  • La posizione ha raggiunto il livello massimo di perdita o stop loss;
  • La posizione ha raggiunto un certo livello di profitto o take profit;
  • Posizione chiusa dal mercato. I livelli di stop loss e take profit non sono stati piazzati o raggiunti.
CampoDescrizione
 CLOSE_AS_MARKET Indica che la posizione è chiusa dal mercato. I livelli di stop loss e take profit non sono stati piazzati o raggiunti.
 CLOSE_AS_STOP_LOSS Indica che la posizione è chiusa per raggiungimento del livello di stop loss.
 CLOSE_AS_TAKE_PROFIT  Indica che la posizione è chiusa a causa del raggiungimento del livello di take profit.

 

Capitolo 3. I fondamenti del trading asincrono

L'argomento delle operazioni asincrone è complesso e richiede un articolo dettagliato separato. Tuttavia, poiché HedgeTerminal utilizza attivamente operazioni asincrone, è opportuno descrivere brevemente i principi di organizzazione degli Expert Advisor che usano questo tipo di invio di richieste. Inoltre, praticamente non esiste altro materiale sull'argomento.

3.1. Organizzazione e schema di invio di un ordine di trading sincrono

MetaTrader 5 fornisce due funzioni per inviare richieste di trading al server:

La funzione OrderSend() accetta una richiesta come struttura MqlTradeRequest compilata ed esegue la verifica di base della correttezza della struttura. Se la verifica di base ha esito positivo, invia la richiesta a un server, attende il suo risultato e quindi restituisce il risultato al thread personalizzato tramite la struttura MqlTradeResult e il flag di ritorno. Se la verifica di base fallisce, la funzione restituisce un valore negativo.

Il motivo per cui non è stato possibile verificare la richiesta è incluso anche in MqlTradeResult.

Lo schema seguente presenta l'esecuzione del thread di un programma MQL5 personalizzato con la funzione OrderSend():

Fig. 6. Schema di organizzazione e invio di una richiesta di trading sincrona

Fig. 6. Schema di organizzazione e invio di una richiesta di trading sincrona

Come si vede dallo schema, il thread del programma MQL5 non può essere separato dal thread di sistema comune che invia una richiesta al server ed esegue operazioni di trading in borsa.

Ecco perché, dopo il completamento di OrderSend(), possiamo analizzare il risultato effettivo della richiesta di trading. Il thread personalizzato è contrassegnato da frecce rosse. Viene eseguito quasi istantaneamente. La maggior parte del tempo viene impiegata per eseguire operazioni di trading in borsa. Poiché i due thread sono connessi, tra l'inizio e la fine della funzione OrderSend() passa molto tempo. A causa del fatto che le operazioni di trading vengono eseguite in un singolo thread, la logica dei programmi MQL5 può essere sequenziale.


3.2. Organizzazione e schema di invio di un ordine di negoziazione asincrono

La funzione OrderSendAsync() è diversa. Come OrderSend(), essa accetta la richiesta commerciale MqlTradeRequest e restituisce un flag che ne indica il risultato.

Tuttavia, a differenza del primo esempio, non attende che la richiesta di scambio venga eseguita dal server, ma restituisce i valori ​ottenuti solo dal modulo di verifica di base dei valori di richiesta di scambio (Verifica di base all'interno del terminale). Lo schema seguente mostra la procedura di esecuzione del thread personalizzato quando si utilizza la funzione OrderSendAsync():

Fig. 7. Lo schema di organizzazione e invio di una richiesta di trading asincrona.

Fig. 7. Fig. 7. Lo schema di organizzazione e invio di una richiesta di trading asincrona.

Una volta che una richiesta di trading è stata verificata con successo, viene inviata al server di trading parallelamente al thread principale. Il passaggio di una richiesta di trading sulla rete e la sua esecuzione sullo scambio richiedono del tempo, come nel primo caso. Ma il thread personalizzato otterrà quasi un risultato istantaneo dalla funzione OrderSendAsync().

Lo schema sopra mostra che OrderSendAsync() forma effettivamente un nuovo thread parallelo che viene eseguito da un server di trading, e il suo risultato di esecuzione entra nella funzione OnTradeTransaction() o OnTrade(). Queste funzioni iniziano un nuovo thread personalizzato. Il risultato dell'invio di una richiesta di scambio dovrebbe essere elaborato in questo nuovo thread. Questo complica notevolmente la logica dell'Expert Advisor, perché con l'invio asincrono dell'ordine, è impossibile organizzare l'invio della richiesta e il suo controllo in un unico thread. Ad esempio, non è possibile inserire in sequenza il codice per l'invio e il controllo di una richiesta in OnTick().

Scriviamo un semplice test EA per illustrare quanto sopra:

//+------------------------------------------------------------------+
//|                                                    AsynchExp.mq5 |
//|                           Copyright 2014, Vasiliy Sokolov (C-4). |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
input bool UsingAsynchMode=true;
bool sendFlag=false;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(sendFlag)return;
   printf("Formation of order and send to the server...");
   MqlTradeRequest request={0};
   request.magic=12345;
   request.symbol = Symbol();
   request.volume = 0.1;
   request.type=ORDER_TYPE_BUY;
   request.comment= "asynch test";
   request.action = TRADE_ACTION_DEAL;
   request.type_filling=ORDER_FILLING_FOK;
   MqlTradeResult result;
   uint tiks= GetTickCount();
   bool res = false;
   if(UsingAsynchMode)
      res=OrderSendAsync(request,result);
   else
      res=OrderSend(request,result);
   uint delta=GetTickCount()-tiks;
   if(OrderSendAsync(request,result))
     {
      printf("The order has been successfully"+
             "sent to the server.");
     }
  else
     {
     printf("The order is not shipped."+
             " Reason: "+(string)result.retcode);
     }
   printf("Time to send a trade request: "+(string)delta);
   sendFlag=true;
//---
  }

Assicuriamoci che l'EA funzioni avviandolo con UsingAsynchMode = false.

L'EA apre una posizione lunga di 0,1 lotti. La richiesta di trading viene eseguita in modo sincrono utilizzando la funzione OrderSend(). Ecco il suo registro di esempio:

2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   Time to send a trade request: 94
2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   The order has been successfullysent to the server.
2014.11.06 17:49:28.345 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

La richiesta di scambio è stata completata entro 94 millisecondi. Questa volta ci dice che la richiesta ha superato la verifica di base, è stata inviata al server e quindi è stata soddisfatta.

Ora modifichiamo il codice EA modificando il volume della transazione al valore massimo possibile DBL_MAX:

request.volume = DBL_MAX;

Ovviamente, questo valore è al di fuori dell'intervallo effettivo. Proviamo ad eseguire questa richiesta in modalità sincrona:

2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10014
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

L'invio di una richiesta non è riuscito. Il motivo dell'errore è l’errore 10014 (volume richiesto non valido). La richiesta è fallita durante la verifica di base e non è stata nemmeno inviata al server, come risulta dal tempo di esecuzione della richiesta di 0 millisecondi.

Ancora una volta, cambiamo la richiesta. Questa volta abbiamo specificato un volume abbastanza grande, ma non un valore estremo: 15 lotti. Per il conto di $ 1.000 in cui viene testato l'EA, è troppo. Tale posizione non può essere aperta su questo conto.

Vediamo cosa restituisce OrderSend():

2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   Time to send a trade request: 78
2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10019
2014.11.06 17:59:22.550 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

L'errore questa volta è diverso: 10019 (Fondi insufficienti per l'esecuzione della richiesta, il che è vero). Si noti che il tempo di esecuzione della richiesta è ora di 79 milliseconds. Indica che la richiesta è stata inviata al server e che il server ha restituito un errore.

Inviamo ora la stessa richiesta con il volume da 15 lotti utilizzando la funzione OrderSendAsync(). Come con OrderSend(), non viene aperta alcuna posizione. Ma analizziamo il log:

2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:03:58.104 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Il registro dice che non ci sono errori! Poiché l'errore 10019 viene rilevato dal server di trading, esso non è disponibile per il thread corrente nella modalità di invio asincrono dell'ordine. Il valore restituito indica solo che la richiesta ha superato la verifica di base. Per ottenere l'errore effettivo 10019, dobbiamo analizzare i risultati in un nuovo thread personalizzato, nella funzione di sistema OnTradeTransaction(), che dovrebbe essere aggiunto al nostro EA:

void  OnTradeTransaction(const MqlTradeTransaction    &trans,
                         const MqlTradeRequest        &request,
                         const MqlTradeResult         &result)
  {
   uint delta = GetTickCount() - tiks;
   printf("Server answer: " + (string)result.retcode + "; Time: " + (string)delta);
  }

Eseguiamo di nuovo l'EA e vediamo i log:

2014.11.06 18:17:00.943 AsynchExp (AUDCAD,H1)   Server answer: 10019; Time: 94
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:17:00.851 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

L'errore 10019 è stato ricevuto, ma non immediatamente dopo l'invio. È stato ricevuto nel nuovo thread personalizzato in esecuzione in OnTradeTransaction().


3.3. Velocità di esecuzione asincrona dell'ordine

I trader credono erroneamente che la velocità di esecuzione di una richiesta asincrona sia vicina allo zero.

Deriva dall'osservazione di OrderSendAsync(), che di norma viene completata in meno di un millisecondo. In realtà, come mostrato sopra, il tempo effettivo di esecuzione della transazione dovrebbe essere misurato quando una risposta dal server viene ricevuta all'interno delle funzioni OnTradeTransaction() o OnTrade(). Questa misura mostra la velocità reale, che è uguale alla velocità di un'esecuzione sincrona per un singolo ordine. I reali vantaggi in termini di tempo di esecuzione sono percepibili quando si invia un gruppo di transazioni. Esistono almeno tre situazioni in cui è necessario inviare più richieste:

  • Il tempo necessario tra due richieste successive è talmente ridotto che non è possibile verificare l'esito della richiesta prima di inviare la successiva. Quando viene inviata la richiesta successiva, si spera che la precedente sia stata eseguita. Tattiche simili sono utilizzate nel trading ad alta frequenza;
  • Devi aprire più posizioni per più simboli alla volta. Ad esempio, le strategie di arbitraggio e le posizioni sintetiche composite richiedono l'apertura simultanea di posizioni per vari strumenti a prezzi correnti. La graduale formazione di posizioni è indesiderabile in tali tattiche;
  • È necessario completare il thread il prima possibile e attendere ulteriori eventi e comandi dell'utente. Questo requisito è importante per le soluzioni multi-thread e infrastrutturali. Questo è il motivo principale per cui HedgeTerminal utilizza richieste asincrone. Se HT utilizzasse l'invio sincrono delle richieste, si bloccherebbe costantemente per 1-2 secondi ogni volta che l'utente chiude o modifica la posizione, cosa inaccettabile.

Ricorda di tenere in considerazione il limite di invio delle richieste quando effettui più ordini.

In MetaTrader 5 build 1010 e versioni successive, il limite è di 64 transazioni, di cui 4 riservate agli utenti e altre a disposizione degli Expert Advisor. Il limite ha lo scopo di proteggere i trader alle prime armi da gravi errori nei loro programmi, oltre a ridurre il carico di spam su un server di trading.

Ciò significa che allo stesso tempo, ad esempio nel ciclo for, puoi inviare fino a 60 ordini chiamando SendOrderAsync() con una richiesta di scambio appropriata. Dopo che tutte le 60 transazioni sono state inviate, il buffer delle transazioni sarà pieno. Dobbiamo attendere la conferma dal server che una delle transazioni è stata elaborata.

Dopo essere stato gestito, il posto di una transazione nel buffer delle transazioni viene rilasciato e una nuova richiesta di scambio può prenderlo. Una volta che il buffer è pieno, lo spazio per le nuove transazioni viene rilasciato lentamente, perché un server ha bisogno di tempo per elaborare ogni transazione, e perché l'evento TradeTransaction() che notifica l'inizio dell'elaborazione viene passato sulla rete, causando ulteriori ritardi.

Pertanto, il tempo necessario per inviare le richieste aumenterà in modo non lineare rispetto alla crescita del numero di richieste. Nella tabella seguente sono riportate le tariffe stimate di invio degli ordini in modalità asincrona. I test sono stati eseguiti più volte e il tasso mostrato è il valore medio:

Numero di richiestemillisecondi.
5050
100180
2002100
5009000
100023000

Nel caso in cui il numero di richieste sia inferiore a 60, lo script non attende la risposta del server, ecco perché il tempo è così ridotto. È approssimativamente uguale al tempo necessario per inviare una singola richiesta. Infatti, per ottenere un tempo di esecuzione reale approssimativo, aggiungere il tempo medio di esecuzione della richiesta al tempo di immissione della richiesta specificato nella tabella.

 

Capitolo 4. I fondamenti della programmazione multi-thread nell'IDE MetaTrader 5

I programmatori MQL5 sanno che i thread non possono essere controllati direttamente dai programmi MQL. Questa restrizione è per il bene dei programmatori alle prime armi, perché l'uso dei thread complica enormemente gli algoritmi del programma. Tuttavia, in alcune situazioni, due o più EA devono comunicare tra loro, ad esempio devono creare e leggere dati globali.

HedgeTerminal è uno di questi EA. Per informare ogni EA che utilizza la libreria HedgeTerminalAPI sulle azioni di altri Expert Advisor, l'HT organizza lo scambio di dati attraverso la lettura e la scrittura multi-thread del file ActivePositions.xml. Questa soluzione non è banale ed è usata raramente dai programmatori MQL. Pertanto, creeremo un EA multi-thread con l'algoritmo simile a HedgeTerminal. Ciò aiuterà a comprendere meglio la programmazione multi-thread e quindi a capire meglio come funziona HedgeTerminal.


4.1. Programmazione multithread spiegata con l'esempio di Quote Collector UnitedExchangeQuotes

Impareremo le basi della programmazione multi-thread con l’aiuto di un esempio specifico: scriveremo un raccoglitore di quotazioni da diversi provider (broker).

L'idea è questa: supponiamo di avere 6-7 broker che forniscono quotazioni per lo stesso strumento. Naturalmente, le quotazioni di diversi broker possono variare leggermente. L'analisi di queste differenze apre la strada a strategie di arbitraggio. Inoltre, il confronto delle dinamiche delle quotazioni aiuterà a identificare il fornitore migliore e quello peggiore. Ad esempio, se un broker offre prezzi migliori, preferiamo selezionare questo broker. Non stiamo cercando il valore pratico dei risultati, ma descriviamo solo il meccanismo attraverso il quale questi risultati possono essere raggiunti.

Ecco uno screenshot dell'EA che dovremo scrivere entro la fine di questo capitolo:

Fig. 8. L'aspetto del raccoglitore di quotazioni UnitedExhangesQuotes.

Fig. 8. L'aspetto del raccoglitore di quotazioni UnitedExhangesQuotes.

L'Expert Advisor mostra i risultati in una semplice tabella composta da quattro colonne e un numero illimitato di righe.

Ogni riga rappresenta un broker che fornisce quotazioni di simboli (in questo caso, EURUSD). Ask e Bid sono la migliore offerta e domanda del broker. Lo screenshot mostra che i prezzi differiscono leggermente. Nella colonna D-ASK (Delta Ask) compare la differenza tra l'offerta del broker attuale e quella di un altro. Allo stesso modo, la differenza tra i valori della domanda viene visualizzata in D-BID (Delta Bid). Ad esempio, al momento dello screenshot, la migliore Ask era fornita da "Alpari Limited" e la più costosa era quella di "Bank VTB 24".

I programmi MQL non possono accedere all'ambiente di altri terminali MetaTrader. In altre parole, se un programma è in esecuzione su un terminale, non può ricevere dati da un altro. Tuttavia, tutti i programmi MQL possono comunicare attraverso i file nella directory condivisa dei terminali MetaTrader. Se un programma scrive informazioni, ad esempio la citazione corrente, in un file, il programma MQL da un altro terminale può leggerle. MQL non ha altri mezzi senza DLL esterne. Pertanto, utilizzeremo questo metodo.

La difficoltà maggiore è organizzare tale accesso. Da un lato, l'EA deve leggere le quotazioni di altri fornitori e, dall'altro, scrivere la quotazione del suo fornitore nello stesso file. Un altro problema è il fatto che al momento della lettura delle quotazioni, un altro EA può scrivere una nuova quotazione su questo file. Il risultato di tale lavoro parallelo è imprevedibile. Nel migliore dei casi, questo sarà seguito da un arresto anomalo e dall'interruzione del programma, e nel peggiore dei casi porterà alla comparsa occasionale di strani errori quasi impercettibili associati alla visualizzazione delle quotazioni.

Per eliminare questi errori, o almeno ridurre al minimo la probabilità che si verifichino, svilupperemo un piano chiaro.

In primo luogo, tutte le informazioni verranno archiviate nel formato XML. Questo formato ha sostituito il file ini. XML consente la distribuzione flessibile dei suoi nodi in strutture dati complesse, come le classi. Successivamente, determiniamo l'algoritmo generale di lettura e scrittura. Ci sono due operazioni di base: leggere i dati e scrivere i dati. Quando un programma MQL sta leggendo o scrivendo, nessun altro programma può accedere a questo file. Quindi eliminiamo ogni situazione in cui un programma legge i dati e il secondo li modifica. Per questo motivo, l'accesso ai dati non sarà sempre possibile.

Creiamo una classe speciale CQuoteList che conterrà gli algoritmi di accesso XML, nonché i dati di tutte le virgolette da questo file.

Una delle funzioni di questa classe è TryGetHandle(), prova ad accedere al file e restituisce il suo handle in caso di successo. Ecco l'implementazione della funzione:

int CQuoteList::TryGetHandle(void)
{
   int attempts = 10;
   int handle = INVALID_HANDLE;
   // We try to open 'attemps' times
   for(att = 0; att < attempts; att++)
   {
      handle = FileOpen("Quotes.xml", FILE_WRITE|FILE_READ|FILE_BIN|FILE_COMMON);
      if(handle == INVALID_HANDLE)
      {
         Sleep(15);
         continue;
      }
      break;
   }
   return handle;
}

Fa diversi tentativi per aprire il file in una modalità combinata di lettura/scrittura. Il numero predefinito di tentativi è dieci.

Se un tentativo non va a buon fine, la funzione si blocca per 15 millisecondi e riprova ad aprire il file, effettuando così fino a 10 tentativi.

Una volta aperto il file, il suo handle viene passato alla funzione LoadQuotes(). Un elenco completo di questa funzione e della classe CQuoteList sono disponibili in allegato all'articolo. Quindi qui descriviamo solo la sequenza delle azioni nella funzione:

  1. TryGetHandle() apre il file da leggere e scrivere;
  2. Il documento XML viene caricato nella memoria EA utilizzando la libreria XML Parser;
  3. Sulla base del documento XML caricato, viene formata una nuova matrice di citazioni che memorizza le informazioni richieste;
  4. L'array creato contiene una citazione appartenente all'EA corrente. I suoi valori ​sono aggiornati;
  5. L'array di virgolette viene riconvertito in un documento XML. Il contenuto del file XML aperto viene sostituito con questo documento XML;
  6. Il file XML delle quotazioni è chiuso.

La funzione LoadQuotes() fa un ottimo lavoro, ma nella maggior parte dei casi impiega meno di 1 millisecondo.

La lettura dei dati e l'aggiornamento dei dati con il loro ulteriore salvataggio sono combinati in un blocco. Questo viene fatto apposta, in modo da non perdere il controllo dell'accesso al file tra le operazioni di lettura e scrittura.

Una volta che le quotazioni sono state caricate e si trovano all'interno di una classe, è possibile accedervi come qualsiasi altro dato in MetaTrader 5. Questo viene fatto attraverso una speciale interfaccia di programma in stile MetaTrader 5 implementata nella classe CQuotesList.

La chiamata alla funzione e il rendering dei dati vengono eseguiti all'interno del blocco OnTick(). Ecco i contenuti:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
      return;
   if(!QuotesList.LoadQuotes())    
      return;   
   PrintQuote quote = {0};
   Panel.DrawAccess(QuotesList.CountAccess());
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   string brokerName = AccountInfoString(ACCOUNT_COMPANY);
   for(int i = 0; i < QuotesList.BrokersTotal(); i++)
   {
      if(!QuotesList.BrokerSelect(i))
         continue;
      if(!QuotesList.SymbolSelect(Symbol()))
         continue;
      quote.ask = QuotesList.QuoteInfoDouble(QUOTE_ASK);
      quote.bid = QuotesList.QuoteInfoDouble(QUOTE_BID);
      quote.delta_ask = ask - quote.ask;
      quote.delta_bid = quote.bid - bid;
      quote.broker_name = QuotesList.BrokerName();
      quote.index = i;
      Panel.DrawBroker(quote);
   }
  }

È interessante notare che il codice di esempio funziona sia in MetaTrader 4 che in MetaTrader 5 senza ulteriori modifiche!

Ci sono solo piccole differenze estetiche nel modo in cui i pannelli vengono visualizzati nelle diverse versioni del terminale. Indubbiamente, questo è un fatto notevole che facilita il porting del codice tra piattaforme.

Il funzionamento dell'EA si osserva meglio in dinamica. Il video qui sotto mostra l'operazione EA su diversi account:

 

La lettura e la scrittura su un file hanno vantaggi significativi, ma ci sono anche alcuni svantaggi.

I principali vantaggi sono:

  1. Flessibilità. Puoi archiviare e caricare qualsiasi dato, anche intere classi;
  2. Velocità relativamente alta L'intero ciclo di lettura e riscrittura richiede quasi sempre meno di 1 millisecondo, il che è buono rispetto a operazioni di trading relativamente lente che richiedono 80-150 millisecondi o talvolta anche di più;
  3. Basato sugli strumenti standard del linguaggio MQL5 senza chiamare DLL.

Lo svantaggio principale di tale soluzione è un grave carico sul sistema di archiviazione. Quando c'è una quotazione e due broker, il numero di operazioni di riscrittura è relativamente piccolo, ma con un flusso pesante di quotazioni e un gran numero di broker/simboli, il numero di operazioni di riscrittura diventa molto grande. In meno di un'ora, la demo EA ha prodotto oltre 90.000 operazioni di riscrittura di file Quotes.xml. Queste statistiche sono mostrate nella parte superiore del pannello EA: "I/O Rewrite" mostra il numero totale di riscritture di file, "fps" indica la velocità tra le ultime due operazioni di riscrittura e "Avrg" mostra la velocità media di riscritture al secondo.

Se archivi file su SSD o HDD, queste operazioni avranno un impatto negativo sulla durata di vita del disco. Pertanto è meglio utilizzare un disco RAM virtuale per tale scambio di dati.

A differenza dell'esempio precedente, HedgeTerminal utilizza con parsimonia ActivePositions.xml, scrivendo solo cambiamenti di posizione significativi che sono inaccessibili attraverso il contesto globale. Quindi produce molte meno operazioni di lettura/scrittura rispetto all'esempio precedente e quindi non richiede condizioni speciali, come i dischi RAM.


4.2. Utilizzo dell'interazione multi-thread tra Expert Advisor

L'interazione in tempo reale tra programmi MQL indipendenti è un argomento complicato ma interessante. L'articolo ne contiene solo una breve descrizione, ma merita un articolo a parte. Nella maggior parte dei casi l'interazione multi-thread tra Expert Advisor non è richiesta. Tuttavia, ecco un elenco di attività e i programmi, per i quali è richiesta l'organizzazione di tale interazione:

  • Trade copier (Copiatrice commerciale). Qualsiasi copiatrice commerciale prevede il lancio simultaneo di almeno due EA: uno fornisce scambi, l'altro li copia. In questo caso, è necessario organizzare la lettura/scrittura multi-thread di un file di dati comune per fornire e copiare operazioni;
  • Organizzazione dello scambio di dati globali tra EA, variabili globali. Le variabili globali standard in MetaTrader 5 sono disponibili per gli Expert Advisor solo a livello di un terminale. Una variabile globale dichiarata in un terminale non è disponibile nell'altro. Tuttavia, attraverso l'utilizzo di dati comuni, è possibile organizzare variabili globali complesse che potrebbero essere disponibili per tutti i terminali anche di versioni diverse;
  • Strategie di arbitraggio. Analizzatori di quotazioni di diversi fornitori di liquidità. Se la differenza tra i prezzi forniti da diversi broker è significativa, i trader possono trarne vantaggio creando strategie di arbitraggio. Gli analizzatori consentono inoltre di raccogliere statistiche sui migliori prezzi e identificare oggettivamente il miglior fornitore di liquidità.


Descrizione degli allegati

Di seguito una breve descrizione dei file allegati all'articolo, nonché della procedura di compilazione.

Prototypes.mqh è un file con la descrizione delle funzioni della libreria HedgeTerminalAPI. Questo file contiene la descrizione ei prototipi delle funzioni della libreria HedgeTerminalAPI. Consente al tuo EA di sapere quali funzioni e modificatori sono disponibili nella libreria, come chiamare le funzioni e quali valori restituiscono.

Salva questo file in C:\Program Files\MetaTrader 5\MQL5\Include, dove "C:\Program Files\MetaTrader 5" è il nome della directory in cui è installato il tuo terminale MetaTrader 5. Una volta che il file è stato copiato nella directory corretta, puoi fare riferimento ad esso nel tuo programma MQL. Questa operazione dovrebbe essere eseguita ogni volta che è necessario utilizzare la libreria HedgeTerminalAPI. Per fare riferimento al file Prototypes.mqh, aggiungi il file speciale incule directory nel tuo codice:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //...
   // Here is the content of your program.
   //...
  }

Nell'esempio sopra, questa direttiva è contrassegnata in giallo e si chiama "#include <Ptototypes.mqh>". Ora lo script sopra può fare riferimento alle funzioni della libreria e utilizzare le loro funzionalità.

Si prega di notare che nel processo di sviluppo della libreria HedgeTerminalAPI, il file dei prototipi potrebbe subire piccole modifiche. Spesso, con l'aggiornamento della versione della libreria è necessario aggiornare il file prototipo, che descriverà le modifiche. Cerca di avere pazienza. In ogni caso, l'ultima versione del file prototipo può sempre essere installata manualmente dalla libreria (la procedura di installazione è descritta nella sezione 1.1) o scaricata dall'allegato a questo articolo (sono previsti aggiornamenti periodici per gli allegati).

Chaos2.mqh è un codice sorgente di Chaos2 EA. Il suo funzionamento è descritto nella sezione 1.12: "L'esempio della funzione SendTradeRequest e la funzione HedgeTradeRequest spiegate con l'esempio di Chaos II EA. Per compilare correttamente il codice, salvare il file dei prototipi di funzione nella directory corrispondente \Include e salvare la libreria HedgeTerminalAPI in: C:\Program Files\MetaTrader 5\MQL5\Market\hedgeterminalapi.ex5. Dove "C:\Program Files\MetaTrader 5\" è il nome della directory (cartella dei dati del terminale) in cui è installato il tuo terminale MetaTrader 5.

Il codice sorgente di UnitedExchangeQuotes è lo speciale archivio zip (unitedexchangequotes.zip) che contiene il progetto descritto in dettaglio nel capitolo 4: "I fondamenti della programmazione multi-thread nell'IDE MetaTrader 5". Questo zip contiene i seguenti file:

  • UnitedExchangeQuotes.mq5 - il file centrale dell'EA. Salvalo nella cartella degli Expert: \MetaTrader 5\MQL5\Experts. Compila questo file in MetaEditor.
  • MultiThreadXML.mqh è il file principale contenente algoritmi di accesso multi-thread al file XML. Esso organizza lo scambio di informazioni tra thread indipendenti. Salva in \MetaTrader 5\MQL5\Include. Gli algoritmi in questo file si basano sulla libreria speciale sviluppata da ya-sha, disponibile in CodeBase. Tuttavia, è stato leggermente modificato per il funzionamento multi-thread. L'allegato contiene questa versione modificata. Si compone delle seguenti schede:</t4>
    • XmlBase.mqh;
    • XmlDocument.mqh;
    • XmlAttribute.mqh;
    • XmlElement.mqh.
    Salva questi file nella cartella \Include.
  • Panel.mqh contiene la classe panel descritta nell'esempio. Salva questo file nella stessa directory in cui salvi UnitedEchangesQuotes.mqh, cioè nella cartella \Experts.

Tutti i file nell'archivio contengono percorsi relativi. Ad esempio, il file UnitedExchangeQuotes.mq5 si trova nella cartella \MQL5\Experts. Ciò significa che dovrebbe essere posizionato nella stessa sottodirectory della cartella dei dati del terminale MetaTrader 5, ad esempio C:\Program Files\MetaTrader 5\MQL5\Experts\UnitedExchangeQuotes.mq5.


Conclusione

Abbiamo considerato in dettaglio il modo di lavorare con l'interfaccia del programma HedgeTerminal.

È stato dimostrato che i principi di questa libreria sono molto simili all'API MetaTrader 4. Come in MetaTrader 4 API, prima di iniziare a lavorare con una transazione (simile al concetto di "ordine" in MetaTrader 4), dovresti prima selezionarla usando TransactionSelect(). Una transazione in Hedge Terminal è di norma una posizione bidirezionale. Una volta selezionata una posizione, è possibile ottenere le sue proprietà o applicarvi un'azione di trading, ad esempio impostare un livello di stop loss oppure chiuderla. Questa sequenza di azioni è quasi identica all'algoritmo per lavorare con gli ordini in MetaTrader 4.

Oltre alle informazioni di base sul numero di posizioni bidirezionali e sulle loro proprietà, HedgeTerminal fornisce l'accesso ai valori ​che non sono disponibili direttamente in MetaTrader 5 e richiedono calcoli analitici complessi. Ad esempio, puoi vedere la quantità di slippage di ogni posizione bidirezionale richiedendo solo una delle sue proprietà. Puoi controllare il numero di offerte all'interno di una posizione selezionata. Tutti questi calcoli e l'abbinamento richiesto delle offerte vengono eseguiti "dietro le quinte" durante l'avvio di HedgeTerminal. È conveniente perché l'Expert Advisor di trading non ha bisogno di calcolare nulla. Tutte le informazioni necessarie sono già state calcolate e sono disponibili tramite un'API semplice e intuitivo.

L'utilizzo degli algoritmi comuni da parte dell'API HedgeTerminal e del pannello consente la presentazione unificata dei dati. Pertanto, puoi controllare gli EA dal pannello HedgeTerminal, mentre le modifiche apportate dall'EA verranno visualizzate direttamente sul pannello.


Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/1316

File allegati |
Prototypes.mqh (15.3 KB)
Chaos2.mq5 (23.56 KB)
Studiare la classe CCanvas. Come disegnare oggetti trasparenti Studiare la classe CCanvas. Come disegnare oggetti trasparenti
La solida grafica scomoda delle medie mobili non ti va più bene? Vuoi disegnare qualcosa di più bello di un semplice rettangolo riempito di colore nel tuo terminale? Puoi disegnare una grafica più accattivante nel tuo terminale. Questo può essere implementato tramite la classe CСanvas, che viene utilizzata per creare grafiche personalizzate. Con questa classe è possibile implementare la trasparenza, fondere i colori e produrre l'illusione della trasparenza mediante la sovrapposizione e la fusione dei colori.
Trading bidirezionale e copertura delle posizioni in MetaTrader 5 utilizzando il pannello HedgeTerminal, parte 1 Trading bidirezionale e copertura delle posizioni in MetaTrader 5 utilizzando il pannello HedgeTerminal, parte 1
Questo articolo descrive un nuovo approccio al hedging delle posizioni e pone dei limiti nei dibattiti tra gli utenti di MetaTrader 4 e MetaTrader 5 su questo argomento. Gli algoritmi che rendono affidabile tale copertura sono descritti in parole povere e illustrati con semplici grafici e diagrammi. Questo articolo è dedicato al nuovo pannello HedgeTerminal, che è essenzialmente un terminale di trading completo all'interno di MetaTrader 5. Utilizzando HedgeTerminal e la virtualizzazione del trading che esso offre, le posizioni possono essere gestite in modo simile a MetaTrader 4.
MQL5 Cookbook: Implementazione di un array associativo o di un dizionario per l'accesso rapido ai dati MQL5 Cookbook: Implementazione di un array associativo o di un dizionario per l'accesso rapido ai dati
Questo articolo descrive uno speciale algoritmo che consente di accedere agli elementi tramite le loro chiavi univoche. Qualsiasi tipo di dati di base può essere utilizzato come chiave. Ad esempio può essere rappresentato come una stringa o una variabile intera. Tale contenitore di dati è comunemente indicato come dizionario o array associativo. Fornisce un modo più semplice ed efficiente di risolvere i problemi.
Principi dei prezzi di scambio attraverso l'esempio del mercato dei derivati della Borsa di Mosca Principi dei prezzi di scambio attraverso l'esempio del mercato dei derivati della Borsa di Mosca
Questo articolo descrive la teoria dei prezzi di cambio e le specifiche di compensazione del mercato dei derivati della Borsa di Mosca. Questo è un articolo completo per i principianti che cercano una prima esperienza di scambio sul trading di derivati, così come per i trader forex esperti che stanno considerando di fare trading su una piattaforma di scambio centralizzata.