Documentation

Applications

Dernière mise à jour le 18. 5. 2020 par Mark Fric

Indicateur ForceIndex

Nous allons voir comment ajouter un nouvel indicateur personnalisé dans StrategyQuant X. Contrairement à SQ3, SQX permet d'étendre le programme en créant vos propres indicateurs personnalisés et blocs de construction - de la même manière que vous pouvez ajouter de nouveaux indicateurs aux plateformes de trading normales comme MetaTrader 4/5, Tradestation et ainsi de suite.

Nous allons suivre ce processus étape par étape afin d'expliquer tout ce qui est nécessaire.

Au final, nous aurons un nouvel indicateur personnalisé ajouté à StrategyQuant et vous pourrez l'ajouter à la liste des blocs de construction pour la génération de stratégies.

Nous utiliserons Indice de force un exemple. Cet indicateur est déjà intégré dans Metatrader, vous pouvez trouver son code source ici : https://www.mql5.com/en/code/8013

Voici comment se présente l'indicateur sur le graphique :

 

Il y a quatre étapes à suivre pour ajouter un nouvel indicateur à SQ X :

  1. Ajout d'un nouvel indicateur
  2. (Facultatif, recommandé) Test du nouvel indicateur dans SQ X par rapport aux données de MT
  3. (Facultatif, recommandé) Ajout de nouveau(x) bloc(s) de signaux basé(s) sur l'indicateur
  4. Ajout de la traduction du bloc d'indicateurs dans la langue de la plateforme cible

 

Étape 1 - Création d'un nouvel indicateur personnalisé dans l'éditeur de code

La première étape consiste à créer notre indicateur dans l'éditeur de code. StrategyQuant utilise en interne le langage de programmation Java, et les indicateurs personnalisés doivent être programmés en Java.

Vous ne pouvez pas copier et coller votre code MQL dans StrategyQuant, cela ne fonctionnerait pas. Vous devez réécrire l'indicateur en langage Java.

Nous devons d'abord ouvrir l'éditeur de code :

Dans l'éditeur de code, vous pouvez créer, éditer et modifier des snippets, nous allons donc créer un nouveau snippet pour notre nouvel indicateur.

Une fois dans l'éditeur de code, nous cliquons sur le bouton Créer un nouveau dans la barre d'outils supérieure.

Cela ouvrira une boîte de dialogue dans laquelle nous pourrons nommer notre nouveau snippet et choisir son type. Nous l'appellerons "ForceIndex"et choisissez Indicateur comme type d'extrait.

Après avoir cliqué sur OK, un nouveau dossier ForceIndex sera créé sous Snippets -> SQ -> Blocs -> Indicateurs et un nouveau fichier de snippets ForceIndex.java dans ce dossier.

La convention dans StrategyQuant est que chaque indicateur est dans son propre dossier - c'est parce que plus tard nous pourrions vouloir créer des signaux pour cet indicateur, et tous les snippets liés seront dans le même dossier.

Cette action a créé un nouveau fichier pour notre indicateur ForceIndex et l'a ouvert dans l'éditeur. Vous pouvez voir que l'indicateur est une classe et qu'il a déjà une certaine structure.

Chaque indicateur est étendu à partir de la classe IndicatorBlock et doit implémenter une méthode :

  • OnBarUpdate() - où la valeur de l'indicateur est calculée et stockée dans l'un des tampons de sortie. Elle est appelée pour chaque barre du graphique.

Lorsque vous vérifierez le code source, vous devriez remarquer certaines choses.

Tout d'abord, les snippets dans StrategyQuant utilisent beaucoup d'annotations - @Parameter, @Output, @BuildingBlock - ces annotations sont utilisées pour déclarer une propriété spéciale d'une variable ou d'une classe donnée - que la variable est un paramètre public ou une valeur de sortie, ou que la classe est un bloc d'indicateur.

Deuxièmement, une fois que vous avez créé et compilé un indicateur, vous pouvez l'appeler à partir d'autres méthodes ou indicateurs en utilisant la fonction Indicators.YourIndicator(YourIndicatorParameters). Voici comment notre nouvel indicateur est appelé dans la méthode OnBlockEvaluate().

Nous allons parcourir pas à pas le code source de la classe d'indicateur par défaut créée à partir d'un modèle :

package SQ.Blocks.Indicators.ForceIndex ;
 
import com.strategyquant.lib.* ; import com.strategyquant.datalib.* ; import com.strategyquant.tradinglib.* ;
 
import SQ.Internal.IndicatorBlock ;

il s'agit de la déclaration Java standard d'un paquet et de l'importation des classes nécessaires - celles que nous utilisons dans notre propre classe.

@BuildingBlock(name="(XXX) ForceIndex ", display="ForceIndex (#Period#)[#Shift#]", returnType = ReturnTypes.Price)
@Help("Texte d'aide ForceIndex")

Définition annotée de notre classe d'indicateurs, indiquant au système qu'il s'agit d'un bloc de construction avec un nom donné.

  • nom est ce qui est affiché dans l'interface utilisateur lors de la sélection des blocs de construction.
  • affichage est ce qui est affiché dans l'assistant avec les paramètres. Vous pouvez contrôler quels paramètres sont affichés et à quel endroit.
  • type de retour est un type d'indicateur, il indique quel type de valeur cet indicateur calcule. Il est utilisé pour que StrategyQuant sache quels types doivent être comparés avec quoi. Par exemple, il ne comparera pas l'indicateur CCI (qui renvoie un nombre) avec les bandes de Bolinger (qui renvoient un prix).

Un indicateur peut avoir trois types de retour :

  • Prix - L'indicateur calcule le prix et est affiché sur le graphique des prix - comme les bandes de Bollinger, les moyennes mobiles, etc.
  • Nombre - L'indicateur calcule un nombre qui est affiché sur son propre graphique - comme CCI, RSI, MACD, etc.
  • Fourchette de prix - l'indicateur calcule la fourchette de prix (différence entre deux prix) - comme l'ATR

D'autres types de retour sont utilisés dans d'autres types de blocs de construction.

Dans notre cas, ForceIndex N'EST PAS affiché sur le graphique des prix, et son type de retour est donc Number (nombre) et non Price (prix). Nous le modifierons à l'étape suivante.

@Parameter public DataSeries Input ;
 
@Parameter(defaultValue = "10", isPeriod = true, minValue=1, maxValue=10000, step=1) public int Period ;
 
@Output public DataSeries Value ;

ce qui suit sont les paramètres de l'indicateur. Chaque indicateur peut avoir plusieurs paramètres d'entrée, le modèle n'en crée que deux à titre d'exemple. Chaque paramètre est annoté avec @Paramètre qui peut avoir plusieurs attributs.

Le tout premier paramètre est EntréeIl s'agit du tableau de la série de données à partir duquel l'indicateur est calculé - il peut s'agir par exemple du cours d'ouverture, du cours le plus élevé, du cours le plus bas ou du cours de clôture.

Le deuxième paramètre est PériodeLes indicateurs ont généralement une période sur laquelle ils sont calculés.

La troisième variable est ValeurIl est à noter qu'il a une annotation différente @Sortie. Cela signifie que cette variable n'est pas un paramètre de l'indicateur mais son tampon de sortie. Les indicateurs n'ont généralement qu'un seul tampon de sortie, mais ils peuvent en avoir plusieurs - par exemple, la bande de Bollinger a un tampon supérieur et un tampon inférieur.

Il existe un autre paramètre caché Déplacement - Il est présent par défaut dans tous les indicateurs et indique au moteur de trading quelle valeur il doit rechercher. Vous n'avez généralement pas besoin de vous préoccuper de ce paramètre, il est utilisé automatiquement.

Ensuite, il y a une méthode :

protected void OnBarUpdate() {...}

C'est la méthode par laquelle les valeurs des indicateurs sont calculées. Elle est appelée en interne par SQ pour chaque barre et doit calculer la valeur de l'indicateur pour cette barre et l'enregistrer dans le tampon de sortie.

Il s'agit du code source du modèle d'indicateur standard. Dans l'étape suivante, nous montrerons les modifications à apporter pour mettre en œuvre notre indicateur ForceIndex.

 

Étape 2 - Modification du modèle par défaut généré et mise en œuvre de l'indicateur

L'indicateur créé à l'étape 1 est un modèle d'indicateur standard, il ne calcule pas encore ForceIndex . Pour implémenter ForceIndex, nous devons faire quelques choses :

 

Mettre à jour son annotation @BuildingBlocks

Nous mettrons à jour l'annotation de cet indicateur comme suit :

@BuildingBlock(name="(FI) ForceIndex ", display="ForceIndex (#Nbr_Periods#, #Multiplier)[#Shift#]", returnType = ReturnTypes.Number)

C'est la partie la plus simple. Nous allons simplement mettre à jour nom de l'indicateur et ajouter les nouveaux paramètres actuels (voir ci-dessous) à l'indicateur affichage attribut.

Nous avons également changé le type de retour (returnType) en Nombre, car cet indicateur calcule des nombres affichés sur un graphique séparé, il ne produit pas de valeur de prix.

 

Définir les paramètres de ForceIndex

La première chose à faire est un peu délicate - nous devons changer le type de l'élément par défaut Entrée paramètre. Dans le modèle standard, il est défini comme suit :

@Parameter public DataSeries Input ;

il s'agit d'un paramètre nommé Entréeavec le type Série de données. Ceci est valable pour une grande partie des indicateurs qui sont calculés à partir d'un seul prix. Par exemple, les indicateurs CCI, RSI, etc. sont généralement calculés à partir du cours de clôture. Vous pouvez les configurer pour qu'ils soient calculés à partir d'un prix différent, par exemple à partir du prix de l'ouverture, mais il s'agit toujours d'un seul tableau de prix.

Le type DataSeries est un tableau de valeurs qui contient des valeurs pour les cours de clôture, les cours d'ouverture, les cours typiques, etc.
Cependant, si vous regardez le code source MQL de ForceIndex, vous verrez qu'il calcule ses valeurs à partir de l'une des valeurs de prix et du volume.

Pour pouvoir accéder à plusieurs tableaux de prix à la fois, nous utiliserons différents types de sortie :

@Parameter public ChartData Chart ;


Données graphiques
est un objet qui représente l'ensemble du graphique - vous aurez accès aux prix Open, High, Low, Close, Volume dans le graphique donné.


Note rapide - choisir le bon type de variable pour les données d'entrée :
Si l'indicateur est calculé à partir d'un seul prix, utilisez DataSeries.
S'il est calculé à partir de plusieurs prix - par exemple High, Low, Close, etc. - utilisez ChartData.

Il y a ensuite un paramètre "Période", que nous pouvons également laisser inchangé :

@Parameter(defaultValue="10", isPeriod=true, minValue=2, maxValue=1000, step=1) public int Period ; 

Le troisième paramètre est la méthode de la moyenne mobile :

@Parameter(name="Method", defaultValue="0") @Editor(type=Editors.Selection, values="Simple=0,Exponential=1,Smoothed=2,Linear weighted=3") public int MAMethod ;

C'est un peu plus complexe, car nous définissons une liste de sélection (boîte combo) comme contrôle d'édition de ce paramètre. Ainsi, lors de l'édition dans l'assistant, l'utilisateur pourra choisir parmi les valeurs prédéfinies.

Le dernier paramètre est le prix appliqué - prix qui doit être utilisé dans le calcul de la moyenne mobile :

@Parameter(defaultValue="0") @Editor(type=Editors.Selection, values="Close=0,Open=1,High=2,Low=3,Median=4,Typical=5,Weighted=6") public int AppliedPrice ;

Notez que nous avons utilisé certains attributs dans le @Paramètre annotation :

  • valeur par défaut définit la valeur par défaut de ce paramètre
  • isPeriod=true indique à SQ que ce paramètre est une période - ils sont traités d'une manière spéciale, dans la configuration du Builder vous pouvez configurer la valeur minimum et maximum pour les périodes, donc SQ a besoin de savoir quels paramètres sont des périodes.
  • minValue, maxValue, step sont les valeurs minimale et maximale qui seront générées pour ce paramètre au cours du processus de génération aléatoire.

Définir les sorties ForceIndex

L'indicateur ForceIndex n'a qu'une seule sortie, nous pouvons donc le laisser inchangé :

@Output public DataSeries Value ;

L'annotation @Sortie signifie qu'il s'agit d'un tampon de sortie pour cet indicateur. Notez qu'il est de type DataSeries, ce qui signifie qu'il s'agit d'un tableau de valeurs doubles.

Mise en œuvre de la méthode OnBarUpdate()

Si vous regardez le code MQL de ForceIndex, vous verrez qu'il est assez simple, son code MQL est le suivant :

int start() {
  int nLimit ;
  int nCountedBars=IndicatorCounted() ;

  //---- données insuffisantes
  if(BarsExtForcePeriod) nCountedBars-- ;
  nLimit=Bars-nCountedBars ;

  //---- Indice de force compté
  for(int i=0 ; i<nLimit ; i++)
    ExtForceBuffer[i] = Volume[i] * (iMA(NULL,0,ExtForcePeriod,0,ExtForceMAMethod,ExtForceAppliedPrice,i) - iMA(NULL,0,ExtForcePeriod,0,ExtForceMAMethod,ExtForceAppliedPrice,i+1)) ;

  //---- fait
  return(0) ;
}

En analysant l'algorithme, nous pouvons voir que la valeur de l'indice de force à une bougie donnée est calculée comme suit :

ForceIndex[i] = Volume[i] *(MovAvg(Period, MAMethod, AppliedPrice)[i] - MovAvg(Period, MAMethod, AppliedPrice)
[i+1]

 

Nous pouvons l'implémenter en Java de la manière suivante :

protected void OnBarUpdate() throws TradingException {
    DataSeries computedFrom = Chart.getSeries(AppliedPrice) ;
 
    double indiValue = Chart.Volume.get(0) * (Indicators.MA(computedFrom, Period, MAMethod).Value.get(0) - Indicators.MA(computedFrom, Period, MAMethod).Value.get(1)) ;
 
    Value.set(0, indiValue) ;
} 

Dans ce cas, l'indicateur est assez simple et son calcul ne nécessite pratiquement qu'une seule ligne dans StrategyQuant.

Nous obtenons d'abord le prix pour calculer l'indicateur à partir de - en appelant Graphique.getSeries(PrixAppliqué).
Ensuite, nous calculons la nouvelle valeur de l'indicateur en tant que différence de la moyenne de la valeur du prix actuel et de la valeur du prix précédent multipliée par le volume actuel.

 

C'est tout, maintenant quand on touche Compiler et redémarrer SQ nous verrons notre nouvel indicateur ForceIndex dans la section Random Indicators Signals.

Code source complet de notre nouvel indicateur :

package SQ.Blocks.Indicators.ForceIndex ;

import com.strategyquant.lib.* ;
import com.strategyquant.datalib.* ;
import com.strategyquant.tradinglib.* ;

import SQ.Internal.IndicatorBlock ;

/**
 * Nom de l'indicateur tel qu'il sera affiché dans l'interface utilisateur, et son type de retour.
 * Types de retour possibles :
 * ReturnTypes.Price - l'indicateur est dessiné sur le graphique des prix, comme SMA, Bollinger Bands etc.
 * ReturnTypes.Price - l'indicateur est dessiné sur un graphique séparé, comme CCI, RSI, MACD.
 * ReturnTypes.PriceRange - l'indicateur est une fourchette de prix, comme ATR.
 */
@BuildingBlock(name="(FI) ForceIndex ", display="ForceIndex (#Period#)[#Shift#]", returnType = ReturnTypes.Number)
@Help("Texte d'aide ForceIndex")
public class ForceIndex extends IndicatorBlock {

  @Parameter
  public ChartData Chart ;

  @Parameter(defaultValue="10", isPeriod = true, minValue=2, maxValue=1000, step=1)
  public int Period ;

  @Parameter(name="Method", defaultValue="0")
  @Editor(type=Editors.Selection, values="Simple=0,Exponential=1,Smoothed=2,Linear weighted=3")
  public int MAMethod ;

  @Parameter(defaultValue="0")
  @Editor(type=Editors.Selection, values="Close=0,Open=1,High=2,Low=3,Median=4,Typical=5,Weighted=6")
  public int AppliedPrice ;

  @Sortie
  public DataSeries Value ;

  //------------------------------------------------------------------------
  //------------------------------------------------------------------------
  //------------------------------------------------------------------------

  /**
   * Cette méthode est appelée à chaque mise à jour de la barre et la valeur de l'indicateur est calculée ici.
   *
   * Contrairement à MT4, vous ne calculez pas les valeurs de l'indicateur pour plusieurs barres dans une boucle,
   * Vous ne devez calculer la valeur que pour la dernière barre (actuelle).
   * Le moteur de trading se chargera d'appeler cette méthode pour chaque barre du graphique.
   *
   * La barre pour laquelle la valeur de l'indicateur est calculée est stockée dans la variable CurrentBar.
   * Si elle est égale à 0, cela signifie qu'il s'agit de la toute première barre du graphique.
   */
  @Override
  protected void OnBarUpdate() throws TradingException {
    	double indiValue ;

    	indiValue = Chart.Volume.get(0) * (Indicators.MA(Chart.Close, Period, MAMethod).Value.get(0) - Indicators.MA(Chart.Close, Period, MAMethod).Value.get(1)) ;

      	Value.set(0, indiValue) ;
  }

}

Cet article a-t-il été utile ? L'article était utile L'article n'était pas utile

S'abonner
Notification pour
5 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires
Pieter Kotzee
Pieter Kotzee
21. 6. 2020 8:56 am

Bonjour, j'ai copié le code exactement comme indiqué ci-dessus mais j'ai obtenu 2 erreurs en essayant de compiler. Les deux sont à la ligne 55 et ont la même description. L'une est la colonne 54 et l'autre la colonne 114 :
cannot find symbol symbole : method MA(com.strategyquant.datalib.Dataseries,int,int) location : variable Indicators of type SQ>Internal.Indicators
 

Dernière modification le 3 années il y a par Pieter Kotzee
tomas262
tomas262
Répondre à  Pieter Kotzee
29. 6. 2020 10:06 pm

La méthode MA ne fait pas partie du pack. Vous pouvez la trouver ici https://strategyquant.com/codebase/forceindex/

Emmanuel2
21. 9. 2021 7:37 pm

Merci beaucoup, cet exemple nous aide beaucoup.

Matthew Nowlis
Matthew Nowlis
26. 2. 2022 7:09 pm

J'ai réussi à ajouter le code java pour l'indicateur Force Index et les blocs de signaux. Il se compile sans erreur. Cependant, lorsque je génère des stratégies dans StrategyQuant avec l'indicateur, j'obtiens une erreur parce que les modèles de code (MT4, MT5, pseudocode, etc.) sont manquants. Il est indiqué dans le code de la stratégie :

Erreur ! Un ou plusieurs blocs utilisés par la stratégie ne sont pas implémentés dans (MT4, MT5, pseudocode, etc)

L'inclusion du modèle a échoué (pour valeur du paramètre "blocs/ForceIndex.tpl"):
Modèle non trouvé pour nom "PseudoCode/blocs/ForceIndex.tpl".
Le nom a été interprété par cette TemplateLoader : MultiTemplateLoader(loader1 = FileTemplateLoader(baseDir="C:\StrategyQuantX\internal\extend\Code"), loader2 = FileTemplateLoader(baseDir="C:\StrategyQuantX\user\extend\Code")).

—-
Trace de pile FTL (“~” signifie lié à la nidification) :
    - Échec à : #include "blocs/" + blockKey + ".tpl"  [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 122, colonne 20]
    - Atteint par : @printBlock bloc?enfants[0], shift [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 107, colonne 9]
    - Atteint par : @printBlock c, shift [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlockChild" à la ligne 148, colonne 14]
    - Atteint par : @printBlockBloc enfant, "#Gauche#"  [en modèle "PseudoCode/blocs/IsGreater.tpl" à la ligne 1, colonne 2]
    - Atteint par : 1TP5Inclure "blocs/" + blockKey + ".tpl"  [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 122, colonne 20]
    - Atteint par : @printBlock bloc?enfants[0], shift [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 107, colonne 9]
    - Atteint par : @printBlock bloc [en modèle "PseudoCode/blocs/AND.tpl" à la ligne 5, colonne 38]
    - Atteint par : 1TP5Inclure "blocs/" + blockKey + ".tpl"  [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 122, colonne 20]
    - Atteint par : @printBlock bloc?enfants[0], shift [en modèle "PseudoCode/pseudoBlocks.inc" en macro "printBlock" (bloc d'impression) à la ligne 107, colonne 9]
    ... (Had 18 plus, caché pour tersènes) (caché 4 “~” lignes pour terseness)

Comment ajouter les modèles de code pour les différents langages de code de la stratégie ?

tomas262
Administrateur
Répondre à  Matthew Nowlis
28. 2. 2022 1:53 pm

Vous pouvez essayer d'importer le paquet à partir de cette page. https://strategyquant.com/codebase/forceindex/

Le pseudo-code et MT4 sont implémentés et fonctionnent pour moi lorsque je les ai testés.