mtcli 4.0.0.dev0__py3-none-any.whl → 4.0.0.dev1__py3-none-any.whl

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.
Files changed (93) hide show
  1. mtcli/__main__.py +3 -0
  2. mtcli/cli.py +51 -0
  3. mtcli/commands/bars.py +75 -153
  4. mtcli/commands/conf.py +35 -41
  5. mtcli/commands/doctor.py +194 -0
  6. mtcli/conf.py +125 -96
  7. mtcli/config_registre.py +41 -0
  8. mtcli/controllers/__init__.py +0 -0
  9. mtcli/controllers/bars_controller.py +75 -0
  10. mtcli/data/base.py +20 -9
  11. mtcli/data/csv.py +63 -31
  12. mtcli/data/factory.py +29 -0
  13. mtcli/data/mt5.py +123 -70
  14. mtcli/domain/__init__.py +0 -0
  15. mtcli/domain/bar.py +40 -0
  16. mtcli/domain/calculations.py +6 -0
  17. mtcli/domain/config.py +4 -0
  18. mtcli/domain/pattern.py +10 -0
  19. mtcli/domain/price.py +13 -0
  20. mtcli/domain/result.py +8 -0
  21. mtcli/domain/session.py +11 -0
  22. mtcli/domain/structure.py +7 -0
  23. mtcli/domain/symbol.py +10 -0
  24. mtcli/domain/tick.py +9 -0
  25. mtcli/domain/timeframe.py +102 -0
  26. mtcli/logger.py +136 -46
  27. mtcli/models/bars_model.py +175 -21
  28. mtcli/models/rate_model.py +71 -0
  29. mtcli/models_legado/__init__.py +1 -0
  30. mtcli/{models → models_legado}/bar_model.py +105 -105
  31. mtcli/models_legado/bars_model.py +25 -0
  32. mtcli/models_legado/chart.py +26 -0
  33. mtcli/models_legado/consecutive.py +29 -0
  34. mtcli/{models → models_legado}/rates_model.py +1 -1
  35. mtcli/plugin.py +18 -9
  36. mtcli/plugin_loader.py +123 -0
  37. mtcli/plugin_manager.py +27 -0
  38. mtcli/plugins/exemplo.py-dist +30 -0
  39. mtcli/plugins/media_movel/__init__.py +5 -3
  40. mtcli/plugins/media_movel/cli.py +73 -0
  41. mtcli/plugins/media_movel/conf.py +75 -4
  42. mtcli/plugins/media_movel/controller.py +43 -0
  43. mtcli/plugins/media_movel/tests/test_mm.py +13 -13
  44. mtcli/plugins/media_movel/tests/test_model_media_movel.py +9 -9
  45. mtcli/plugins/media_movel/view.py +15 -0
  46. mtcli/plugins/range_medio/__init__.py +5 -2
  47. mtcli/plugins/range_medio/{command.py → cli.py} +32 -33
  48. mtcli/plugins/range_medio/conf.py +67 -1
  49. mtcli/plugins/range_medio/models/average_range_model.py +29 -29
  50. mtcli/plugins/range_medio/tests/test_rm.py +11 -11
  51. mtcli/plugins/volume_medio/__init__.py +5 -3
  52. mtcli/plugins/volume_medio/{command.py → cli.py} +41 -42
  53. mtcli/plugins/volume_medio/conf.py +67 -1
  54. mtcli/plugins/volume_medio/models/model_average_volume.py +31 -31
  55. mtcli/plugins/volume_medio/tests/test_vm.py +21 -21
  56. mtcli/utils/__init__.py +0 -0
  57. mtcli/utils/pidfile.py +35 -0
  58. mtcli/utils/time.py +65 -0
  59. mtcli/views/base_view.py +80 -0
  60. mtcli/views/factory_view.py +30 -0
  61. mtcli/views/full_view.py +40 -69
  62. mtcli/views/hl_view.py +32 -0
  63. mtcli/views/range_view.py +47 -0
  64. mtcli/views/rate_view.py +18 -0
  65. mtcli/views/volume_view.py +62 -0
  66. {mtcli-4.0.0.dev0.dist-info → mtcli-4.0.0.dev1.dist-info}/METADATA +14 -9
  67. mtcli-4.0.0.dev1.dist-info/RECORD +84 -0
  68. {mtcli-4.0.0.dev0.dist-info → mtcli-4.0.0.dev1.dist-info}/WHEEL +1 -1
  69. mtcli-4.0.0.dev1.dist-info/entry_points.txt +8 -0
  70. mtcli-4.0.0.dev1.dist-info/licenses/LICENSE +21 -0
  71. mtcli/commands/logs.py +0 -42
  72. mtcli/models/chart_model.py +0 -155
  73. mtcli/models/conf_model.py +0 -32
  74. mtcli/models/consecutive_bars_model.py +0 -75
  75. mtcli/models/signals_model.py +0 -100
  76. mtcli/models/unconsecutive_bar_model.py +0 -67
  77. mtcli/mt.py +0 -45
  78. mtcli/plugins/media_movel/command.py +0 -99
  79. mtcli/plugins/media_movel/models/__init__.py +0 -1
  80. mtcli/views/close_view.py +0 -37
  81. mtcli/views/high_view.py +0 -37
  82. mtcli/views/low_view.py +0 -37
  83. mtcli/views/min_view.py +0 -42
  84. mtcli/views/open_view.py +0 -37
  85. mtcli/views/ranges_view.py +0 -43
  86. mtcli/views/rates_view.py +0 -41
  87. mtcli/views/utils.py +0 -37
  88. mtcli/views/vars_view.py +0 -46
  89. mtcli/views/volumes_view.py +0 -55
  90. mtcli-4.0.0.dev0.dist-info/LICENSE +0 -17
  91. mtcli-4.0.0.dev0.dist-info/RECORD +0 -64
  92. mtcli-4.0.0.dev0.dist-info/entry_points.txt +0 -9
  93. /mtcli/plugins/media_movel/{models/model_media_movel.py → model.py} +0 -0
mtcli/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .cli import mt
2
+
3
+ mt()
mtcli/cli.py ADDED
@@ -0,0 +1,51 @@
1
+ """
2
+ CLI principal do mtcli.
3
+ """
4
+
5
+ import os
6
+ import click
7
+ from .plugin_loader import load_plugins
8
+ from .logger import setup_logger
9
+
10
+ from .commands.bars import bars
11
+ from .commands.doctor import doctor
12
+
13
+ logger = setup_logger(__name__)
14
+
15
+
16
+ @click.group(context_settings={"max_content_width": 120}, invoke_without_command=True)
17
+ @click.version_option(package_name="mtcli")
18
+ @click.pass_context
19
+ def mt(ctx):
20
+ """
21
+ CLI principal do mtcli.
22
+ """
23
+
24
+ if ctx.invoked_subcommand is None:
25
+ click.echo(ctx.get_help())
26
+
27
+
28
+ mt.add_command(doctor, name="doctor")
29
+ mt.add_command(bars, name="bars")
30
+
31
+ loaded_plugins = load_plugins(mt)
32
+ logger.info("Plugins carregados: %s", loaded_plugins)
33
+
34
+
35
+ @mt.command(name="plugins")
36
+ def list_plugins():
37
+ """
38
+ Lista os plugins carregados no mtcli.
39
+ """
40
+
41
+ if not loaded_plugins:
42
+ click.echo("Nenhum plugin carregado.")
43
+ return
44
+
45
+ click.echo("Plugins carregados:\n")
46
+ for name in loaded_plugins:
47
+ click.echo(f" {name}")
48
+
49
+
50
+ if __name__ == "__main__":
51
+ mt()
mtcli/commands/bars.py CHANGED
@@ -1,153 +1,75 @@
1
- """Exibe o gráfico de barras."""
2
-
3
- import click
4
-
5
- from mtcli.conf import (
6
- DATE,
7
- PERIOD,
8
- PERIODOS,
9
- SYMBOL,
10
- TIMEFRAMES,
11
- VIEW,
12
- VOLUME,
13
- )
14
- from mtcli.models.bars_model import BarsModel
15
- from mtcli.models.rates_model import RatesModel
16
- from mtcli.views.close_view import CloseView
17
- from mtcli.views.full_view import FullView
18
- from mtcli.views.high_view import HighView
19
- from mtcli.views.low_view import LowView
20
- from mtcli.views.min_view import MinView
21
- from mtcli.views.open_view import OpenView
22
- from mtcli.views.ranges_view import RangesView
23
- from mtcli.views.rates_view import RatesView
24
- from mtcli.views.vars_view import VarsView
25
- from mtcli.views.volumes_view import VolumesView
26
-
27
-
28
- @click.command(
29
- help="Mostra o gráfico de candles em texto para o ativo e período especificados.",
30
- )
31
- @click.option(
32
- "--symbol",
33
- "-s",
34
- default=SYMBOL,
35
- show_default=True,
36
- help="Codigo ou ticker do ativo.",
37
- )
38
- @click.option(
39
- "--view",
40
- "-v",
41
- type=click.Choice(
42
- [
43
- "ch",
44
- "m",
45
- "hl",
46
- "f",
47
- "full",
48
- "r",
49
- "range",
50
- "v",
51
- "volume",
52
- "va",
53
- "percentual",
54
- "oh",
55
- "ohlc",
56
- "o",
57
- "open",
58
- "h",
59
- "high",
60
- "l",
61
- "low",
62
- "c",
63
- "close",
64
- ],
65
- case_sensitive=False,
66
- ),
67
- default=VIEW,
68
- show_default=True,
69
- 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.",
70
- )
71
- @click.option(
72
- "--period",
73
- "-p",
74
- type=click.Choice(TIMEFRAMES, case_sensitive=False),
75
- default=PERIOD,
76
- show_default=True,
77
- help="Timeframe do grafico.",
78
- )
79
- @click.option(
80
- "--count",
81
- "-c",
82
- type=int,
83
- default=PERIODOS,
84
- show_default=True,
85
- help="Quantidade de periodos a serem exibidos.",
86
- )
87
- @click.option(
88
- "--date",
89
- "-d",
90
- default=DATE,
91
- show_default=True,
92
- help="Data para intraday, formato AAAA-MM-DD.",
93
- )
94
- @click.option(
95
- "--numerator",
96
- "-n",
97
- is_flag=True,
98
- default=False,
99
- show_default=True,
100
- help="Ativa a numeracao das barras.",
101
- )
102
- @click.option(
103
- "--show-date",
104
- "-sd",
105
- is_flag=True,
106
- default=False,
107
- show_default=True,
108
- help="Ativa a datacao das barras.",
109
- )
110
- @click.option(
111
- "--volume",
112
- "-vo",
113
- type=click.Choice(["tick", "real"], case_sensitive=False),
114
- default=VOLUME,
115
- show_default=True,
116
- help="Tipo de volume.",
117
- )
118
- def bars(symbol, view, period, count, date, numerator, show_date, volume):
119
- """Exibe o grafico do MetaTrader 5."""
120
- period = period.lower()
121
- view = view.lower()
122
- rates = RatesModel(symbol, period, count).get_data()
123
- bars = BarsModel(rates, date).get_bars()
124
- views = []
125
- if view in ["m", "ch", "hl"]: # máximas e mínimas
126
- views = MinView(bars, count, period, date, numerator, show_date).views()
127
- elif view in ["r", "range"]: # ranges
128
- views = RangesView(bars, count, period, date, numerator, show_date).views()
129
- elif view in ["oh", "ohlc"]: # OHLC
130
- views = RatesView(bars, count, period, date, numerator, show_date).views()
131
- elif view in ["va", "percentual"]: # variações percentuais
132
- views = VarsView(bars, count, period, date, numerator, show_date).views()
133
- elif view in ["o", "open"]: # abertura
134
- views = OpenView(bars, count, period, date, numerator, show_date).views()
135
- elif view in ["h", "high"]: # máximas
136
- views = HighView(bars, count, period, date, numerator, show_date).views()
137
- elif view in ["l", "low"]: # mínimas
138
- views = LowView(bars, count, period, date, numerator, show_date).views()
139
- elif view in ["c", "close"]: # fechamentos
140
- views = CloseView(bars, count, period, date, numerator, show_date).views()
141
- elif view in ["v", "volume"]: # volumes
142
- views = VolumesView(
143
- bars, count, period, date, numerator, show_date, volume
144
- ).views()
145
- else: # completo
146
- views = FullView(bars, count, period, date, numerator, show_date).views()
147
- if views:
148
- for view in views:
149
- click.echo(view)
150
-
151
-
152
- if __name__ == "__main__":
153
- bars()
1
+ """
2
+ Comando CLI `bars`.
3
+
4
+ Responsável por:
5
+ - receber parâmetros via terminal (Click)
6
+ - instanciar a fonte de dados
7
+ - delegar execução ao BarsController
8
+ - imprimir saída no terminal
9
+
10
+ Este comando é a camada de entrada (CLI) no padrão MVC do mtcli.
11
+ """
12
+
13
+ import click
14
+
15
+ from ..controllers.bars_controller import BarsController
16
+ from ..conf import (
17
+ DATA_SOURCE_NAME,
18
+ SYMBOL,
19
+ TIMEFRAME,
20
+ MAX_BARS,
21
+ VIEW,
22
+ VOLUME_TYPE,
23
+ )
24
+ from ..data.factory import create_data_source
25
+
26
+
27
+ @click.command()
28
+ @click.option("--symbol", "-s", default=SYMBOL, show_default=True, help="Código do ativo (ex: WINM26)")
29
+ @click.option("--timeframe", "-t", "period", default=TIMEFRAME, show_default=True, help="Timeframe (ex: M1, M5, D1)")
30
+ @click.option("--max-bars", "-mb", "count", default=MAX_BARS, show_default=True, help="Quantidade de barras")
31
+ @click.option("--view", "-v", default=VIEW, show_default=True, help="Formato da view (hl_view, full_view, etc)")
32
+ @click.option("--date", "-d", default=None, show_default=True, help="Filtrar pregão (YYYY-MM-DD)")
33
+ @click.option("--numerator", "-n", is_flag=True, show_default=True, help="Numerar barras")
34
+ @click.option("--show-date", "-sd", is_flag=True, show_default=True, help="Exibir data/hora")
35
+ @click.option("--volume-type", "-vt", "volume", default=VOLUME_TYPE, show_default=True, help="Tipo de volume (tick/real)")
36
+ @click.option("--data-source", "-ds", "source", default=None, show_default=True, help="Fonte de dados (mt5/csv)")
37
+ def bars(symbol, period, count, view, date, numerator, show_date, volume, source):
38
+ """
39
+ Executa o comando `bars`.
40
+
41
+ Fluxo:
42
+ 1. Resolve fonte de dados
43
+ 2. Executa controller
44
+ 3. Renderiza saída linha a linha
45
+
46
+ Args:
47
+ symbol (str): Ativo
48
+ period (str): Timeframe
49
+ count (int): Quantidade de barras
50
+ view (str): Nome da view
51
+ date (str | None): Filtro de pregão
52
+ numerator (bool): Numerar barras
53
+ show_date (bool): Mostrar timestamp
54
+ volume (str | None): Tipo de volume
55
+ source (str | None): Fonte de dados
56
+ """
57
+
58
+ source_name = source or DATA_SOURCE_NAME
59
+ data_source = create_data_source(source_name)
60
+
61
+ controller = BarsController(data_source)
62
+
63
+ lines = controller.execute(
64
+ symbol=symbol,
65
+ period=period,
66
+ count=count,
67
+ date=date,
68
+ view=view,
69
+ numerator=numerator,
70
+ show_date=show_date,
71
+ volume=volume,
72
+ )
73
+
74
+ for line in lines:
75
+ click.echo(line)
mtcli/commands/conf.py CHANGED
@@ -1,50 +1,44 @@
1
- """Gerencia configurações registradas no mtcli.ini."""
1
+ """
2
+ Comando para exibir configurações disponíveis.
3
+ """
2
4
 
3
5
  import click
4
6
 
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.")
7
+ from mtcli.config_registry import registry
8
+ from mtcli.conf import conf
42
9
 
10
+
11
+ @click.command()
12
+ @click.argument("section", required=False)
13
+ def conf_cmd(section):
14
+ """
15
+ Exibe configurações disponíveis.
16
+
17
+ Exemplos:
18
+
19
+ mt conf
20
+ mt conf renko
21
+ """
22
+
23
+ if section:
24
+ options = registry.get_section(section)
43
25
  else:
26
+ options = registry.get_all()
27
+
28
+ if not options:
29
+ click.echo("Nenhuma configuração registrada.")
30
+ return
31
+
32
+ for opt in options:
33
+
34
+ value = conf.get(opt.name, section=opt.section, default=opt.default)
35
+
44
36
  click.echo(
45
- "Nenhuma opção fornecida. Use --help para ver as opções disponíveis."
37
+ f"{opt.section}.{opt.name} = {value} "
38
+ f"(default={opt.default})"
46
39
  )
47
40
 
41
+ if opt.description:
42
+ click.echo(f" {opt.description}")
48
43
 
49
- if __name__ == "__main__":
50
- conf()
44
+ click.echo()
@@ -0,0 +1,194 @@
1
+ """
2
+ Comando de diagnóstico do mtcli.
3
+
4
+ Este comando executa uma série de verificações básicas para ajudar
5
+ a identificar problemas de instalação, configuração ou carregamento
6
+ de plugins no ambiente do mtcli.
7
+
8
+ As verificações incluem:
9
+
10
+ - Versão do Python
11
+ - Instalação do mtcli
12
+ - Fonte de dados configurada
13
+ - Caminho de dados CSV
14
+ - Conexão com MetaTrader5
15
+ - Descoberta e carregamento de plugins
16
+
17
+ Caso algum plugin falhe ao carregar, o erro é exibido e registrado
18
+ no sistema de logs.
19
+ """
20
+
21
+ import sys
22
+ import click
23
+ import traceback
24
+
25
+ from importlib.metadata import version, PackageNotFoundError
26
+
27
+ import MetaTrader5 as mt5
28
+
29
+ from mtcli.logger import setup_logger
30
+ from mtcli.conf import conf, DATA_SOURCE_NAME
31
+ from mtcli.mt5_context import mt5_conexao
32
+ from mtcli.plugin_loader import discover_plugins
33
+
34
+
35
+ logger = setup_logger(__name__)
36
+
37
+
38
+ def status(ok: bool) -> str:
39
+ """
40
+ Retorna um marcador visual simples de status.
41
+
42
+ Parameters
43
+ ----------
44
+ ok : bool
45
+ Indica se o teste foi bem sucedido.
46
+
47
+ Returns
48
+ -------
49
+ str
50
+ Texto representando o status.
51
+ """
52
+ return "OK " if ok else "ERRO"
53
+
54
+
55
+ @click.command()
56
+ def doctor():
57
+ """
58
+ Executa diagnóstico do ambiente mtcli.
59
+
60
+ O comando verifica se os componentes essenciais do ambiente
61
+ estão funcionando corretamente e apresenta um relatório simples
62
+ no terminal.
63
+ """
64
+
65
+ click.echo("Diagnóstico do ambiente mtcli\n")
66
+
67
+ # ---------------------------------------------------------
68
+ # Python
69
+ # ---------------------------------------------------------
70
+
71
+ python_ok = sys.version_info >= (3, 10)
72
+
73
+ click.echo(f"{status(python_ok)} Python: {sys.version.split()[0]}")
74
+
75
+ if not python_ok:
76
+ click.echo(" Versão mínima recomendada: Python 3.10")
77
+
78
+ # ---------------------------------------------------------
79
+ # mtcli
80
+ # ---------------------------------------------------------
81
+
82
+ try:
83
+
84
+ mtcli_version = version("mtcli")
85
+
86
+ click.echo(f"{status(True)} mtcli: {mtcli_version}")
87
+
88
+ except PackageNotFoundError:
89
+
90
+ click.echo(f"{status(False)} mtcli não encontrado")
91
+
92
+ logger.error("Pacote mtcli não encontrado no ambiente.")
93
+
94
+ # ---------------------------------------------------------
95
+ # Fonte de dados
96
+ # ---------------------------------------------------------
97
+
98
+ click.echo(f"{status(True)} Data source: {DATA_SOURCE_NAME}")
99
+
100
+ # ---------------------------------------------------------
101
+ # CSV path
102
+ # ---------------------------------------------------------
103
+
104
+ try:
105
+
106
+ path = conf.get_csv_path()
107
+
108
+ click.echo(f"{status(True)} CSV path: {path}")
109
+
110
+ except Exception as exc:
111
+
112
+ click.echo(f"{status(False)} CSV path inválido")
113
+
114
+ logger.exception("Erro ao obter CSV path: %s", exc)
115
+
116
+ # ---------------------------------------------------------
117
+ # MT5 conexão
118
+ # ---------------------------------------------------------
119
+
120
+ try:
121
+
122
+ with mt5_conexao():
123
+
124
+ info = mt5.terminal_info()
125
+
126
+ if info:
127
+
128
+ click.echo(f"{status(True)} MetaTrader5 conectado")
129
+
130
+ else:
131
+
132
+ click.echo(f"{status(False)} MetaTrader5 não respondeu")
133
+
134
+ except Exception as exc:
135
+
136
+ click.echo(f"{status(False)} Falha na conexão MT5")
137
+
138
+ logger.exception("Erro ao conectar MT5: %s", exc)
139
+
140
+ # ---------------------------------------------------------
141
+ # Plugins
142
+ # ---------------------------------------------------------
143
+
144
+ click.echo("\nPlugins:")
145
+
146
+ try:
147
+
148
+ entry_points = list(discover_plugins())
149
+
150
+ if not entry_points:
151
+
152
+ click.echo(" Nenhum plugin encontrado.")
153
+ return
154
+
155
+ ok_count = 0
156
+ fail_count = 0
157
+
158
+ for ep in entry_points:
159
+
160
+ plugin_name = ep.name
161
+
162
+ try:
163
+
164
+ ep.load()
165
+
166
+ click.echo(f" OK {plugin_name}")
167
+
168
+ ok_count += 1
169
+
170
+ except Exception as exc:
171
+
172
+ click.echo(f" ERRO {plugin_name}")
173
+
174
+ click.echo(f" {exc}")
175
+
176
+ logger.error(
177
+ "Erro ao carregar plugin %s\n%s",
178
+ plugin_name,
179
+ traceback.format_exc(),
180
+ )
181
+
182
+ fail_count += 1
183
+
184
+ click.echo(
185
+ f"\nResumo: {ok_count} plugins OK | {fail_count} com erro"
186
+ )
187
+
188
+ except Exception as exc:
189
+
190
+ click.echo(f"{status(False)} Falha ao descobrir plugins")
191
+
192
+ logger.exception("Erro ao descobrir plugins: %s", exc)
193
+
194
+ click.echo("\nDiagnóstico concluído.")