Errori, bug, domande - pagina 105

 
Interesting:

Il tester ha la sua lista separata di strumenti e deve essere generato (preferibilmente fatto quando si inizializza l'EA).

Grazie mille! Capito. Mancava molto...
 
Renat:

Il codice era approssimativo (copiato da due pezzi), ma i tuoi commenti sono corretti.

Ecco la versione corretta:

Ho aggiunto un paio di chiamate a Print() per rendere chiaro come funziona effettivamente:

double CalculateMaxVolume(string symbol)
  {
   double price=0.0;
   double margin=0.0;
//--- select lot size
   if(!SymbolInfoDouble(symbol,SYMBOL_ASK,price))                return(0.0);
   if(!OrderCalcMargin(ORDER_TYPE_BUY,symbol,1.0,price,margin)) return(0.0);
   if(margin<=0.0)                                            return(0.0);

   double lot_pure=AccountInfoDouble(ACCOUNT_FREEMARGIN)/margin;
   double lot=NormalizeDouble(lot_pure,2);
   Print("lot_pure = ", lot_pure, ", lot = ", lot);
//--- normalize and check limits
   double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
   if(stepvol>0.0)
     {
      double newlot=stepvol*NormalizeDouble(lot/stepvol,0);
      if(newlot>lot) { Print("Чёрт побери: lot = ", lot, ", newlot = ", newlot);
                       lot=NormalizeDouble(newlot-stepvol,2);
                     }
      else           lot=newlot;
     }

   double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
   if(lot<minvol) lot=0.0;   // 

   double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
   if(lot>maxvol) lot=maxvol;
//--- return trading volume
   return(lot);
  }

void OnStart()
{
  Print("CalculateMaxVolume(Symbol()) = ", CalculateMaxVolume(Symbol()));
}

/* Вывод в лог (хронология - сверху вниз)
CO      0       1 (EURUSD,M15)  01:40:33        lot_pure = 7.799703611262773, lot = 7.8
JG      0       1 (EURUSD,M15)  01:40:33        Чёрт побери: lot = 7.8, newlot = 7.800000000000001
MQ      0       1 (EURUSD,M15)  01:40:33        CalculateMaxVolume(Symbol()) = 7.7
*/

Ora funziona correttamente. Ma con un avvertimento: nelle condizioni che si incontrano effettivamente ora. Se vengono estesi in futuro, questo codice comincerà a fare errori in alcuni casi in determinate condizioni. Lo spiegherò di seguito.

In realtà ho dovuto fare qualche ricerca e ho ottenuto risultati molto interessanti. Ma parliamo di loro uno per uno.

La prima cosa che mi salta all'occhio è questa: perché ho dovuto ballare così tanto con NormalizeDouble()? Bene, cosa fa NormalizeDoubel()? Lega un valore libero alla griglia. Per esempio, in questo frammento di codice

double lot=NormalizeDouble(lot_pure,2);

NormalizeDouble() prende come parametro lot_pure (valore libero, cioè quello calcolato da margine libero e margine necessario per 1 lotto senza arrotondamenti e altri vincoli) e dà il valore, legato al valore più vicino della griglia con inizio a 0 e passo 0,01.
Qui è importante notare: al nodo più vicino della griglia, compreso quello più grande!

A cosa serve questo vincolo in questo punto del codice? E perché alla griglia dello 0,01 e non, diciamo, dello 0,001?

A proposito, possiamo vedere che il risultato segnato con CO nel log (la prima linea del frammento mostrato) ha portato al valore aumentato.

Inoltre, sappiamo che tutte le funzioni commerciali che accettano il numero di lotti come uno dei parametri, richiedono che questo valore sia legato alla griglia: minvol + N * stepvol, dove N è un intero da 0 al valore di una parte intera dell'espressione (maxvol - minvol) / stepvol. Di conseguenza, il valore del lotto libero ottenuto in questo frammento:

double lot_pure=AccountInfoDouble(ACCOUNT_FREEMARGIN)/margin;

deve essere legato al nodo più vicino della griglia specificata: minvol + N * stepvol. Questo significa che bisogna prima sottrarre minvol dal valore del lotto prima di dividere per stepvol (per ottenere quell'intero N), e dopo aver moltiplicato per N, aggiungere minvol. Ma voi dividete immediatamente per stepvol, assumendo implicitamente che stepvol sia un divisore di minvol, cioè che si adatti un numero intero di volte, perché solo se questa condizione è soddisfatta potete "semplificare" in questo modo e non ottenere effetti collaterali:

double newlot=stepvol*NormalizeDouble(lot/stepvol,0);

Ancora una volta si usa NormalizeDouble(), questa volta per legarsi a una griglia con inizio a 0 e passo 1, cioè a numeri interi. I parametri di binding della griglia sono corretti, ma lo strumento di snap è un po' sfortunato: si lega al nodo più vicino nella griglia, incluso un nodo più grande se capita che sia più vicino. E nel nostro caso porterà ad una successiva elaborazione obbligatoria del codice di correzione. Perché non usare qui un meraviglioso strumento di collegamento a "una griglia di interi" usando invece di chiamare NormalizeDouble() un cast al tipo intero che non incrementa il valore che viene castato, ma lo decrementa solo al più vicino intero se necessario, cioè - ciò di cui abbiamo bisogno?

Ma qui si verifica un artefatto più interessante che è provato nella seconda riga del frammento citato contrassegnato da JG. Si scopre che l'espressione "0.1 * NormalizeDouble(7.8 / 0.1)" dà il risultato 7.800000000000001, che fa sì che il codice di correzione funzioni! Perché avete bisogno di un codice che funziona così male da doverne aggiungere uno correttivo?

Chiaramente, il codice di legame alla griglia dei valori accettabili del lotto deve essere sostituito con uno migliore.

Naturalmente, possiamo lasciare anche questo codice - dopo tutto, la parte correttiva del codice, semmai, funzionerà. Ecco la terza linea del log lo dimostra: il risultato ritorna proprio alla fine. Ma questo codice, d'altra parte, è un indicatore della professionalità e della qualità dei suoi creatori. Compresa la qualità del codice della piattaforma MT5. E la prova di questo sarà data da me, perché mi sono imbattuto in due bug come risultato della mia ricerca.

A proposito, diamo un'altra occhiata al codice di collegamento iniziale del valore calcolato lot_pure e al codice di correzione:

double lot=NormalizeDouble(lot_pure,2);
...
lot=NormalizeDouble(newlot-stepvol,2);
In entrambi i casi c'è un legame alla griglia con il passo 0,01. Perché questa griglia? Perché vuoi legarti alla griglia minvol + N * stepvol che ha uno stepvol. Cosa succederà quando in futuro avremo sia un valore minimo del lotto che uno stepvol di 0,001?

È molto semplice - il codice darà risultati sbagliati nei casi in cui nel processo di legame alla griglia con il passo 0,01 il valore libero cambia sul valore più di 0,001. Questo è l'avvertimento che ho menzionato all'inizio.

Se si "arrotonda per eccesso" di più di 0,001, verranno restituiti i valori dei lotti dove non c'è abbastanza margine libero per aprire la posizione, mentre in caso di "arrotondamento per difetto" della stessa quantità - valori sottostimati o 0, se il valore libero si trova tra 0,001 e 0,004999...

Cioè, il codice contiene un potenziale bug per il futuro. È una questione di professionalità degli sviluppatori e di qualità del loro codice.

Ora, tenendo conto di ciò che ho trovato, suggerirò la mia variante della funzione:

double CalculateMaxVolume_New(string symbol)
{
  double stepvol = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
  double minvol  = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
  double maxvol  = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);

  // Не доверяем значениям, которые вернули функции
  if(stepvol > 0 && minvol > 0 && maxvol > minvol)
  {
    double tmp = 0;

    // Вычисляем цену Ask, Margin на 1 лот, лотов на FreeMargin
    if(SymbolInfoDouble(symbol, SYMBOL_ASK, tmp)            && tmp > 0       &&
       OrderCalcMargin(ORDER_TYPE_BUY, symbol, 1, tmp, tmp) && tmp > 0       &&
       (tmp = AccountInfoDouble(ACCOUNT_FREEMARGIN) / tmp)         >= minvol &&
       tmp < ULONG_MAX * stepvol)
    {
      Print("pure_lot = ", tmp); // Эту строку нужно удалить из рабочего кода

      if(tmp > maxvol) // Здесь в tmp содержится недискретизированное число лотов
        return maxvol;

      // Привязываемся к сетке дискретизации
      return minvol + stepvol * (ulong)((tmp - minvol) / stepvol);
    }
  }

  return 0;
}

void OnStart()
{
  Print("CalculateMaxVolume_New(Symbol()) = ", CalculateMaxVolume_New(Symbol()));
}

/* Вывод в лог (хронология - сверху вниз)
LQ      0       1 (EURUSD,M15)  01:39:07        pure_lot = 7.799095304944626
KD      0       1 (EURUSD,M15)  01:39:07        CalculateMaxVolume_New(Symbol()) = 7.7
*/

Ci sono diversi casi, in relazione al valore dei lotti (memorizzato nel mio tmp), calcolato ma non ancora legato a una griglia di valori validi. Chiamiamo il valore della griglia discretizzato.

1. Il caso in cui tmp < minvol. In questo caso, la disuguaglianza rimarrà anche dopo la discretizzazione di tmp, perché il processo di discretizzazione comporta solo la riduzione del valore calcolato (altrimenti non c'è abbastanza margine libero, perché il valore calcolato è il massimo valore possibile per la quantità data di margine libero).

Quindi questo caso può essere eliminato in una fase iniziale, prima del campionamento.

2. Il caso in cui tmp > maxvol. In questo caso la limitazione non è il margine libero, ma il numero massimo consentito di lotti accettati dalle funzioni di trading. In questo caso, basta restituire il valore di maxvol.

Per restituire semplicemente il valore di maxvol, non è richiesto alcun campionamento di tmp, quindi anche questo caso è tagliato fuori prima del codice di campionamento.

3. Il caso in cui minvol <= tmp <= maxvol. In questo caso è necessario discretizzare, ma il valore discretizzato rimane all'interno di una disuguaglianza per questo caso, cioè non c'è bisogno di correggere nulla dopo la discretizzazione.

Il codice di campionamento è semplice ed efficiente:

return minvol + stepvol * (ulong)((tmp - minvol) / stepvol);

Qui, l'espressione "(tmp - minvol) / stepvol" calcola lo stesso numero N (il parametro della griglia di ancoraggio), ma con una parte frazionaria. Poiché la disuguaglianza minvol <= tmp (caso 3) è qui soddisfatta, è quindi garantito che il valore calcolato sia non negativo. Poi castiamo esplicitamente il valore calcolato in un valore di tipo ulong. È ulong perché è garantito che il valore calcolato sia non negativo.

Durante la conversione al tipo intero, la parte frazionaria del tipo reale viene scartata. Non è un arrotondamento all'uno più vicino, ma la parte frazionaria viene scartata, il che garantisce che il valore massimo dei lotti, che il margine libero permette, non sarà aumentato. Questo è esattamente ciò di cui abbiamo bisogno.

Poiché il valore intero di N è ottenuto, allora il valore massimo discretizzato dei lotti, che soddisfa i requisiti di margine libero, è ottenuto nel modo standard (cioè il prossimo intero nella griglia di discretizzazione dei valori massimi dei lotti non soddisferà i requisiti di margine libero, mentre l'N ottenuto soddisferà ancora i requisiti di margine libero): "minvol + stepvol * N".

Voglio sottolineare un punto abbastanza importante. Il fatto è che i numeri di tipo doppio possono assumere valori massimi fino a circa 1.8e308, mentre i numeri di tipo ulong - solo circa 1.8e19 (è solo una voce per comodità, la costante 1.8e19 non è di tipo ulong).

Cosa succede se il valore dell'espressione "(tmp - minvol) / stepvol" supera 1,8e19? In questo caso, durante la conversione al tipo ulong, avverrà un "taglio" - il valore sarà il resto della divisione intera del valore dell'espressione per ULONG_MAX.

Se i numeri non fossero così grandi, in termini di MQL5, sarebbe come "X % ULONG_MAX", dove X è un intero uguale a "(tmp - minvol) / stepvol".

Questo non è un caso tipico, ma perché lasciare bug nel codice? Inoltre, non ci si può fidare delle funzioni della libreria MQL5, possono restituire qualsiasi sciocchezza (ne darò la prova).

Per i casi in cui il valore dell'espressione "tmp / stepvol" non rientra in 1.8e19, abbiamo volutamente introdotto un controllo (l'ultima riga della condizione if):

tmp < ULONG_MAX * stepvol

Naturalmente, si può scrivere "tmp / stepvol < (double)ULONG_MAX" ma in primo luogo, cerco di evitare le operazioni di divisione quando non c'è una necessità esplicita e in secondo luogo, dovrei eseguire una conversione di tipo esplicita poiché la costante ULONG_MAX è di tipo ulong, Quando si confrontano gli operandi, non vengono implicitamente castati a un tipo superiore (almeno è così in C/C++), e nel terzo - non avrei incontrato un bel bug nel linguaggio stesso, nemmeno nelle funzioni di libreria - il bug non è quello nel DNA, ma letteralmente nelle molecole e negli atomi di MQL5.

L'operando sinistro dell'operazione di confronto è tmp e ha il tipo doppio mentre l'operando destro è l'espressione "ULONG_MAX * stepvol" e ha anch'esso il tipo doppio.

Questa espressione ha due operandi, uno di tipo ulong e l'altro di tipo double. Secondo le regole della conversione implicita dei tipi, prima l'operando del tipo inferiore viene castato nell'operando del tipo superiore, l'operazione viene eseguita e il risultato viene castato nell'operando del tipo superiore. Il tipo double è più "vecchio" di ulong, ecco perché il valore ULONG_MAX di ulong è implicitamente castato a double, l'operazione viene eseguita e il risultato è di tipo double.

Tuttavia, c'è un bug qui che, tra l'altro, appare non sempre ma solo in alcuni casi incluso questo, e il bug consiste nel fatto che il risultato dell'espressione "ULONG_MAX * stepvol" è solo il valore di stepvol.

Pertanto, la funzione che sto mostrando non funziona e non funzionerà finché gli sviluppatori di MetaQuotes non risolveranno questo bug.

Per iniziare a usare questa funzione ora, dovreste approfittare della peculiarità del bug: scomparirà se eseguite una conversione esplicita del tipo:

tmp < (double)ULONG_MAX * stepvol

Tornando al controllo descritto: garantisce che il valore dell'espressione "tmp / stepvol" non superi ULONG_MAX. Ma il codice di campionamento usa l'espressione "(tmp - minvol) / stepvol".

Il valore di questa espressione non supererà nemmeno ULONG_MAX perché i controlli precedenti assicurano che minvol > 0 e tmp >= minvol, cioè tmp - minvol < tmp.

Pertanto, la garanzia di non superare ULOMG_MAX si applica anche all'espressione "(tmp - minvol) / stepvol".

In generale, una delle principali differenze tra un professionista e un profano è che un professionista può almeno garantire qualcosa, mentre un profano...

Ho smontato entrambi i bug trovati nell'altropost, chiarendo allo stesso tempo ciò che MetaQuotes ha fatto e non è riuscito a fare.

 

Для чего в этом месте кода выполняется эта привязка? И почему именно к сетке 0.01, а не к, скажем, 0.001?

Nel sistema, il lotto minimo = 0,01


Note:

  1. La tua condizione iniziale minvol + N * stepvol non è garantita per essere corretta, puoi impostare minlot su un valore diverso e la tua logica sarà rotta.
  2. Avresti dovuto passare a ulong per niente - ti sei creato delle difficoltà, e poi hai scritto un'intera pagina di pensieri su questo
  3. La sostituzione di tmp nel tuo codice è troppo intelligente, la mia versione è molto più chiara in termini di operazioni
 

Parlo solo per me (ma se vedi il tuo riflesso, non sei l'unico).

Negli ultimi mesi di caccia ai bug, ho sviluppato l'abitudine di considerare un bug in MetaTrader come un bug in primo luogo.

Perché così, è solo un modello ben collaudato, se qualcosa non funziona, allora è un bug e fate suonare i campanelli d'allarme.

Esempio: ho trovato un bug, ho inviato una richiesta a servicedesk, hanno scritto un codice di verifica, ma niente.

Ho fatto di nuovo domanda e nell'attesa di una risposta ho trovato la mia goffaggine.

Il risultato è che mi vergogno di aver distratto la gente sul posto.

Ma analizzando il flusso di messaggi capisco che la massa di persone, anche se persone intelligenti sono ancora sottoposti alla psicologia della folla.

Se ci sono dei bug, scriverò un bug e lascerò che Renat risolva il mio codice e punti il dito sul mio errore.

Capisco che la tolleranza non permette di dire: sì, sei un idiota, il tuo codice è storto.

Ma non si può andare così lontano, e prolungando ulteriormente tutto il personale MQ sarà presto impegnato in che sedersi sui codici di altre persone in luttuosa contemplazione "ma perché abbiamo bisogno di tutto", mentre il campionato sta arrivando, e ci e andare ai conti reali non sono lontani.

Concludo, il mio motto per oggi è "Se stai per pubblicare un bug, controlla se il problema è nelle tue mani".

Общайтесь с разработчиками через Сервисдеск!
Общайтесь с разработчиками через Сервисдеск!
  • www.mql5.com
Ваше сообщение сразу станет доступно нашим отделам тестирования, технической поддержки и разработчикам торговой платформы.
 

Nuova costruzione - nuovi problemi. Expert, che ha funzionato bene in 306 dopo la compilazione in 314 (compilazione senza errori), si spegne in tester:

2010.08.21 17:03:36 Core 1 disconnesso
2010.08.21 17:03:36 Core 1 tester fermato perché OnInit fallito
2010.08.21 17:03:36 Core 1 2010.01.04 00:00:00 Violazione di accesso letto a 0x00000000000014
2010.08.21 17:03:36 Core 1 2010.01.04 00:00:00 Balance=10000.00 Equite=10000.00 Profit=0.00
2010.08.21 17:03:36 Core 1 2010.01.04 01.04 00:00:00 PriceChannel_multi_Ch_Timer Expert Advisor ha iniziato a lavorare nel 2010.01.04 00:00 sul grafico EURUSD per il periodo H1

Si scarica anche nella vita reale. Sembrava che la fonte di errore fosse una linea

m_symbol[j].Name(TradeSymbols[i]);

Sostituendolo con un paio di righe

string curSymbol=TradeSymbols[i];
m_symbol[j].Name(curSymbol);

ha restituito lo status quo all'Expert Advisor.

Cosa c'è che non va?

A proposito, il codice compilato nell'ultima build funziona bene anche in questa.

 
Valmars:

Cosa c'è che non va?

A proposito, il codice compilato nell'ultima build funziona bene anche in questa.

Errore nostro - lo sistemeremo sicuramente.
 
Renat:

Lotto minimo = 0,01


Note:

  1. La tua condizione iniziale minvol + N * stepvol non è garantita per essere corretta, puoi impostare minlot su un valore diverso e la tua logica sarà rotta.
  2. Non avresti dovuto passare a ulong - ti sei creato delle difficoltà, e poi hai scritto un'intera pagina di pensieri su questo
  3. La sostituzione di tmp nel tuo codice è troppo intelligente, mentre la mia versione è molto più chiara in termini di operazioni.

In questo momento il lotto minimo del sistema = 0,01. Ma cosa succederà tra un anno? In due anni?

1) Quale condizione è corretta? Qual è allora la formula corretta? Diciamo, per minvol = 0.15 e stepvol = 0.1 - quali sarebbero i primi valori di lotto validi? a) 0.15, 0.25, 0.35... ? б) 0.15, 0.2, 0.3... ? в) ... ? Ho dato per scontato che sia l'opzione a.

2. Sono passato a ulong per una ragione - ho il diritto di scegliere il tipo con la gamma più ampia, in modo che sia sufficiente per la più ampia gamma possibile di casi, perché tali funzioni sono mattoni molto basilari. E il fatto che mi sia imbattuto in un bug non significa che sia stato io a creare i problemi. :) Il ragionamento è stato scritto più per gli altri per renderlo chiaro alla gamma più ampia possibile - non abbiamo qui una corrispondenza personale.

3. La sostituzione non è difficile - solo il risparmio, per non creare variabili di uso unico. Ed è stato controllato e verificato che la variabile viene passata per riferimento quando una funzione viene chiamata al massimo una volta, in modo da evitare possibili errori a causa di ciò. Se a qualcuno dà fastidio, può creare una variabile per ogni valore trasferito (anche uno intermedio, come Ask price), come hai fatto tu. Questo punto non è importante.

Molto più importante è il meccanismo di vincolo alla griglia dei valori ammissibili, che peraltro non richiede correzioni, e che garantisce contro il verificarsi di glitch in vari casi non molto tipici, pur mantenendo la massima semplicità possibile.

La premessa è che il blocco di base dovrebbe essere il più robusto e versatile possibile - allora l'intera casa probabilmente sopravviverà anche a un terremoto.

 
Urain:

Parlo solo per me (ma se vedi il tuo riflesso, non sei l'unico).

Negli ultimi mesi di caccia ai bug, ho sviluppato l'abitudine di considerare un bug in MetaTrader come un bug in primo luogo.

Perché è così, è solo un modello noto, se qualcosa non funziona, allora è un bug e che suoni il campanello d'allarme.

Il fatto che i bug di MQL5 e MQL5 assomiglino molto ai loro gioca un ruolo importante qui. E ci sono molti bug di MQL5.

Se MQL5 avesse significativamente meno bug e non fosse così semplice, sarebbe molto più difficile confonderli.

Urain:

Hanno già iniziato a pensare alla possibilità di iniziare il campionato, che sta per iniziare la vera sessione di trading.

Concludo, il motto di oggi è "Se stai per pubblicare un bug, controlla se il problema è nelle tue mani".

Il fatto che il campionato per gli Expert Advisors scritti SOLO in MQL5 sia un azzardo era chiaro al momento dell'annuncio della decisione. Ma la direzione dell'EA ha una sua visione. L'hanno deciso loro stessi. Nessuno ha interferito con la loro decisione. Quindi, che importa se il campionato è dietro l'angolo, si sono fatti una vita.

Qui è facile: bisogna fare un po' di lavoro sulla localizzazione del bug: iniziare a rimuovere dal codice tutto ciò che non riguarda il bug. Alla fine otterrete una specie di esempio di test, che è abbastanza piccolo ma dimostra soprattutto il bug. Questo non sarà più "il codice di qualcun altro" ma "il codice che dimostra il bug MQL5".

 
Scritto uno script per testare la funzione OrderCalcMargin()
void OnStart()
  {
//---
   int total=SymbolsTotal(false);
   double marginbay;
   double marginsell;
   MqlTick last_tick;
   for(int i=0;i<total;i++)
     {

      string symbol=SymbolName(i,false);
      Print("************************************************");
      Print("Инструмент - ",symbol);
      Print("Валюта депозита = ",AccountInfoString(ACCOUNT_CURRENCY));
      Print("Базовая валюта = ",SymbolInfoString(symbol,SYMBOL_CURRENCY_BASE));
      Print("Валюта маржи = ",SymbolInfoString(symbol,SYMBOL_CURRENCY_MARGIN));
      if(SymbolInfoTick(symbol,last_tick))
        {
         OrderCalcMargin(ORDER_TYPE_BUY,symbol,1.0,last_tick.ask,marginbay);
         OrderCalcMargin(ORDER_TYPE_SELL,symbol,1.0,last_tick.bid,marginsell);
         Print("Маржа для покупки = ",marginbay);
         Print("Маржа для продажи = ",marginsell);
        }
      else Print("SymbolInfoTick() failed, error = ",GetLastError());
     }
  }
//+------------------------------------------------------------------+
La funzione restituisce zero per alcuni strumenti, è un bug o è stato progettato così?
 
sergey1294:
Ho scritto uno script per controllare la funzione OrderCalcMargin(). La funzione restituisce zero per alcuni simboli.

Questo è probabilmente per quei simboli che non sono in MarketWatch, come si dice SymbolName per SymbolName:

SymbolName

Restituisce il nome del simbolo specificato.

stringaSymbolName(
intpos,// numero nella lista
bool selected// true - solo simboli in MarketWatch
);

Parametri

pos

[Numero di simbolo in ordine.

selezionato

[Modalità di interrogazione. Se vero, allora il simbolo viene preso dalla lista dei selezionati in MarketWatch. Se il valore è false, allora il simbolo viene preso dalla lista comune.

Valore restituito

Valore di tipo stringa con nome del simbolo.

Stampa il nome del simbolo per il quale ottieni un risultato inaspettato e confrontalo con la lista in MarketWatch.