mtcli 3.2.0.dev2__tar.gz → 3.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/PKG-INFO +1 -1
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/commands/bars.py +42 -13
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/conecta.py +41 -41
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/conf.py +101 -101
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/data/mt5.py +97 -97
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/logger.py +45 -45
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/mt5_context.py +33 -33
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/pyproject.toml +1 -1
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/LICENSE +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/README.md +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/commands/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/commands/conf.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/commands/logs.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/data/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/data/base.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/data/csv.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/bar_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/bars_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/chart_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/conf_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/consecutive_bars_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/rates_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/signals_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/models/unconsecutive_bar_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/mt.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugin.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/command.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/conf.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/models/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/models/model_agressao.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/views/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/agressao/views/view_agressao.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/command.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/conf.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/models/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/command.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/conf.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/models/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/command.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/conf.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/__init__.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/close_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/full_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/high_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/low_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/min_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/open_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/ranges_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/rates_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/vars_view.py +0 -0
- {mtcli-3.2.0.dev2 → mtcli-3.2.1}/mtcli/views/volumes_view.py +0 -0
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import click
|
|
4
4
|
|
|
5
|
-
from mtcli import conf
|
|
6
5
|
from mtcli.models.rates_model import RatesModel
|
|
7
6
|
from mtcli.models.bars_model import BarsModel
|
|
8
7
|
from mtcli.views.full_view import FullView
|
|
@@ -15,6 +14,14 @@ from mtcli.views.open_view import OpenView
|
|
|
15
14
|
from mtcli.views.high_view import HighView
|
|
16
15
|
from mtcli.views.low_view import LowView
|
|
17
16
|
from mtcli.views.close_view import CloseView
|
|
17
|
+
from mtcli.conf import (
|
|
18
|
+
VIEW,
|
|
19
|
+
PERIOD,
|
|
20
|
+
BARS,
|
|
21
|
+
DATE,
|
|
22
|
+
VOLUME,
|
|
23
|
+
TIMEFRAMES,
|
|
24
|
+
)
|
|
18
25
|
|
|
19
26
|
|
|
20
27
|
@click.command(
|
|
@@ -51,34 +58,56 @@ from mtcli.views.close_view import CloseView
|
|
|
51
58
|
],
|
|
52
59
|
case_sensitive=False,
|
|
53
60
|
),
|
|
54
|
-
default=
|
|
55
|
-
|
|
61
|
+
default=VIEW,
|
|
62
|
+
show_default=True,
|
|
63
|
+
help="Formato de exibicao. Opcoes: minima ou HL - minima; f - completa; r - ranges; v - volumes; va - variações percentuais; oh - OHLC; o - aberturas; h - maximas; l - minimas; c - fechamentos.",
|
|
56
64
|
)
|
|
57
65
|
@click.option(
|
|
58
66
|
"--period",
|
|
59
67
|
"-p",
|
|
60
|
-
type=click.Choice(
|
|
61
|
-
default=
|
|
62
|
-
|
|
68
|
+
type=click.Choice(TIMEFRAMES, case_sensitive=False),
|
|
69
|
+
default=PERIOD,
|
|
70
|
+
show_default=True,
|
|
71
|
+
help="Timeframe das barras.",
|
|
63
72
|
)
|
|
64
73
|
@click.option(
|
|
65
74
|
"--count",
|
|
66
75
|
"-c",
|
|
67
76
|
type=int,
|
|
68
|
-
default=
|
|
69
|
-
|
|
77
|
+
default=BARS,
|
|
78
|
+
show_default=True,
|
|
79
|
+
help="Numero de barras.",
|
|
80
|
+
)
|
|
81
|
+
@click.option(
|
|
82
|
+
"--date",
|
|
83
|
+
"-d",
|
|
84
|
+
default=DATE,
|
|
85
|
+
show_default=True,
|
|
86
|
+
help="Data para intraday, formato AAAA-MM-DD.",
|
|
87
|
+
)
|
|
88
|
+
@click.option(
|
|
89
|
+
"--numerator",
|
|
90
|
+
"-n",
|
|
91
|
+
is_flag=True,
|
|
92
|
+
default=False,
|
|
93
|
+
show_default=True,
|
|
94
|
+
help="Ativa a numeracao das barras.",
|
|
70
95
|
)
|
|
71
96
|
@click.option(
|
|
72
|
-
"--
|
|
97
|
+
"--show-date",
|
|
98
|
+
"-sd",
|
|
99
|
+
is_flag=True,
|
|
100
|
+
default=False,
|
|
101
|
+
show_default=True,
|
|
102
|
+
help="Ativa a datacao das barras.",
|
|
73
103
|
)
|
|
74
|
-
@click.option("--numerator", "-n", is_flag=True, help="Ativa a numeracao das barras.")
|
|
75
|
-
@click.option("--show-date", "-sd", is_flag=True, help="Ativa a datacao das barras.")
|
|
76
104
|
@click.option(
|
|
77
105
|
"--volume",
|
|
78
106
|
"-vo",
|
|
79
107
|
type=click.Choice(["tick", "real"], case_sensitive=False),
|
|
80
|
-
default=
|
|
81
|
-
|
|
108
|
+
default=VOLUME,
|
|
109
|
+
show_default=True,
|
|
110
|
+
help="Tipo de volume.",
|
|
82
111
|
)
|
|
83
112
|
def bars(symbol, view, period, count, date, numerator, show_date, volume):
|
|
84
113
|
"""Exibe o grafico do MetaTrader 5."""
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Gerencia a conexão direta com o terminal MetaTrader 5.
|
|
3
|
-
Usado internamente pelo contexto mt5_conexao().
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import MetaTrader5 as mt5
|
|
7
|
-
from mtcli.logger import setup_logger
|
|
8
|
-
|
|
9
|
-
log = setup_logger()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def conectar() -> bool:
|
|
13
|
-
"""
|
|
14
|
-
Inicializa a conexão com o MetaTrader 5.
|
|
15
|
-
|
|
16
|
-
Retorna:
|
|
17
|
-
bool: True se conectado com sucesso, False caso contrário.
|
|
18
|
-
|
|
19
|
-
Levanta:
|
|
20
|
-
ConnectionError: se a inicialização do MT5 falhar.
|
|
21
|
-
"""
|
|
22
|
-
log.debug("Tentando inicializar conexao com MetaTrader 5...")
|
|
23
|
-
|
|
24
|
-
if not mt5.initialize():
|
|
25
|
-
erro = mt5.last_error()
|
|
26
|
-
log.error(f"Erro ao conectar ao MetaTrader 5: {erro}")
|
|
27
|
-
raise ConnectionError(f"Falha ao conectar ao MetaTrader 5: {erro}")
|
|
28
|
-
|
|
29
|
-
log.info("Conexao com MetaTrader 5 estabelecida com sucesso.")
|
|
30
|
-
return True
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def shutdown():
|
|
34
|
-
"""
|
|
35
|
-
Encerra a conexão com o MetaTrader 5 de forma segura.
|
|
36
|
-
"""
|
|
37
|
-
try:
|
|
38
|
-
mt5.shutdown()
|
|
39
|
-
log.debug("Conexao com MetaTrader 5 encerrada.")
|
|
40
|
-
except Exception as e:
|
|
41
|
-
log.error(f"Erro ao encerrar conexao com MetaTrader 5: {e}")
|
|
1
|
+
"""
|
|
2
|
+
Gerencia a conexão direta com o terminal MetaTrader 5.
|
|
3
|
+
Usado internamente pelo contexto mt5_conexao().
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import MetaTrader5 as mt5
|
|
7
|
+
from mtcli.logger import setup_logger
|
|
8
|
+
|
|
9
|
+
log = setup_logger()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def conectar() -> bool:
|
|
13
|
+
"""
|
|
14
|
+
Inicializa a conexão com o MetaTrader 5.
|
|
15
|
+
|
|
16
|
+
Retorna:
|
|
17
|
+
bool: True se conectado com sucesso, False caso contrário.
|
|
18
|
+
|
|
19
|
+
Levanta:
|
|
20
|
+
ConnectionError: se a inicialização do MT5 falhar.
|
|
21
|
+
"""
|
|
22
|
+
log.debug("Tentando inicializar conexao com MetaTrader 5...")
|
|
23
|
+
|
|
24
|
+
if not mt5.initialize():
|
|
25
|
+
erro = mt5.last_error()
|
|
26
|
+
log.error(f"Erro ao conectar ao MetaTrader 5: {erro}")
|
|
27
|
+
raise ConnectionError(f"Falha ao conectar ao MetaTrader 5: {erro}")
|
|
28
|
+
|
|
29
|
+
log.info("Conexao com MetaTrader 5 estabelecida com sucesso.")
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def shutdown():
|
|
34
|
+
"""
|
|
35
|
+
Encerra a conexão com o MetaTrader 5 de forma segura.
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
mt5.shutdown()
|
|
39
|
+
log.debug("Conexao com MetaTrader 5 encerrada.")
|
|
40
|
+
except Exception as e:
|
|
41
|
+
log.error(f"Erro ao encerrar conexao com MetaTrader 5: {e}")
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import MetaTrader5 as mt5
|
|
3
|
-
from mtcli.conecta import conectar, shutdown
|
|
4
|
-
from mtcli.models.conf_model import ConfModel
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
config = ConfModel("mtcli.ini").carregar()
|
|
8
|
-
|
|
9
|
-
section = "DEFAULT"
|
|
10
|
-
symbol = os.getenv("SYMBOL", config[section].get("symbol", fallback="WIN$N"))
|
|
11
|
-
digitos = int(os.getenv("DIGITOS", config[section].getint("digitos", fallback=2)))
|
|
12
|
-
period = os.getenv("PERIOD", config[section].get("period", fallback="D1"))
|
|
13
|
-
periodos = count = int(
|
|
14
|
-
os.getenv("COUNT", config[section].getint("count", fallback=999))
|
|
15
|
-
)
|
|
16
|
-
view = os.getenv("VIEW", config[section].get("view", fallback="ch"))
|
|
17
|
-
volume = os.getenv("VOLUME", config[section].get("volume", fallback="tick"))
|
|
18
|
-
date = os.getenv("DATE", config[section].get("date", fallback=""))
|
|
19
|
-
|
|
20
|
-
lateral = os.getenv("LATERAL", config[section].get("lateral", fallback="doji"))
|
|
21
|
-
alta = os.getenv("ALTA", config[section].get("alta", fallback="verde"))
|
|
22
|
-
baixa = os.getenv("BAIXA", config[section].get("baixa", fallback="vermelho"))
|
|
23
|
-
rompimento_alta = os.getenv(
|
|
24
|
-
"ROMPIMENTO_ALTA", config[section].get("rompimento_alta", fallback="c")
|
|
25
|
-
)
|
|
26
|
-
rompimento_baixa = os.getenv(
|
|
27
|
-
"ROMPIMENTO_BAIXA", config[section].get("rompimento_baixa", fallback="v")
|
|
28
|
-
)
|
|
29
|
-
percentual_rompimento = int(
|
|
30
|
-
os.getenv(
|
|
31
|
-
"PERCENTUAL_ROMPIMENTO",
|
|
32
|
-
config[section].getint("percentual_rompimento", fallback=50),
|
|
33
|
-
)
|
|
34
|
-
)
|
|
35
|
-
percentual_doji = int(
|
|
36
|
-
os.getenv("PERCENTUAL_DOJI", config[section].getint("percentual_doji", fallback=10))
|
|
37
|
-
)
|
|
38
|
-
up_bar = os.getenv("UP_BAR", config[section].get("up_bar", fallback="asc"))
|
|
39
|
-
down_bar = os.getenv("DOWN_BAR", config[section].get("down_bar", fallback="desc"))
|
|
40
|
-
inside_bar = os.getenv("INSIDE_BAR", config[section].get("inside_bar", fallback="ib"))
|
|
41
|
-
outside_bar = os.getenv(
|
|
42
|
-
"OUTSIDE_BAR", config[section].get("outside_bar", fallback="ob")
|
|
43
|
-
)
|
|
44
|
-
sombra_superior = os.getenv(
|
|
45
|
-
"SOMBRA_SUPERIOR", config[section].get("sombra_superior", fallback="top")
|
|
46
|
-
)
|
|
47
|
-
sombra_inferior = os.getenv(
|
|
48
|
-
"SOMBRA_INFERIOR", config[section].get("sombra_inferior", fallback="bottom")
|
|
49
|
-
)
|
|
50
|
-
data_source = dados = os.getenv("DADOS", config[section].get("dados", fallback="mt5"))
|
|
51
|
-
csv_path = mt5_pasta = os.getenv(
|
|
52
|
-
"MT5_PASTA", config[section].get("mt5_pasta", fallback="")
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def get_data_source():
|
|
57
|
-
from mtcli.data import CsvDataSource, MT5DataSource
|
|
58
|
-
|
|
59
|
-
if data_source.lower() == "csv":
|
|
60
|
-
return CsvDataSource()
|
|
61
|
-
elif data_source.lower() == "mt5":
|
|
62
|
-
return MT5DataSource()
|
|
63
|
-
else:
|
|
64
|
-
raise ValueError(f"Fonte de dados desconhecida: {data_source}")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if not csv_path:
|
|
68
|
-
conectar()
|
|
69
|
-
terminal_info = mt5.terminal_info()
|
|
70
|
-
if terminal_info is None:
|
|
71
|
-
raise RuntimeError("Não foi possível obter as informações do terminal.")
|
|
72
|
-
|
|
73
|
-
csv_path = terminal_info.data_path + "/MQL5/Files"
|
|
74
|
-
shutdown()
|
|
75
|
-
|
|
76
|
-
csv_path = csv_path.replace("\\", "/")
|
|
77
|
-
csv_path += "/"
|
|
78
|
-
|
|
79
|
-
timeframes = [
|
|
80
|
-
"mn1",
|
|
81
|
-
"w1",
|
|
82
|
-
"d1",
|
|
83
|
-
"h12",
|
|
84
|
-
"h8",
|
|
85
|
-
"h6",
|
|
86
|
-
"h4",
|
|
87
|
-
"h3",
|
|
88
|
-
"h2",
|
|
89
|
-
"h1",
|
|
90
|
-
"m30",
|
|
91
|
-
"m20",
|
|
92
|
-
"m15",
|
|
93
|
-
"m12",
|
|
94
|
-
"m10",
|
|
95
|
-
"m6",
|
|
96
|
-
"m5",
|
|
97
|
-
"m4",
|
|
98
|
-
"m3",
|
|
99
|
-
"m2",
|
|
100
|
-
"m1",
|
|
101
|
-
]
|
|
1
|
+
import os
|
|
2
|
+
import MetaTrader5 as mt5
|
|
3
|
+
from mtcli.conecta import conectar, shutdown
|
|
4
|
+
from mtcli.models.conf_model import ConfModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
config = ConfModel("mtcli.ini").carregar()
|
|
8
|
+
|
|
9
|
+
section = "DEFAULT"
|
|
10
|
+
symbol = os.getenv("SYMBOL", config[section].get("symbol", fallback="WIN$N"))
|
|
11
|
+
digitos = int(os.getenv("DIGITOS", config[section].getint("digitos", fallback=2)))
|
|
12
|
+
PERIOD = period = os.getenv("PERIOD", config[section].get("period", fallback="D1"))
|
|
13
|
+
BARS = periodos = count = int(
|
|
14
|
+
os.getenv("COUNT", config[section].getint("count", fallback=999))
|
|
15
|
+
)
|
|
16
|
+
VIEW = view = os.getenv("VIEW", config[section].get("view", fallback="ch"))
|
|
17
|
+
VOLUME = volume = os.getenv("VOLUME", config[section].get("volume", fallback="tick"))
|
|
18
|
+
DATE = date = os.getenv("DATE", config[section].get("date", fallback=""))
|
|
19
|
+
|
|
20
|
+
lateral = os.getenv("LATERAL", config[section].get("lateral", fallback="doji"))
|
|
21
|
+
alta = os.getenv("ALTA", config[section].get("alta", fallback="verde"))
|
|
22
|
+
baixa = os.getenv("BAIXA", config[section].get("baixa", fallback="vermelho"))
|
|
23
|
+
rompimento_alta = os.getenv(
|
|
24
|
+
"ROMPIMENTO_ALTA", config[section].get("rompimento_alta", fallback="c")
|
|
25
|
+
)
|
|
26
|
+
rompimento_baixa = os.getenv(
|
|
27
|
+
"ROMPIMENTO_BAIXA", config[section].get("rompimento_baixa", fallback="v")
|
|
28
|
+
)
|
|
29
|
+
percentual_rompimento = int(
|
|
30
|
+
os.getenv(
|
|
31
|
+
"PERCENTUAL_ROMPIMENTO",
|
|
32
|
+
config[section].getint("percentual_rompimento", fallback=50),
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
percentual_doji = int(
|
|
36
|
+
os.getenv("PERCENTUAL_DOJI", config[section].getint("percentual_doji", fallback=10))
|
|
37
|
+
)
|
|
38
|
+
up_bar = os.getenv("UP_BAR", config[section].get("up_bar", fallback="asc"))
|
|
39
|
+
down_bar = os.getenv("DOWN_BAR", config[section].get("down_bar", fallback="desc"))
|
|
40
|
+
inside_bar = os.getenv("INSIDE_BAR", config[section].get("inside_bar", fallback="ib"))
|
|
41
|
+
outside_bar = os.getenv(
|
|
42
|
+
"OUTSIDE_BAR", config[section].get("outside_bar", fallback="ob")
|
|
43
|
+
)
|
|
44
|
+
sombra_superior = os.getenv(
|
|
45
|
+
"SOMBRA_SUPERIOR", config[section].get("sombra_superior", fallback="top")
|
|
46
|
+
)
|
|
47
|
+
sombra_inferior = os.getenv(
|
|
48
|
+
"SOMBRA_INFERIOR", config[section].get("sombra_inferior", fallback="bottom")
|
|
49
|
+
)
|
|
50
|
+
data_source = dados = os.getenv("DADOS", config[section].get("dados", fallback="mt5"))
|
|
51
|
+
csv_path = mt5_pasta = os.getenv(
|
|
52
|
+
"MT5_PASTA", config[section].get("mt5_pasta", fallback="")
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_data_source():
|
|
57
|
+
from mtcli.data import CsvDataSource, MT5DataSource
|
|
58
|
+
|
|
59
|
+
if data_source.lower() == "csv":
|
|
60
|
+
return CsvDataSource()
|
|
61
|
+
elif data_source.lower() == "mt5":
|
|
62
|
+
return MT5DataSource()
|
|
63
|
+
else:
|
|
64
|
+
raise ValueError(f"Fonte de dados desconhecida: {data_source}")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if not csv_path:
|
|
68
|
+
conectar()
|
|
69
|
+
terminal_info = mt5.terminal_info()
|
|
70
|
+
if terminal_info is None:
|
|
71
|
+
raise RuntimeError("Não foi possível obter as informações do terminal.")
|
|
72
|
+
|
|
73
|
+
csv_path = terminal_info.data_path + "/MQL5/Files"
|
|
74
|
+
shutdown()
|
|
75
|
+
|
|
76
|
+
csv_path = csv_path.replace("\\", "/")
|
|
77
|
+
csv_path += "/"
|
|
78
|
+
|
|
79
|
+
TIMEFRAMES = timeframes = [
|
|
80
|
+
"mn1",
|
|
81
|
+
"w1",
|
|
82
|
+
"d1",
|
|
83
|
+
"h12",
|
|
84
|
+
"h8",
|
|
85
|
+
"h6",
|
|
86
|
+
"h4",
|
|
87
|
+
"h3",
|
|
88
|
+
"h2",
|
|
89
|
+
"h1",
|
|
90
|
+
"m30",
|
|
91
|
+
"m20",
|
|
92
|
+
"m15",
|
|
93
|
+
"m12",
|
|
94
|
+
"m10",
|
|
95
|
+
"m6",
|
|
96
|
+
"m5",
|
|
97
|
+
"m4",
|
|
98
|
+
"m3",
|
|
99
|
+
"m2",
|
|
100
|
+
"m1",
|
|
101
|
+
]
|
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
"""Módulo fonte de dados via API do MetaTrader 5."""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
|
|
5
|
-
import MetaTrader5 as mt5
|
|
6
|
-
|
|
7
|
-
from mtcli.logger import setup_logger
|
|
8
|
-
from mtcli.mt5_context import mt5_conexao
|
|
9
|
-
|
|
10
|
-
from .base import DataSourceBase
|
|
11
|
-
|
|
12
|
-
log = setup_logger()
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class MT5DataSource(DataSourceBase):
|
|
16
|
-
"""Fonte de dados via API do MetaTrader 5."""
|
|
17
|
-
|
|
18
|
-
def get_data(self, symbol, period, count=100):
|
|
19
|
-
"""Retorna uma lista de lista de cotações do MetaTrader."""
|
|
20
|
-
period = period.upper()
|
|
21
|
-
log.info(
|
|
22
|
-
f"Iniciando coleta de dados via API MT5: {symbol} no timeframe {period}"
|
|
23
|
-
)
|
|
24
|
-
tf_map = {
|
|
25
|
-
"M1": mt5.TIMEFRAME_M1,
|
|
26
|
-
"M2": mt5.TIMEFRAME_M2,
|
|
27
|
-
"M3": mt5.TIMEFRAME_M3,
|
|
28
|
-
"M4": mt5.TIMEFRAME_M4,
|
|
29
|
-
"M5": mt5.TIMEFRAME_M5,
|
|
30
|
-
"M6": mt5.TIMEFRAME_M6,
|
|
31
|
-
"M10": mt5.TIMEFRAME_M10,
|
|
32
|
-
"M12": mt5.TIMEFRAME_M12,
|
|
33
|
-
"M15": mt5.TIMEFRAME_M15,
|
|
34
|
-
"M20": mt5.TIMEFRAME_M20,
|
|
35
|
-
"M30": mt5.TIMEFRAME_M30,
|
|
36
|
-
"H1": mt5.TIMEFRAME_H1,
|
|
37
|
-
"H2": mt5.TIMEFRAME_H2,
|
|
38
|
-
"H3": mt5.TIMEFRAME_H3,
|
|
39
|
-
"H4": mt5.TIMEFRAME_H4,
|
|
40
|
-
"H6": mt5.TIMEFRAME_H6,
|
|
41
|
-
"H8": mt5.TIMEFRAME_H8,
|
|
42
|
-
"H12": mt5.TIMEFRAME_H12,
|
|
43
|
-
"D1": mt5.TIMEFRAME_D1,
|
|
44
|
-
"W1": mt5.TIMEFRAME_W1,
|
|
45
|
-
"MN1": mt5.TIMEFRAME_MN1,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if period.upper() not in tf_map:
|
|
49
|
-
log.error(f"Timeframe inválido: {period}.")
|
|
50
|
-
raise ValueError(f"Timeframe '{period}' inválido.")
|
|
51
|
-
|
|
52
|
-
corretoras_b3 = [
|
|
53
|
-
"clear",
|
|
54
|
-
"xp",
|
|
55
|
-
"rico",
|
|
56
|
-
"modal",
|
|
57
|
-
"terra",
|
|
58
|
-
"btg",
|
|
59
|
-
"toro",
|
|
60
|
-
]
|
|
61
|
-
|
|
62
|
-
with mt5_conexao():
|
|
63
|
-
for corretora in corretoras_b3:
|
|
64
|
-
symbol = (
|
|
65
|
-
symbol.upper()
|
|
66
|
-
if corretora in mt5.account_info().company.lower()
|
|
67
|
-
else symbol
|
|
68
|
-
)
|
|
69
|
-
log.info(
|
|
70
|
-
f"Finalizada verificacao da corretora para tratar symbol: {symbol}."
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
rates = mt5.copy_rates_from_pos(symbol, tf_map[period], 0, count)
|
|
74
|
-
|
|
75
|
-
if rates is None:
|
|
76
|
-
log.warning("Nenum dado retornado da API MT5.")
|
|
77
|
-
raise ValueError("Nenhum dado retornado da API MT5.")
|
|
78
|
-
|
|
79
|
-
result = []
|
|
80
|
-
for r in rates:
|
|
81
|
-
dt_str = (
|
|
82
|
-
r["time"].astype("datetime64[s]").item().strftime("%Y.%m.%d %H:%M:%S")
|
|
83
|
-
)
|
|
84
|
-
result.append(
|
|
85
|
-
[
|
|
86
|
-
dt_str,
|
|
87
|
-
r["open"],
|
|
88
|
-
r["high"],
|
|
89
|
-
r["low"],
|
|
90
|
-
r["close"],
|
|
91
|
-
r["tick_volume"],
|
|
92
|
-
r["real_volume"],
|
|
93
|
-
]
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
log.info("Coleta de dados via API MT5 finalizada.")
|
|
97
|
-
return result
|
|
1
|
+
"""Módulo fonte de dados via API do MetaTrader 5."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
import MetaTrader5 as mt5
|
|
6
|
+
|
|
7
|
+
from mtcli.logger import setup_logger
|
|
8
|
+
from mtcli.mt5_context import mt5_conexao
|
|
9
|
+
|
|
10
|
+
from .base import DataSourceBase
|
|
11
|
+
|
|
12
|
+
log = setup_logger()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MT5DataSource(DataSourceBase):
|
|
16
|
+
"""Fonte de dados via API do MetaTrader 5."""
|
|
17
|
+
|
|
18
|
+
def get_data(self, symbol, period, count=100):
|
|
19
|
+
"""Retorna uma lista de lista de cotações do MetaTrader."""
|
|
20
|
+
period = period.upper()
|
|
21
|
+
log.info(
|
|
22
|
+
f"Iniciando coleta de dados via API MT5: {symbol} no timeframe {period}"
|
|
23
|
+
)
|
|
24
|
+
tf_map = {
|
|
25
|
+
"M1": mt5.TIMEFRAME_M1,
|
|
26
|
+
"M2": mt5.TIMEFRAME_M2,
|
|
27
|
+
"M3": mt5.TIMEFRAME_M3,
|
|
28
|
+
"M4": mt5.TIMEFRAME_M4,
|
|
29
|
+
"M5": mt5.TIMEFRAME_M5,
|
|
30
|
+
"M6": mt5.TIMEFRAME_M6,
|
|
31
|
+
"M10": mt5.TIMEFRAME_M10,
|
|
32
|
+
"M12": mt5.TIMEFRAME_M12,
|
|
33
|
+
"M15": mt5.TIMEFRAME_M15,
|
|
34
|
+
"M20": mt5.TIMEFRAME_M20,
|
|
35
|
+
"M30": mt5.TIMEFRAME_M30,
|
|
36
|
+
"H1": mt5.TIMEFRAME_H1,
|
|
37
|
+
"H2": mt5.TIMEFRAME_H2,
|
|
38
|
+
"H3": mt5.TIMEFRAME_H3,
|
|
39
|
+
"H4": mt5.TIMEFRAME_H4,
|
|
40
|
+
"H6": mt5.TIMEFRAME_H6,
|
|
41
|
+
"H8": mt5.TIMEFRAME_H8,
|
|
42
|
+
"H12": mt5.TIMEFRAME_H12,
|
|
43
|
+
"D1": mt5.TIMEFRAME_D1,
|
|
44
|
+
"W1": mt5.TIMEFRAME_W1,
|
|
45
|
+
"MN1": mt5.TIMEFRAME_MN1,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if period.upper() not in tf_map:
|
|
49
|
+
log.error(f"Timeframe inválido: {period}.")
|
|
50
|
+
raise ValueError(f"Timeframe '{period}' inválido.")
|
|
51
|
+
|
|
52
|
+
corretoras_b3 = [
|
|
53
|
+
"clear",
|
|
54
|
+
"xp",
|
|
55
|
+
"rico",
|
|
56
|
+
"modal",
|
|
57
|
+
"terra",
|
|
58
|
+
"btg",
|
|
59
|
+
"toro",
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
with mt5_conexao():
|
|
63
|
+
for corretora in corretoras_b3:
|
|
64
|
+
symbol = (
|
|
65
|
+
symbol.upper()
|
|
66
|
+
if corretora in mt5.account_info().company.lower()
|
|
67
|
+
else symbol
|
|
68
|
+
)
|
|
69
|
+
log.info(
|
|
70
|
+
f"Finalizada verificacao da corretora para tratar symbol: {symbol}."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
rates = mt5.copy_rates_from_pos(symbol, tf_map[period], 0, count)
|
|
74
|
+
|
|
75
|
+
if rates is None:
|
|
76
|
+
log.warning("Nenum dado retornado da API MT5.")
|
|
77
|
+
raise ValueError("Nenhum dado retornado da API MT5.")
|
|
78
|
+
|
|
79
|
+
result = []
|
|
80
|
+
for r in rates:
|
|
81
|
+
dt_str = (
|
|
82
|
+
r["time"].astype("datetime64[s]").item().strftime("%Y.%m.%d %H:%M:%S")
|
|
83
|
+
)
|
|
84
|
+
result.append(
|
|
85
|
+
[
|
|
86
|
+
dt_str,
|
|
87
|
+
r["open"],
|
|
88
|
+
r["high"],
|
|
89
|
+
r["low"],
|
|
90
|
+
r["close"],
|
|
91
|
+
r["tick_volume"],
|
|
92
|
+
r["real_volume"],
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
log.info("Coleta de dados via API MT5 finalizada.")
|
|
97
|
+
return result
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from logging.handlers import RotatingFileHandler
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
LOG_DIR = os.path.join(os.path.expanduser("~"), ".mtcli")
|
|
6
|
-
os.makedirs(LOG_DIR, exist_ok=True)
|
|
7
|
-
LOG_FILE = os.path.join(LOG_DIR, "mtcli.log")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def setup_logger(name: str = "mtcli") -> logging.Logger:
|
|
11
|
-
"""Configura logger rotativo com saída em arquivo e console.
|
|
12
|
-
|
|
13
|
-
- Escreve logs em ~/.mtcli/mtcli.log (máx. 2 MB, 3 backups).
|
|
14
|
-
- Mostra logs também no console (stdout), capturáveis via pytest/caplog.
|
|
15
|
-
- Evita duplicar handlers.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(name)
|
|
19
|
-
logger.setLevel(logging.DEBUG)
|
|
20
|
-
|
|
21
|
-
formatter = logging.Formatter(
|
|
22
|
-
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
|
|
23
|
-
datefmt="%Y-%m-%d %H:%M:%S",
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
# === File handler rotativo ===
|
|
27
|
-
if not any(isinstance(h, RotatingFileHandler) for h in logger.handlers):
|
|
28
|
-
file_handler = RotatingFileHandler(LOG_FILE, maxBytes=2_000_000, backupCount=3)
|
|
29
|
-
file_handler.setFormatter(formatter)
|
|
30
|
-
logger.addHandler(file_handler)
|
|
31
|
-
|
|
32
|
-
# === Stream handler (console) ===
|
|
33
|
-
if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
|
|
34
|
-
console_handler = logging.StreamHandler()
|
|
35
|
-
console_handler.setFormatter(formatter)
|
|
36
|
-
logger.addHandler(console_handler)
|
|
37
|
-
|
|
38
|
-
# Permite que pytest caplog capture logs
|
|
39
|
-
logger.propagate = True
|
|
40
|
-
|
|
41
|
-
return logger
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# Inicializa logger padrão
|
|
45
|
-
log = setup_logger()
|
|
1
|
+
import logging
|
|
2
|
+
from logging.handlers import RotatingFileHandler
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
LOG_DIR = os.path.join(os.path.expanduser("~"), ".mtcli")
|
|
6
|
+
os.makedirs(LOG_DIR, exist_ok=True)
|
|
7
|
+
LOG_FILE = os.path.join(LOG_DIR, "mtcli.log")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def setup_logger(name: str = "mtcli") -> logging.Logger:
|
|
11
|
+
"""Configura logger rotativo com saída em arquivo e console.
|
|
12
|
+
|
|
13
|
+
- Escreve logs em ~/.mtcli/mtcli.log (máx. 2 MB, 3 backups).
|
|
14
|
+
- Mostra logs também no console (stdout), capturáveis via pytest/caplog.
|
|
15
|
+
- Evita duplicar handlers.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(name)
|
|
19
|
+
logger.setLevel(logging.DEBUG)
|
|
20
|
+
|
|
21
|
+
formatter = logging.Formatter(
|
|
22
|
+
"%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
|
|
23
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# === File handler rotativo ===
|
|
27
|
+
if not any(isinstance(h, RotatingFileHandler) for h in logger.handlers):
|
|
28
|
+
file_handler = RotatingFileHandler(LOG_FILE, maxBytes=2_000_000, backupCount=3)
|
|
29
|
+
file_handler.setFormatter(formatter)
|
|
30
|
+
logger.addHandler(file_handler)
|
|
31
|
+
|
|
32
|
+
# === Stream handler (console) ===
|
|
33
|
+
if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
|
|
34
|
+
console_handler = logging.StreamHandler()
|
|
35
|
+
console_handler.setFormatter(formatter)
|
|
36
|
+
logger.addHandler(console_handler)
|
|
37
|
+
|
|
38
|
+
# Permite que pytest caplog capture logs
|
|
39
|
+
logger.propagate = True
|
|
40
|
+
|
|
41
|
+
return logger
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Inicializa logger padrão
|
|
45
|
+
log = setup_logger()
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Gerencia o ciclo de conexão com o MetaTrader 5.
|
|
3
|
-
Fornece o contexto 'mt5_conexao' para uso seguro em blocos with.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
from contextlib import contextmanager
|
|
7
|
-
from mtcli.logger import setup_logger
|
|
8
|
-
from mtcli.conecta import conectar, shutdown
|
|
9
|
-
|
|
10
|
-
log = setup_logger()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@contextmanager
|
|
14
|
-
def mt5_conexao():
|
|
15
|
-
"""
|
|
16
|
-
Context manager para conexão com o MetaTrader 5.
|
|
17
|
-
- Chama `conectar()` ao entrar no contexto.
|
|
18
|
-
- Chama `shutdown()` ao sair.
|
|
19
|
-
- Loga falhas e garante fechamento seguro.
|
|
20
|
-
"""
|
|
21
|
-
try:
|
|
22
|
-
log.debug("Inicializando conexao com MetaTrader 5...")
|
|
23
|
-
conectar()
|
|
24
|
-
yield
|
|
25
|
-
except Exception as e:
|
|
26
|
-
log.error(f"Erro ao conectar ao MetaTrader 5: {e}")
|
|
27
|
-
raise
|
|
28
|
-
finally:
|
|
29
|
-
try:
|
|
30
|
-
shutdown()
|
|
31
|
-
log.debug("Conexao com MetaTrader 5 encerrada.")
|
|
32
|
-
except Exception as e:
|
|
33
|
-
log.error(f"Erro ao encerrar conexao MT5: {e}")
|
|
1
|
+
"""
|
|
2
|
+
Gerencia o ciclo de conexão com o MetaTrader 5.
|
|
3
|
+
Fornece o contexto 'mt5_conexao' para uso seguro em blocos with.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from mtcli.logger import setup_logger
|
|
8
|
+
from mtcli.conecta import conectar, shutdown
|
|
9
|
+
|
|
10
|
+
log = setup_logger()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@contextmanager
|
|
14
|
+
def mt5_conexao():
|
|
15
|
+
"""
|
|
16
|
+
Context manager para conexão com o MetaTrader 5.
|
|
17
|
+
- Chama `conectar()` ao entrar no contexto.
|
|
18
|
+
- Chama `shutdown()` ao sair.
|
|
19
|
+
- Loga falhas e garante fechamento seguro.
|
|
20
|
+
"""
|
|
21
|
+
try:
|
|
22
|
+
log.debug("Inicializando conexao com MetaTrader 5...")
|
|
23
|
+
conectar()
|
|
24
|
+
yield
|
|
25
|
+
except Exception as e:
|
|
26
|
+
log.error(f"Erro ao conectar ao MetaTrader 5: {e}")
|
|
27
|
+
raise
|
|
28
|
+
finally:
|
|
29
|
+
try:
|
|
30
|
+
shutdown()
|
|
31
|
+
log.debug("Conexao com MetaTrader 5 encerrada.")
|
|
32
|
+
except Exception as e:
|
|
33
|
+
log.error(f"Erro ao encerrar conexao MT5: {e}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|