mtcli 3.0.0.dev2__tar.gz → 3.2.0__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.0.0.dev2 → mtcli-3.2.0}/PKG-INFO +1 -1
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/commands/bars.py +118 -126
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/commands/conf.py +50 -50
- mtcli-3.2.0/mtcli/conecta.py +41 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/conf.py +101 -101
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/data/mt5.py +18 -20
- mtcli-3.2.0/mtcli/logger.py +45 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/conf_model.py +33 -33
- mtcli-3.2.0/mtcli/mt5_context.py +33 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/pyproject.toml +1 -1
- mtcli-3.0.0.dev2/mtcli/conecta.py +0 -18
- mtcli-3.0.0.dev2/mtcli/logger.py +0 -29
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/LICENSE +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/README.md +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/commands/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/commands/logs.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/data/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/data/base.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/data/csv.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/bar_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/bars_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/chart_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/consecutive_bars_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/rates_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/signals_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/models/unconsecutive_bar_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/mt.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugin.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/command.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/conf.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/models/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/models/model_agressao.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/views/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/agressao/views/view_agressao.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/command.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/conf.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/models/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/models/model_media_movel.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/tests/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/tests/test_mm.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/media_movel/tests/test_model_media_movel.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/command.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/conf.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/models/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/models/average_range_model.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/tests/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/range_medio/tests/test_rm.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/command.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/conf.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/models/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/models/model_average_volume.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/tests/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/plugins/volume_medio/tests/test_vm.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/__init__.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/close_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/full_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/high_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/low_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/min_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/open_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/ranges_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/rates_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/vars_view.py +0 -0
- {mtcli-3.0.0.dev2 → mtcli-3.2.0}/mtcli/views/volumes_view.py +0 -0
|
@@ -1,126 +1,118 @@
|
|
|
1
|
-
"""Exibe o gráfico de barras."""
|
|
2
|
-
|
|
3
|
-
import click
|
|
4
|
-
|
|
5
|
-
from mtcli import conf
|
|
6
|
-
from mtcli.models.rates_model import RatesModel
|
|
7
|
-
from mtcli.models.bars_model import BarsModel
|
|
8
|
-
from mtcli.views.full_view import FullView
|
|
9
|
-
from mtcli.views.min_view import MinView
|
|
10
|
-
from mtcli.views.ranges_view import RangesView
|
|
11
|
-
from mtcli.views.volumes_view import VolumesView
|
|
12
|
-
from mtcli.views.vars_view import VarsView
|
|
13
|
-
from mtcli.views.rates_view import RatesView
|
|
14
|
-
from mtcli.views.open_view import OpenView
|
|
15
|
-
from mtcli.views.high_view import HighView
|
|
16
|
-
from mtcli.views.low_view import LowView
|
|
17
|
-
from mtcli.views.close_view import CloseView
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@click.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
default=conf.
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
@click.option(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
view
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if views:
|
|
120
|
-
for view in views:
|
|
121
|
-
click.echo(view)
|
|
122
|
-
logger.info("Exibição do gráfico MT5 finalizada.")
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if __name__ == "__main__":
|
|
126
|
-
bars()
|
|
1
|
+
"""Exibe o gráfico de barras."""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from mtcli import conf
|
|
6
|
+
from mtcli.models.rates_model import RatesModel
|
|
7
|
+
from mtcli.models.bars_model import BarsModel
|
|
8
|
+
from mtcli.views.full_view import FullView
|
|
9
|
+
from mtcli.views.min_view import MinView
|
|
10
|
+
from mtcli.views.ranges_view import RangesView
|
|
11
|
+
from mtcli.views.volumes_view import VolumesView
|
|
12
|
+
from mtcli.views.vars_view import VarsView
|
|
13
|
+
from mtcli.views.rates_view import RatesView
|
|
14
|
+
from mtcli.views.open_view import OpenView
|
|
15
|
+
from mtcli.views.high_view import HighView
|
|
16
|
+
from mtcli.views.low_view import LowView
|
|
17
|
+
from mtcli.views.close_view import CloseView
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.command(
|
|
21
|
+
"bars",
|
|
22
|
+
help="Mostra o gráfico de candles em texto para o ativo e período especificados.",
|
|
23
|
+
)
|
|
24
|
+
@click.argument("symbol")
|
|
25
|
+
@click.option(
|
|
26
|
+
"--view",
|
|
27
|
+
"-v",
|
|
28
|
+
type=click.Choice(
|
|
29
|
+
[
|
|
30
|
+
"ch",
|
|
31
|
+
"m",
|
|
32
|
+
"hl",
|
|
33
|
+
"f",
|
|
34
|
+
"full",
|
|
35
|
+
"r",
|
|
36
|
+
"range",
|
|
37
|
+
"v",
|
|
38
|
+
"volume",
|
|
39
|
+
"va",
|
|
40
|
+
"percentual",
|
|
41
|
+
"oh",
|
|
42
|
+
"ohlc",
|
|
43
|
+
"o",
|
|
44
|
+
"open",
|
|
45
|
+
"h",
|
|
46
|
+
"high",
|
|
47
|
+
"l",
|
|
48
|
+
"low",
|
|
49
|
+
"c",
|
|
50
|
+
"close",
|
|
51
|
+
],
|
|
52
|
+
case_sensitive=False,
|
|
53
|
+
),
|
|
54
|
+
default=conf.view,
|
|
55
|
+
help="Formato de exibicao, default m. Opcoes: ch ou m - minima; f - completa; r - ranges; v - volumes; va - variações percentuais; oh - OHLC; o - aberturas; h - maximas; l - minimas; c - fechamentos",
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
"--period",
|
|
59
|
+
"-p",
|
|
60
|
+
type=click.Choice(conf.timeframes, case_sensitive=False),
|
|
61
|
+
default=conf.period,
|
|
62
|
+
help="Tempo grafico, default D1.",
|
|
63
|
+
)
|
|
64
|
+
@click.option(
|
|
65
|
+
"--count",
|
|
66
|
+
"-c",
|
|
67
|
+
type=int,
|
|
68
|
+
default=conf.periodos,
|
|
69
|
+
help="Quantidade de barras, default 20.",
|
|
70
|
+
)
|
|
71
|
+
@click.option(
|
|
72
|
+
"--date", "-d", default=conf.date, help="Data para intraday, formato AAAA-MM-DD."
|
|
73
|
+
)
|
|
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
|
+
@click.option(
|
|
77
|
+
"--volume",
|
|
78
|
+
"-vo",
|
|
79
|
+
type=click.Choice(["tick", "real"], case_sensitive=False),
|
|
80
|
+
default=conf.volume,
|
|
81
|
+
help="Tipo de volume, default tick.",
|
|
82
|
+
)
|
|
83
|
+
def bars(symbol, view, period, count, date, numerator, show_date, volume):
|
|
84
|
+
"""Exibe o grafico do MetaTrader 5."""
|
|
85
|
+
period = period.lower()
|
|
86
|
+
view = view.lower()
|
|
87
|
+
rates = RatesModel(symbol, period, count).get_data()
|
|
88
|
+
bars = BarsModel(rates, date).get_bars()
|
|
89
|
+
views = []
|
|
90
|
+
if view in ["m", "ch", "hl"]: # máximas e mínimas
|
|
91
|
+
views = MinView(bars, count, period, date, numerator, show_date).views()
|
|
92
|
+
elif view in ["r", "range"]: # ranges
|
|
93
|
+
views = RangesView(bars, count, period, date, numerator, show_date).views()
|
|
94
|
+
elif view in ["oh", "ohlc"]: # OHLC
|
|
95
|
+
views = RatesView(bars, count, period, date, numerator, show_date).views()
|
|
96
|
+
elif view in ["va", "percentual"]: # variações percentuais
|
|
97
|
+
views = VarsView(bars, count, period, date, numerator, show_date).views()
|
|
98
|
+
elif view in ["o", "open"]: # abertura
|
|
99
|
+
views = OpenView(bars, count, period, date, numerator, show_date).views()
|
|
100
|
+
elif view in ["h", "high"]: # máximas
|
|
101
|
+
views = HighView(bars, count, period, date, numerator, show_date).views()
|
|
102
|
+
elif view in ["l", "low"]: # mínimas
|
|
103
|
+
views = LowView(bars, count, period, date, numerator, show_date).views()
|
|
104
|
+
elif view in ["c", "close"]: # fechamentos
|
|
105
|
+
views = CloseView(bars, count, period, date, numerator, show_date).views()
|
|
106
|
+
elif view in ["v", "volume"]: # volumes
|
|
107
|
+
views = VolumesView(
|
|
108
|
+
bars, count, period, date, numerator, show_date, volume
|
|
109
|
+
).views()
|
|
110
|
+
else: # completo
|
|
111
|
+
views = FullView(bars, count, period, date, numerator, show_date).views()
|
|
112
|
+
if views:
|
|
113
|
+
for view in views:
|
|
114
|
+
click.echo(view)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
bars()
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
"""Gerencia configurações registradas no mtcli.ini."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import click
|
|
5
|
-
from mtcli.models.conf_model import ConfModel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@click.command(
|
|
9
|
-
"conf",
|
|
10
|
-
help="Gerencia as configurações salvas no arquivo mtcli.ini (adicionar, editar, listar).",
|
|
11
|
-
)
|
|
12
|
-
@click.option("--list", "list_", is_flag=True, help="Lista todas as configurações.")
|
|
13
|
-
@click.option("--set", "set_", nargs=2, help="Define o valor de uma configuração.")
|
|
14
|
-
@click.option("--get", help="Exibe o valor de uma configuração.")
|
|
15
|
-
@click.option("--reset", is_flag=True, help="Redefine as configurações padrão.")
|
|
16
|
-
def conf(list_, set_, get, reset):
|
|
17
|
-
"""Gerencia configurações registradas no mtcli.ini."""
|
|
18
|
-
conf = ConfModel("mtcli.ini")
|
|
19
|
-
config = conf.carregar()
|
|
20
|
-
|
|
21
|
-
if list_:
|
|
22
|
-
for key in config["DEFAULT"]:
|
|
23
|
-
click.echo(f"{key} = {config['DEFAULT'][key]}")
|
|
24
|
-
|
|
25
|
-
elif set_:
|
|
26
|
-
chave, valor = set_
|
|
27
|
-
config["DEFAULT"][chave] = valor
|
|
28
|
-
conf.salvar(config)
|
|
29
|
-
click.echo(f"Configuração '{chave}' definida como '{valor}'.")
|
|
30
|
-
|
|
31
|
-
elif get:
|
|
32
|
-
valor = config["DEFAULT"].get(get)
|
|
33
|
-
if valor is not None:
|
|
34
|
-
click.echo(f"{get} = {valor}")
|
|
35
|
-
else:
|
|
36
|
-
click.echo(f"Configuração '{get}' não encontrada.")
|
|
37
|
-
|
|
38
|
-
elif reset:
|
|
39
|
-
config["DEFAULT"].clear()
|
|
40
|
-
conf.salvar(config)
|
|
41
|
-
click.echo("Configurações redefinidas.")
|
|
42
|
-
|
|
43
|
-
else:
|
|
44
|
-
click.echo(
|
|
45
|
-
"Nenhuma opção fornecida. Use --help para ver as opções disponíveis."
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if __name__ == "__main__":
|
|
50
|
-
conf()
|
|
1
|
+
"""Gerencia configurações registradas no mtcli.ini."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import click
|
|
5
|
+
from mtcli.models.conf_model import ConfModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.command(
|
|
9
|
+
"conf",
|
|
10
|
+
help="Gerencia as configurações salvas no arquivo mtcli.ini (adicionar, editar, listar).",
|
|
11
|
+
)
|
|
12
|
+
@click.option("--list", "list_", is_flag=True, help="Lista todas as configurações.")
|
|
13
|
+
@click.option("--set", "set_", nargs=2, help="Define o valor de uma configuração.")
|
|
14
|
+
@click.option("--get", help="Exibe o valor de uma configuração.")
|
|
15
|
+
@click.option("--reset", is_flag=True, help="Redefine as configurações padrão.")
|
|
16
|
+
def conf(list_, set_, get, reset):
|
|
17
|
+
"""Gerencia configurações registradas no mtcli.ini."""
|
|
18
|
+
conf = ConfModel("mtcli.ini")
|
|
19
|
+
config = conf.carregar()
|
|
20
|
+
|
|
21
|
+
if list_:
|
|
22
|
+
for key in config["DEFAULT"]:
|
|
23
|
+
click.echo(f"{key} = {config['DEFAULT'][key]}")
|
|
24
|
+
|
|
25
|
+
elif set_:
|
|
26
|
+
chave, valor = set_
|
|
27
|
+
config["DEFAULT"][chave] = valor
|
|
28
|
+
conf.salvar(config)
|
|
29
|
+
click.echo(f"Configuração '{chave}' definida como '{valor}'.")
|
|
30
|
+
|
|
31
|
+
elif get:
|
|
32
|
+
valor = config["DEFAULT"].get(get)
|
|
33
|
+
if valor is not None:
|
|
34
|
+
click.echo(f"{get} = {valor}")
|
|
35
|
+
else:
|
|
36
|
+
click.echo(f"Configuração '{get}' não encontrada.")
|
|
37
|
+
|
|
38
|
+
elif reset:
|
|
39
|
+
config["DEFAULT"].clear()
|
|
40
|
+
conf.salvar(config)
|
|
41
|
+
click.echo("Configurações redefinidas.")
|
|
42
|
+
|
|
43
|
+
else:
|
|
44
|
+
click.echo(
|
|
45
|
+
"Nenhuma opção fornecida. Use --help para ver as opções disponíveis."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
if __name__ == "__main__":
|
|
50
|
+
conf()
|
|
@@ -0,0 +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,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 = 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
|
+
]
|
|
@@ -5,11 +5,11 @@ from datetime import datetime
|
|
|
5
5
|
import MetaTrader5 as mt5
|
|
6
6
|
|
|
7
7
|
from mtcli.logger import setup_logger
|
|
8
|
-
from mtcli.
|
|
8
|
+
from mtcli.mt5_context import mt5_conexao
|
|
9
9
|
|
|
10
10
|
from .base import DataSourceBase
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
log = setup_logger()
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class MT5DataSource(DataSourceBase):
|
|
@@ -18,8 +18,8 @@ class MT5DataSource(DataSourceBase):
|
|
|
18
18
|
def get_data(self, symbol, period, count=100):
|
|
19
19
|
"""Retorna uma lista de lista de cotações do MetaTrader."""
|
|
20
20
|
period = period.upper()
|
|
21
|
-
|
|
22
|
-
f"Iniciando coleta de dados via API MT5: {symbol} no
|
|
21
|
+
log.info(
|
|
22
|
+
f"Iniciando coleta de dados via API MT5: {symbol} no timeframe {period}"
|
|
23
23
|
)
|
|
24
24
|
tf_map = {
|
|
25
25
|
"M1": mt5.TIMEFRAME_M1,
|
|
@@ -46,12 +46,9 @@ class MT5DataSource(DataSourceBase):
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
if period.upper() not in tf_map:
|
|
49
|
-
|
|
49
|
+
log.error(f"Timeframe inválido: {period}.")
|
|
50
50
|
raise ValueError(f"Timeframe '{period}' inválido.")
|
|
51
51
|
|
|
52
|
-
conectar()
|
|
53
|
-
|
|
54
|
-
# Verifica corretoras B3 e aplica tratamento a symbol
|
|
55
52
|
corretoras_b3 = [
|
|
56
53
|
"clear",
|
|
57
54
|
"xp",
|
|
@@ -61,21 +58,22 @@ class MT5DataSource(DataSourceBase):
|
|
|
61
58
|
"btg",
|
|
62
59
|
"toro",
|
|
63
60
|
]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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}."
|
|
69
71
|
)
|
|
70
|
-
logger.info(
|
|
71
|
-
f"Finalizada verificação da corretora para tratar symbol: {symbol}."
|
|
72
|
-
)
|
|
73
72
|
|
|
74
|
-
|
|
75
|
-
shutdown()
|
|
73
|
+
rates = mt5.copy_rates_from_pos(symbol, tf_map[period], 0, count)
|
|
76
74
|
|
|
77
75
|
if rates is None:
|
|
78
|
-
|
|
76
|
+
log.warning("Nenum dado retornado da API MT5.")
|
|
79
77
|
raise ValueError("Nenhum dado retornado da API MT5.")
|
|
80
78
|
|
|
81
79
|
result = []
|
|
@@ -95,5 +93,5 @@ class MT5DataSource(DataSourceBase):
|
|
|
95
93
|
]
|
|
96
94
|
)
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
log.info("Coleta de dados via API MT5 finalizada.")
|
|
99
97
|
return result
|
|
@@ -0,0 +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,33 +1,33 @@
|
|
|
1
|
-
"""Modelo de configurações."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import configparser
|
|
5
|
-
from configparser import MissingSectionHeaderError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class ConfigFormatError(Exception):
|
|
9
|
-
pass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class ConfModel:
|
|
13
|
-
|
|
14
|
-
def __init__(self, configuracoes):
|
|
15
|
-
self.CONFIG_PATH = os.path.abspath(configuracoes)
|
|
16
|
-
|
|
17
|
-
def carregar(self):
|
|
18
|
-
config = configparser.ConfigParser()
|
|
19
|
-
if os.path.exists(self.CONFIG_PATH):
|
|
20
|
-
try:
|
|
21
|
-
config.read(self.CONFIG_PATH)
|
|
22
|
-
except MissingSectionHeaderError:
|
|
23
|
-
raise ConfigFormatError(
|
|
24
|
-
f"Erro: o arquivo '{self.CONFIG_PATH}' não contém seções válidas.\n"
|
|
25
|
-
"Certifique-se de que ele está no formato correto:\n[padrao]\nCHAVE=valor"
|
|
26
|
-
)
|
|
27
|
-
else:
|
|
28
|
-
config["DEFAULT"] = {}
|
|
29
|
-
return config
|
|
30
|
-
|
|
31
|
-
def salvar(self, config):
|
|
32
|
-
with open(self.CONFIG_PATH, "w") as f:
|
|
33
|
-
config.write(f)
|
|
1
|
+
"""Modelo de configurações."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import configparser
|
|
5
|
+
from configparser import MissingSectionHeaderError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ConfigFormatError(Exception):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConfModel:
|
|
13
|
+
|
|
14
|
+
def __init__(self, configuracoes):
|
|
15
|
+
self.CONFIG_PATH = os.path.abspath(configuracoes)
|
|
16
|
+
|
|
17
|
+
def carregar(self):
|
|
18
|
+
config = configparser.ConfigParser()
|
|
19
|
+
if os.path.exists(self.CONFIG_PATH):
|
|
20
|
+
try:
|
|
21
|
+
config.read(self.CONFIG_PATH)
|
|
22
|
+
except MissingSectionHeaderError:
|
|
23
|
+
raise ConfigFormatError(
|
|
24
|
+
f"Erro: o arquivo '{self.CONFIG_PATH}' não contém seções válidas.\n"
|
|
25
|
+
"Certifique-se de que ele está no formato correto:\n[padrao]\nCHAVE=valor"
|
|
26
|
+
)
|
|
27
|
+
else:
|
|
28
|
+
config["DEFAULT"] = {}
|
|
29
|
+
return config
|
|
30
|
+
|
|
31
|
+
def salvar(self, config):
|
|
32
|
+
with open(self.CONFIG_PATH, "w") as f:
|
|
33
|
+
config.write(f)
|
|
@@ -0,0 +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,18 +0,0 @@
|
|
|
1
|
-
"""Módulo de conexão com o MetaTrader 5."""
|
|
2
|
-
|
|
3
|
-
import MetaTrader5 as mt5
|
|
4
|
-
from mtcli.logger import setup_logger
|
|
5
|
-
|
|
6
|
-
logger = setup_logger()
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def conectar():
|
|
10
|
-
if not mt5.initialize():
|
|
11
|
-
click.echo(f"❌ Erro ao conectar ao MT5: {mt5.last_error()}")
|
|
12
|
-
logger.error(f"Erro ao conectar ao MT5: {mt5.last_error()}")
|
|
13
|
-
exit()
|
|
14
|
-
return True
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def shutdown():
|
|
18
|
-
mt5.shutdown()
|
mtcli-3.0.0.dev2/mtcli/logger.py
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"""Módulo de logging."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import logging
|
|
5
|
-
from logging.handlers import RotatingFileHandler
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def setup_logger(name="mtcli"):
|
|
9
|
-
if os.name == "nt":
|
|
10
|
-
base_dir = os.getenv("APPDATA", os.path.expanduser("~"))
|
|
11
|
-
log_dir = os.path.join(base_dir, name, "logs")
|
|
12
|
-
else:
|
|
13
|
-
base_dir = os.path.expanduser("~/.local/share")
|
|
14
|
-
log_dir = os.path.join(base_dir, name, "logs")
|
|
15
|
-
|
|
16
|
-
os.makedirs(log_dir, exist_ok=True)
|
|
17
|
-
log_path = os.path.join(log_dir, f"{name}.log")
|
|
18
|
-
|
|
19
|
-
handler = RotatingFileHandler(log_path, maxBytes=1_000_000, backupCount=3)
|
|
20
|
-
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
|
|
21
|
-
handler.setFormatter(formatter)
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger(name)
|
|
24
|
-
logger.setLevel(logging.DEBUG)
|
|
25
|
-
if not logger.handlers:
|
|
26
|
-
logger.addHandler(handler)
|
|
27
|
-
logger.propagate = False
|
|
28
|
-
|
|
29
|
-
return logger
|
|
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
|