Modèles = Gain de temps lors des paramétrages
Avant-propos
Description
Les 5 courbes du MACD (2 en haut, 3 au milieu), et la courbe du RSI (en bas) avec ses seuils.
MACD
:
Aléatoire-2
.Plots-0
.macd_seeder.yaml
.macd_yaml.py
.macd.py
de la classe Calcul
, contenant :
__init__()
, descr_signal()
et process()
.macd_seeder.yaml
./nodes/indicateurs/macd/macd_seeder.yaml
:
---
# Cas paramètres s'appliquent à tous les signaux d'entrée.
# Modifier le code (.yaml et .py) pour les individualiser.
Longue:
SMMA: # Moyenne mobile lissée.
nb_last: 12 # Nombre de valeurs actives
LWMA: # Moyenne mobile linéaire pondérée (Linear Weighted Moving Average, LWMA).
Ratio: 1.6 # Raison géométrique de la pondération.
Courte:
SMMA:
nb_last: 12
LWMA:
Ratio: 1.6
Signal:
SMMA:
nb_last: 12
LWMA:
Ratio: 1.6
macd_yaml.py
./nodes/indicateurs/macd/macd_yaml.py
:
from nodes.yaml_parent import YamlParent
class YamlParams(YamlParent):
def __init__(self, o_calc):
super().__init__(o_calc)
@property
def long_smma_nb_last(self):
return max(1, int(self.od_yaml.read(['Longue', 'SMMA', 'nb_last'], 2)))
@property
def long_lwma_ratio(self):
return max(.1, self.od_yaml.read(['Longue', 'LWMA', 'Ratio'], 1.2))
@property
def short_smma_nb_last(self):
return max(1, int(self.od_yaml.read(['Courte', 'SMMA', 'nb_last'], 2)))
@property
def short_lwma_ratio(self):
return max(.1, self.od_yaml.read(['Courte', 'LWMA', 'Ratio'], 1.2))
@property
def signal_smma_nb_last(self):
return max(1, int(self.od_yaml.read(['Signal', 'SMMA', 'nb_last'], 2)))
@property
def signal_lwma_ratio(self):
return max(.1, self.od_yaml.read(['Signal', 'LWMA', 'Ratio'], 1.2))
macd.py
de la classe Calcul
.
my_params()
et my_signals()
doivent être modifiées pour la prise en compte :
Original
.Original
en sortie.macd.py
complet avec ses 2 classes./nodes/indicateurs/macd/macd.py
# Imports externes.
import numpy as np
# Imports internes.
from pc.ctrl_node import CtrlNode, CtrlCalcul
from nodes.indicateurs.macd.macd_yaml import YamlParams
import functions.indicators as indic
d_datas = {
'name': 'MACD', # Label affiché sous l'icone.
'icon': 'macd.png', # Icone affichée.
}
class Node(CtrlNode):
def __init__(self, o_scene, s_id, pos):
super().__init__(o_scene, s_id, pos)
self.type = 'MACD'
self.setup()
def setup(self, child_file=__file__):
""" Listes des dictionnaires de base attribués aux sockets. """
self.l_short_circuits = [(0, 0), (0, 1)] # e0 -> s0 et e0 -> s1
super().setup(child_file)
@property
def ld_inputs(self):
return [
{ # Liste de 1 dictionnaire.
'label': 'Entrée',
'label_pos': (6, -10)
}
]
@property
def ld_outputs(self):
return [ # Liste de 2 dictionnaires.
{
'label': 'Sortie',
'label_pos': (-38, -10)
},
'sep', # Séparateur (facultatif).
{
'label': 'Oscillateur',
'label_pos': (-59, -10)
}
]
def my_params(self, context):
""" Code appelant : CtrlNode.dynamic_params() """
return {
'Original': True,
'Type de MM longue': ['SMA', {'values': ['SMA', 'EMA', 'SMMA', 'LWMA']}],
'Moyenne longue': 26,
'Type de MM courte': ['SMA', {'values': ['SMA', 'EMA', 'SMMA', 'LWMA']}],
'Moyenne courte': 12,
'Type de MM Signal': ['SMA', {'values': ['SMA', 'EMA', 'SMMA', 'LWMA']}],
'Signal': 9,
}
def my_signals(self, l_signals_in, num_socket_out):
""" Retourne un faisceau de signaux (l_signals) délivré à la sortie N° num_socket_out. """
l_signals = [[], []] # 2 sorties.
for signals_in in l_signals_in[0]:
typ_id_from, signal_ante_from, signal_now_from, signal_source_from, \
_, signal_title, typ_id, signal_ante, signal_source = signals_in
signal_source = self.join(signal_source_from, self.join(typ_id_from, signal_now_from), sep='\n')
typ_id, signal_ante = f'{self.type}{self.id}', self.join(signal_ante_from, signal_now_from)
""" Sortie 0 : 2 ou 3 signaux. """
if self.get_param([signal_title, 'Original'], True):
l_signals[0].append((typ_id, signal_ante, 'Original', signal_source))
signal_now = "Longue"
l_signals[0].append((typ_id, signal_ante, signal_now, signal_source))
signal_now = "Courte"
l_signals[0].append((typ_id, signal_ante, signal_now, signal_source))
""" Sortie 1 : 3 signaux. """
signal_now = '(Courte–Longue)' # !!! Attention, ce n'est pas le signe moins, mais un caractère spécial !!!
l_signals[1].append((typ_id, signal_ante, signal_now, signal_source))
signal_now = "Signal"
l_signals[1].append((typ_id, signal_ante, signal_now, signal_source))
signal_now = 'MACD'
l_signals[1].append((typ_id, signal_ante, signal_now, signal_source))
return l_signals[num_socket_out]
def refresh(self, l_keys):
""" Ex : l_keys = ["Paramètres du node 'Node0'", 'Signaux-1 : Sinus', 'Signal actif'] """
if self.need_update(l_keys):
""" Update (si l'état actif a changé) OU (si actif ET un des paramètres a changé). """
self.lo_sockets_out[0].to_update()
self.lo_sockets_out[1].to_update()
super().refresh(l_keys)
class Calcul(CtrlCalcul):
""" ********** Le code ci-dessous ne concerne pas le poste de contrôle, mais seulement les calculs. ********** """
def __init__(self):
super().__init__()
self.o_yaml = YamlParams(self)
def descr_signal(self, od_descr, val, root_key):
""" Voir docstring dans la classe-mère. """
""" Original. """
key_dock = f"{root_key}-Original"
od_descr.write(key_dock, dict())
""" Moyenne longue. """
typ = val["Type de MM longue"]
per_long = val['Moyenne longue']
key_dock = f"{root_key}-Longue"
od_descr.write([key_dock, 'typ'], typ)
od_descr.write([key_dock, 'period'], per_long)
""" Moyenne courte. """
typ = val["Type de MM courte"]
per_short = val['Moyenne courte']
key_dock = f"{root_key}-Courte"
od_descr.write([key_dock, 'typ'], typ)
od_descr.write([key_dock, 'period'], per_short)
""" Courte - Longue. """ # !!! Attention, ce n'est pas un tiret, mais un caractère spécial !!!
""" Les key_dock sont traitées par la suite par des split('-'). Le tiret ne doit donc pas être utilisé. """
key_dock = f"{root_key}-(Courte–Longue)" # (Courte{caractère spécial}Longue)
od_descr.write(key_dock, dict())
""" Signal. """
typ = val["Type de MM Signal"]
per_signal = val['Signal']
key_dock = f"{root_key}-Signal"
od_descr.write([key_dock, 'typ'], typ)
od_descr.write([key_dock, 'period'], per_signal)
""" MACD. """
key_dock = f"{root_key}-MACD"
od_descr.write(key_dock, dict())
def process(self):
""" Voir docstring dans la classe-mère. """
o_server = self.dt_servers_in['e0'][0]
od_macd = self.new_od()
l_keys = list()
self.np_array = np.empty((self.len_buffer, 6))
""" Parcours du dictionnaire de description des traitements. """
for key in self.od_descr.key_list():
""" Filtrage. """
l_key = key[0].split('-')
if key[-1] != 'actif' or self.od_descr.read(key) is not True or len(l_key) < 3:
continue
""" Une key_signal par signal d'entrée. """
key_signal = self.join(*l_key[:2])
""" Clé et valeur du dictionnaire de description. """
d_name = {'Long': 'long', 'Cour': 'short', 'Sign': 'signal'}
key_dock = key[:-1][0]
l_keys.append(key_dock)
signal_name = l_key[-1][:4]
if signal_name == 'Orig':
continue
val = self.od_descr.read(key_dock)
if signal_name in d_name:
typ, period = (val['typ'], val['period'])
od_macd.write([key_signal, d_name[signal_name], 'typ'], typ)
od_macd.write([key_signal, d_name[signal_name], 'period'], period)
if signal_name == 'Long':
od_macd.write([key_signal, 'long', 'nb_last'], min(period, self.o_yaml.long_smma_nb_last))
od_macd.write([key_signal, 'long', 'ratio'], self.o_yaml.long_lwma_ratio)
elif signal_name == 'Cour':
od_macd.write([key_signal, 'short', 'nb_last'], min(period, self.o_yaml.short_smma_nb_last))
od_macd.write([key_signal, 'short', 'ratio'], self.o_yaml.short_lwma_ratio)
elif signal_name == 'Sign':
od_macd.write([key_signal, 'signal', 'nb_last'], min(period, self.o_yaml.signal_smma_nb_last))
od_macd.write([key_signal, 'signal', 'ratio'], self.o_yaml.signal_lwma_ratio)
for key, d_val in od_macd.items():
vector_in = o_server.get_vector_in(key+'-*') # nparray.
t_macd = indic.macd(vector_in, d_val) # Tuple 5 valeurs.
for i, key_dock in enumerate(l_keys):
self.add_vector(key_dock, i, t_macd[i])
indic.macd()
. Il vous appartient de la créer./functions/indicators/macd()
:
def macd(v_signal, d_val):
""" Ordre des calculs des vecteurs : https://fr.wikipedia.org/wiki/MACD
1) v_long = mm(v_signal)
2) v_short = mm(v_signal)
3) v_diff = (court - long)
4) v_signal = mm(diff)
5) v_macd = (diff - signal) """
_long, _short, _diff, _signal, _macd = None, None, None, None, None,
# à coder ...
return v_signal, _long, _short, _diff, _signal, _macd
Ne pas oublier de modifier le code d'appel dans show_matplotlib.py
, à la fin : Remplacer 'MM' par 'MACD'
RSI
au graphe :
rsi_seeder.yaml
.rsi_yaml.py
.rsi.py
de la classe Calcul
, contenant :
__init__()
, descr_signal()
et process()
.rsi_seeder.yaml
./nodes/oscillateurs/rsi/rsi_seeder.yaml
: Créer un fichier vide.rsi_yaml.py
./nodes/oscillateurs/rsi/rsi_yaml.py
:
from nodes.yaml_parent import YamlParent
class YamlParams(YamlParent):
def __init__(self, o_calc):
super().__init__(o_calc)
rsi.py
de la classe Calcul
.
my_params()
doit être modifiée. Voici le fichier rsi.py
complet./nodes/oscillateurs/rsi/rsi.py
:
# Imports externes
import numpy as np
# Imports internes
import functions.indicators as indic
from pc.ctrl_node import CtrlNode, CtrlCalcul
from nodes.oscillateurs.rsi.rsi_yaml import YamlParams
d_datas = {
'name': 'RSI', # Label affiché sous l'icone.
'icon': 'rsi.png', # Icone affichée.
}
class Node(CtrlNode):
def __init__(self, o_scene, s_id, pos):
super().__init__(o_scene, s_id, pos)
self.type = 'RSI'
self.setup()
def setup(self, child_file=__file__):
self.l_short_circuits = [(0, 0)] # Entrée N°0 'court-circuitable' avec la sortie N°0.
super().setup(child_file)
@property
def ld_inputs(self):
return [{
'label': '',
'label_pos': (6, -10)
}]
@property
def ld_outputs(self):
return [{
'label': 'Sortie',
'label_pos': (-38, -10)
}]
def my_params(self, context):
# RSI(n) = 100 - (100 / 1 + (H(n)/B(n)))
# n est la période considérée
# H une moyenne des mouvements de prix à la hausse sur la période n
# B une moyenne des mouvements de prix à la baisse sur la période n.
return {
'Zone neutre': [70., {'step': 1, 'limits': (5, 95), 'suffix': '%'}],
"Périodes (sep=',')": '14',
}
def my_signals(self, l_signals_in, num_socket_out):
""" Faisceau de signaux (l_signals) délivré à la sortie. """
l_signals = list()
for signals_in in l_signals_in[0]:
typ_id_from, signal_ante_from, signal_now_from, signal_source_from, \
_, signal_title, typ_id, signal_ante, signal_source = signals_in
periods = [int(p) for p in self.get_param([signal_title, "Périodes (sep=',')"]).split(',')]
for period in periods:
signal_now = str(period)
signal_source = self.join(signal_source_from, self.join(typ_id_from, signal_now_from), sep='\n')
typ_id, signal_ante = f'{self.type}{self.id}', self.join(signal_ante_from, signal_now_from)
l_signals.append((typ_id, signal_ante, signal_now, signal_source))
return l_signals
def refresh(self, l_keys):
""" Code spécifique. """
if self.need_update(l_keys):
self.lo_sockets_out[0].to_update() # Une seule sortie, N° 0.
super().refresh(l_keys)
class Calcul(CtrlCalcul):
""" ********** Le code ci-dessous ne concerne pas le poste de contrôle, mais seulement les calculs. ********** """
def __init__(self):
super().__init__()
self.o_yaml = YamlParams(self)
def descr_signal(self, od_descr, val, root_key):
""" Voir docstring dans la classe-mère. """
""" Les clés 'low' et 'high' sont utilisées par matplotlib pour colorier les zônes extrêmes. """
l_periods = [int(p.strip()) for p in val["Périodes (sep=',')"].split(',')]
for period in l_periods:
key_dock = f"{root_key}-{period}"
""" Limites low et high. """
low = (100 - val['Zone neutre']) / 2
high = 100 - low
od_descr.write([key_dock, 'period'], period)
""" Variables 'low' et 'high' utilisables dans les paramètres étendus yaml. """
od_descr.write([key_dock, 'low'], low)
od_descr.write([key_dock, 'high'], high)
def process(self):
""" Voir docstring dans la classe-mère. """
""" len_array = Nombre de lignes dans le tableau numpy du node-serveur. """
o_server = self.dt_servers_in['e0'][0]
len_array = o_server.np_array.shape[0]
""" Parcours du dictionnaire de description des traitements. """
for key_dock, val in self.od_descr.items():
if key_dock.startswith(self.s_id+'-'):
if not val['actif']:
continue
vector_in = o_server.get_vector_in(key_dock)
if vector_in is None:
continue
period = val['period']
num_col_out = val['num_col']
vector = indic.rsi(vector_in, period)
vector_out = np.full((len_array, 1), np.nan)
vector_out[-vector.shape[0]:, 0] = vector[:]
""" Ajout du vecteur au tableau numpy. """
self.add_vector(key_dock, num_col_out, vector_out)
indic.rsi()
. Il vous appartient de la créer./functions/indicators/rsi()
:
def rsi(v_signal, period):
""" https://fr.wikipedia.org/wiki/Relative_strength_index
RS = H / B => RSI = 1 - (100 / (1 + RS)) <-- Normalisé à [0, 100]
"""
# à coder ...
""" Normalisation entre 0 et 100. """
return 100 - (100 / (1 + rs))
YamlParent
: self.my_file
.aleatoire_yaml.py > Calcul
.matplotlib_yaml.py > Calcul
.signaux_yaml.py > Calcul
.macd_yaml.py > Calcul
.moy_mobile_yaml.py > Calcul
.rsi_yaml.py > Calcul
.__file__
. Toutefois, il ne peut pas être factorisé, car il prend une valeur différente pour chaque fichier.self.models_dir
dans YamlParent
.fusion()
de la classe Dictionnary
.MatPlotLib.ini_watch()
, ajouter la liste des fichiers-modèles yaml.yaml_parent.py
./nodes/yaml_parent.py
:
""" https://yaml.org/spec/1.2/ """
# Imports externes
import yaml
import os
# Imports internes
from functions.utils import Utils, Dictionary
class YamlParent:
def __init__(self, o_calc, my_file):
self.o_calc = o_calc
self.yaml_file = None # Modifiable à tout moment, manuellement par l'utilisateur.
self.ut = Utils()
self.od_yaml = Dictionary()
self.models_dir = ''
if os.path.isfile(my_file):
self.models_dir = os.path.normpath(f"{os.path.dirname(my_file)}/models")
os.makedirs(self.models_dir, exist_ok=True)
def update_odyaml(self): # , od_params=None):
""" Mise à jour de od_yaml. """
try:
with open(self.yaml_file, 'r', encoding='utf-8') as yml:
self.od_yaml = Dictionary(yaml.safe_load(yml))
""" Fusion de modèles. """
if os.path.isdir(self.models_dir) and 'Models' in self.od_yaml:
self.update_from_models()
except (Exception,) as err:
if os.path.isfile(self.yaml_file):
print(f'Erreur dans le fichier {self.yaml_file}.\n{err}')
""" Fichier yaml absent ou incorrect => dictionnaire vide. """
self.od_yaml = Dictionary() # Vide.
def update_from_models(self):
l_models = self.od_yaml['Models']
try:
for model in l_models:
file = os.path.join(self.models_dir, model)
if os.path.isfile(file):
with open(file, 'r', encoding='utf-8') as yml:
d_model = yaml.safe_load(yml)
self.od_yaml.fusion(d_model)
except (Exception,):
pass
def get_od_dock(self):
return Dictionary(self.o_calc.od_calcs.read(self.o_calc.l_mykey))
def compile(self, l_keys, d_context):
"""
Exemple de code appelant :
d_context = {'msg': 'Bonjour les codeurs !'}
self.o_yaml.compile(d_context)
print(d_context['return_value'])
Exemple de code dans le fichier Yaml :
Figure:
Code: |
print(f'Je suis dans le code compilé.\n{msg}')
return_value = 'Terminé.'
En cas d'erreur de compilation, celle-ci peut être lue dans d_context['error'].
"""
yaml_code = self.od_yaml.read(l_keys)
try:
code = compile(yaml_code, '<string>', 'exec')
eval(code, d_context)
d_context['error'] = "Pas d'erreur."
except (Exception,) as err:
d_context['error'] = f'Erreur :\n{err}'
@staticmethod
def new_od(dic=None):
return Dictionary(dic)
*_yaml.py
à modifier. Voici rsi_yaml.py
à titre d'exemple : l'argument __file__
a été ajouté./nodes/oscillateurs/rsi/rsi_yaml.py
:
from nodes.yaml_parent import YamlParent
class YamlParams(YamlParent):
def __init__(self, o_calc):
super().__init__(o_calc, __file__)
☐ Faire de même pour les autres fichiers *_yaml.py.
/functions/utils.py > Dictionnary.fusion()
:
def fusion(self, d_added):
""" Les clés (et les valeurs) de d_added viennent s'ajouter à celle de self.
Si la même clé existe dans les 2 dictionnaires, celle de self est prioritaire. """
od_added = Dictionary(d_added)
l_keys = self.key_list() + od_added.key_list()
""" Dédoublonnage. """
s_keys = set()
for key in l_keys:
s_keys.add(tuple(key))
""" Update de self. """
for key in s_keys:
self.write(key, self.read(key, od_added.read(key)))
☐ Modèle yaml à titre d'exemple et de vérification.
/nodes/afficheurs/plots/models/subplots_60_20_20.yaml
:
---
# Le fichier appelant est prioritaire sur celui-ci. Annuler ses clés pour que celles-ci soient actives.
Fenêtre:
style: dark_background
rcParams:
toolbar: 'None' # 'None', 'toolbar2', 'toolmanager'
Entrée 0:
Réticule: [ both, dotted, C9 ] # Traits verticaux(x), horizontaux (y) ou les 2 (both).
Cadre: False
Axe abscisse:
ticks pos: False # bottom (défaut), top. ax.xaxis.tick_top() Position de l'abscisse : bottom, top, False (invisible)
Géométrie: # _ = valeur par défaut. Valeurs de 0 à 100.
taille: [0, 0, 100, 60] # En % : x, y (coin haut gauche), largeur, hauteur
marges: [6, 5, 0, 5] # En % : haut, droit, bas, gauche.
Entrée 1:
Légende:
# visible: False # True par défaut
loc: 'upper left' # Automatique par défaut
ncol: 2 # 1 par défaut
framealpha: .2 # 1 par défaut (opaque)
facecolor: '#ffe' # Transparent par défaut
edgecolor: '#333' # Bordure blanche par défaut
Réticule: [ both, dotted, C9 ] # Traits verticaux(x), horizontaux (y) ou les 2 (both).
Titre visible: False
Cadre: False
Axe abscisse:
ticks pos: False # bottom (défaut), top. ax.xaxis.tick_top() Position de l'abscisse : bottom, top, False (invisible)
Géométrie: # _ = valeur par défaut. Valeurs de 0 à 100.
taille: [0, 60, 100, 20] # En % : x, y (coin haut gauche), largeur, hauteur
marges: [0, 5, 0, 5] # En % : haut, droit, bas, gauche.
Axe ordonnée:
ticks pos: right # ax.yaxis.tick_left() Position de l'ordonnée : left, right, False (invisible)
ShareX: 0 # ax.sharex(ax_ante) - Axe x partagé avec le subplot 0 (Entrée 0)
Entrée 2:
Légende:
# visible: False # True par défaut
loc: 'upper left' # Automatique par défaut
ncol: 3 # 1 par défaut
framealpha: .2 # 1 par défaut (opaque)
facecolor: '#ffe' # Transparent par défaut
edgecolor: '#333' # Bordure blanche par défaut
Réticule: [ both, dotted, C9 ] # Traits verticaux(x), horizontaux (y) ou les 2 (both).
Titre visible: False
Cadre: False
Géométrie: # _ = valeur par défaut. Valeurs de 0 à 100.
taille: [0, 80, 100, 20] # En % : x, y (coin haut gauche), largeur, hauteur
marges: [0, 5, 2, 5] # En % : haut, droit, bas, gauche.
ShareX: 0 # ax.sharex(ax_ante) - Axe x partagé avec le subplot 0 (Entrée 0)
☐ Ajout, dans la méthode ini_watch()
, de la liste des fichiers-modèles yaml à surveiller.
ShowMatPlotLib.ini_watch()
:
def ini_watch(self):
""" Liste des éléments à surveiller : dossier {graphe} + dossiers {nodes} + fichiers .pkl + fichiers .yaml
- Le dossier est self.final_path, dans les backups.
- Les fichiers calc_*.pkl : un par entrée active de ce node d'affichage.
- Les fichiers .yaml : valeurs des clés 'Yaml file' dans les .pkl """
""" 1 - Dossier du graphe dans les backups :dossier-parent de tous les nodes du graphe. """
graph_path = os.path.normpath(f"{os.path.dirname(__file__).replace('show', 'backups')}/{self.graph_name}")
""" 2 - Liste des dossiers des nodes du graphe (qui contiennent des fichiers nécessaires aux calculs) """
l_folders = [graph_path] + [f'{graph_path}{os.sep}{node}' for node in os.listdir(graph_path) if
str(node).startswith('node')]
""" 3 - Liste des modèles yaml. """
l_models = list()
nodes_dir = os.path.dirname(__file__).replace('show', 'nodes')
for (folder, subfolder, files) in os.walk(nodes_dir):
if folder.endswith('models'):
for file in files:
f_include = os.path.join(folder, file)
l_models.append(f_include)
""" 4 - Liste des fichiers calc_*.pkl physiquement présents dans le dossier self.final_path """
l_watched_files = [os.path.normpath(f'{self.final_path}/{calc}') for calc in
os.listdir(self.final_path) if calc.startswith('calc_')]
""" 5 - Liste des fichiers .yaml : chaque od_calc (extrait du pkl) contient plusieurs clés 'Yaml file'.
On en fait une liste sans doublons. """
s_yaml = set() # set() au lieu de list() pour s'affranchir des doublons.
for path, folder, l_files in os.walk(graph_path):
[s_yaml.add(os.path.normpath(f'{path}/{file}')) for file in l_files if os.path.splitext(file)[1] == '.yaml']
""" 6 - Concaténation des listes précédentes. """
l_watched_files += list(s_yaml) + l_models # Concaténation de 3 listes de fichiers.
l_files = l_folders + l_watched_files # Concaténation des listes : dossiers et fichiers.
""" 7 - Vérification des changements dans cette liste, mais non dans le contenu des fichiers. """
if self.l_files_ante != l_files:
self.l_files_ante = self.ut.deepcopy(l_files) # Mémorisation (copie profonde).
self.ut.watch_file(l_files, self.watch) # (Re)lancement de la surveillance.
self.dt.delay(self.watch, 10, l_watched_files)
Vérification
☐ Lancer show_matplotlib.py
, afin de s'assurer qu'il n'y a pas d'erreurs. Le cas échéant, les corriger.
☐ Tout refermer (main.py
et show_matplotlib.py
).
☐ Supprimer l'éventuel fichier /backups/MACD/node0/0-matplotlib.yaml
.
☐ Lancer uniquement le PC
.
☐ Cliquer sur le bouton Voir
de l'afficheur : La fenêtre des graphiques s'affiche.
☐ Cliquer sur le bouton des paramètres avancés de Plots-0
.
☐ Déclarer le modèle subplots_60_20_20.yaml
.
Pour que celui-ci soit pris en compte, il faut commenter :
☐ La clé Fenêtre
et toutes ses entrées.
☐ Les 3 clés Entrée0
, Entrée1
et Entrée2
.
☐ Enregistrer.
☐ Cliquer sur le bouton Fermer
puis Voir
pour la prise en compte des paramètres de la fenêtre.
Les couleurs inter-courbes :
☐ Remplacer Entrée 1
et Entrée 2
par ce code :
Entrée 1:
color between:
- MACD: # Juste pour l'utilisateur. Non utilisé dans le code.
# Nom des courbes : seule une partie du nom affiché dns le dockable.
courbes: [-MACD, 0] # Seules le 2 premières valeurs de la liste sont traitées.
couleurs: [ '#00f8', '#f006' ]
Entrée 2:
color between:
- RSI low:
courbes: [Normal, Normal$low] # Ici emploi de la variable 'low' du signal contenant 'Normal' dans son nom.
couleurs: [ '#0000', '#f008' ]
- RSI high:
courbes: [Normal, Normal$high] # Ici emploi de la variable 'low' du signal contenant 'Normal' dans son nom.
couleurs: [ '#00fc', '#0000' ]
Notez l'emploi de variables : ici, $low
et $high
(voir rsi.py > Calcul.descr_signal()
).
☐ Enregistrer.
Les couleurs inter-courbes s'affichent.
Bon coding et bon courage !
Snippets
Bonjour les codeurs !