mtcli-risco 2.4.2__tar.gz → 2.5.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.
Files changed (24) hide show
  1. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/PKG-INFO +2 -2
  2. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/cli.py +33 -27
  3. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/commands/monitorar.py +64 -64
  4. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/commands/panic.py +2 -2
  5. mtcli_risco-2.5.0/mtcli_risco/commands/start.py +78 -0
  6. mtcli_risco-2.5.0/mtcli_risco/commands/status.py +57 -0
  7. mtcli_risco-2.5.0/mtcli_risco/commands/stop.py +29 -0
  8. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/conf.py +1 -1
  9. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/models/checar_model.py +117 -117
  10. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/models/hardstop_model.py +1 -1
  11. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/models/panic_model.py +217 -217
  12. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/models/trades_model.py +62 -62
  13. mtcli_risco-2.5.0/mtcli_risco/services/__init__.py +0 -0
  14. mtcli_risco-2.5.0/mtcli_risco/services/monitor_service.py +106 -0
  15. mtcli_risco-2.5.0/mtcli_risco/services/run_monitor.py +59 -0
  16. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/pyproject.toml +2 -2
  17. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/LICENSE +0 -0
  18. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/README.md +0 -0
  19. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/__init__.py +0 -0
  20. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/commands/__init__.py +0 -0
  21. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/commands/checar.py +0 -0
  22. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/commands/trades.py +0 -0
  23. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/models/__init__.py +0 -0
  24. {mtcli_risco-2.4.2 → mtcli_risco-2.5.0}/mtcli_risco/plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtcli-risco
3
- Version: 2.4.2
3
+ Version: 2.5.0
4
4
  Summary: Plugin mtcli para controle de loss diário
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -19,7 +19,7 @@ Classifier: Operating System :: OS Independent
19
19
  Classifier: Topic :: Office/Business :: Financial :: Investment
20
20
  Requires-Dist: click (>=8.3.1,<9.0.0)
21
21
  Requires-Dist: metatrader5 (>=5.0.5572,<6.0.0)
22
- Requires-Dist: mtcli (>=3.7.0)
22
+ Requires-Dist: mtcli (>=3.7.2)
23
23
  Project-URL: Documentation, https://mtcli-risco.readthedocs.io
24
24
  Project-URL: Homepage, https://github.com/vfranca/mtcli-risco
25
25
  Project-URL: Repository, https://github.com/vfranca/mtcli-risco
@@ -1,27 +1,33 @@
1
- """
2
- Comando principal e registro dos subcomandos
3
- """
4
-
5
- import click
6
- from .commands.checar import checar_cmd
7
- from .commands.monitorar import monitorar_cmd
8
- from .commands.trades import trades_cmd
9
- from .commands.panic import panic_cmd
10
-
11
-
12
- @click.group()
13
- @click.version_option(package_name="mtcli-risco")
14
- def cli():
15
- """
16
- Plugin mtcli-risco.
17
-
18
- Conjunto de comandos para gerenciamento e controle de risco diário
19
- baseado em lucro/prejuízo no MetaTrader 5.
20
- """
21
- pass
22
-
23
-
24
- cli.add_command(checar_cmd, name="checar")
25
- cli.add_command(monitorar_cmd, name="monitorar")
26
- cli.add_command(trades_cmd, name="trades")
27
- cli.add_command(panic_cmd, name="panic")
1
+ """
2
+ Comando principal e registro dos subcomandos
3
+ """
4
+
5
+ import click
6
+ from .commands.checar import checar_cmd
7
+ from .commands.monitorar import monitorar_cmd
8
+ from .commands.trades import trades_cmd
9
+ from .commands.panic import panic_cmd
10
+ from .commands.start import start_cmd
11
+ from .commands.stop import stop_cmd
12
+ from .commands.status import status_cmd
13
+
14
+
15
+ @click.group()
16
+ @click.version_option(package_name="mtcli-risco")
17
+ def cli():
18
+ """
19
+ Plugin mtcli-risco.
20
+
21
+ Conjunto de comandos para gerenciamento e controle de risco diário
22
+ baseado em lucro/prejuízo no MetaTrader 5.
23
+ """
24
+ pass
25
+
26
+
27
+ cli.add_command(checar_cmd, name="checar")
28
+ cli.add_command(monitorar_cmd, name="monitorar")
29
+ cli.add_command(trades_cmd, name="trades")
30
+ cli.add_command(panic_cmd, name="panic")
31
+ cli.add_command(start_cmd, name="start")
32
+ cli.add_command(stop_cmd, name="stop")
33
+ cli.add_command(status_cmd, name="status")
@@ -1,64 +1,64 @@
1
- """
2
- Comando monitorar.
3
-
4
- Monitora continuamente o risco diário e executa PANIC CLOSE
5
- quando o limite é excedido.
6
- """
7
-
8
- import time
9
- import click
10
- from datetime import date
11
- from mtcli.logger import setup_logger
12
- from mtcli_risco.conf import LOSS_LIMIT, STATUS_FILE, INTERVALO
13
- from mtcli_risco.models.checar_model import (
14
- carregar_estado,
15
- salvar_estado,
16
- risco_excedido,
17
- )
18
- from mtcli_risco.models.panic_model import panic_close_all
19
-
20
- log = setup_logger()
21
-
22
-
23
- @click.command()
24
- @click.version_option(package_name="mtcli-risco")
25
- @click.option("--limite", "-l", default=LOSS_LIMIT, show_default=True)
26
- @click.option("--intervalo", "-i", default=INTERVALO, show_default=True)
27
- @click.option("--dry-run", is_flag=True, help="Simula o panic close.")
28
- def monitorar_cmd(limite: float, intervalo: int, dry_run: bool):
29
- """
30
- Inicia o monitoramento contínuo do risco diário.
31
- """
32
- click.echo(
33
- f"Monitorando risco | limite={limite:.2f} intervalo={intervalo}s"
34
- )
35
- log.info(
36
- f"[MONITOR] Iniciado | limite={limite} intervalo={intervalo}s dry_run={dry_run}"
37
- )
38
-
39
- try:
40
- while True:
41
- hoje = date.today()
42
- estado = carregar_estado(STATUS_FILE)
43
-
44
- if estado["data"] != hoje.isoformat():
45
- salvar_estado(STATUS_FILE, hoje, False)
46
- estado["bloqueado"] = False
47
-
48
- if not estado["bloqueado"] and risco_excedido(limite):
49
- click.echo("LIMITE DE RISCO EXCEDIDO — PANIC CLOSE")
50
- log.critical("[MONITOR] Disparando PANIC CLOSE")
51
-
52
- resultado = panic_close_all(
53
- retry_on_market_open=True,
54
- dry_run=dry_run,
55
- )
56
-
57
- salvar_estado(STATUS_FILE, hoje, True)
58
- log.critical(f"[MONITOR] Panic finalizado: {resultado}")
59
-
60
- time.sleep(intervalo)
61
-
62
- except KeyboardInterrupt:
63
- click.echo("Monitoramento interrompido.")
64
- log.info("[MONITOR] Interrompido pelo usuário.")
1
+ """
2
+ Comando monitorar.
3
+
4
+ Monitora continuamente o risco diário e executa PANIC CLOSE
5
+ quando o limite é excedido.
6
+ """
7
+
8
+ import time
9
+ import click
10
+ from datetime import date
11
+ from mtcli.logger import setup_logger
12
+ from ..conf import LOSS_LIMIT, STATUS_FILE, INTERVALO
13
+ from ..models.checar_model import (
14
+ carregar_estado,
15
+ salvar_estado,
16
+ risco_excedido,
17
+ )
18
+ from ..models.panic_model import panic_close_all
19
+
20
+ log = setup_logger("risco")
21
+
22
+
23
+ @click.command()
24
+ @click.version_option(package_name="mtcli-risco")
25
+ @click.option("--limite", "-l", default=LOSS_LIMIT, show_default=True)
26
+ @click.option("--intervalo", "-i", default=INTERVALO, show_default=True)
27
+ @click.option("--dry-run", is_flag=True, help="Simula o panic close.")
28
+ def monitorar_cmd(limite: float, intervalo: int, dry_run: bool):
29
+ """
30
+ Inicia o monitoramento contínuo do risco diário.
31
+ """
32
+ click.echo(
33
+ f"Monitorando risco limite={limite:.2f} intervalo={intervalo}s"
34
+ )
35
+ log.info(
36
+ f"[MONITOR] Iniciado | limite={limite} intervalo={intervalo}s dry_run={dry_run}"
37
+ )
38
+
39
+ try:
40
+ while True:
41
+ hoje = date.today()
42
+ estado = carregar_estado(STATUS_FILE)
43
+
44
+ if estado["data"] != hoje.isoformat():
45
+ salvar_estado(STATUS_FILE, hoje, False)
46
+ estado["bloqueado"] = False
47
+
48
+ if not estado["bloqueado"] and risco_excedido(limite):
49
+ click.echo("LIMITE DE RISCO EXCEDIDO — PANIC CLOSE")
50
+ log.critical("[MONITOR] Disparando PANIC CLOSE")
51
+
52
+ resultado = panic_close_all(
53
+ retry_on_market_open=True,
54
+ dry_run=dry_run,
55
+ )
56
+
57
+ salvar_estado(STATUS_FILE, hoje, True)
58
+ log.critical(f"[MONITOR] Panic finalizado: {resultado}")
59
+
60
+ time.sleep(intervalo)
61
+
62
+ except KeyboardInterrupt:
63
+ click.echo("Monitoramento interrompido.")
64
+ log.info("[MONITOR] Interrompido pelo usuário.")
@@ -4,10 +4,10 @@ Comando PANIC CLOSE.
4
4
 
5
5
  import click
6
6
  from mtcli.logger import setup_logger
7
- from mtcli_risco.models.panic_model import panic_close_all
7
+ from ..models.panic_model import panic_close_all
8
8
  from mtcli_risco import conf
9
9
 
10
- log = setup_logger()
10
+ log = setup_logger("risco")
11
11
 
12
12
 
13
13
  @click.command()
@@ -0,0 +1,78 @@
1
+ """
2
+ Comando start.
3
+
4
+ Responsável por iniciar o monitor de risco em background.
5
+
6
+ Este comando cria um novo processo Python que executa o módulo
7
+ `mtcli_risco.services.run_monitor`, responsável por iniciar o
8
+ loop de monitoramento contínuo do risco diário.
9
+
10
+ Fluxo
11
+ -----
12
+
13
+ mt risco start
14
+ ->
15
+ subprocess.Popen()
16
+ ->
17
+ run_monitor.py
18
+ ->
19
+ monitor_loop()
20
+
21
+ O comando também verifica se já existe um processo ativo
22
+ através do arquivo PID.
23
+ """
24
+
25
+ import os
26
+ import click
27
+ import subprocess
28
+ import sys
29
+
30
+ from mtcli.logger import setup_logger
31
+ from mtcli.conf import PID_FILE
32
+ from ..conf import LOSS_LIMIT, INTERVALO
33
+
34
+ log = setup_logger("risco")
35
+
36
+
37
+ @click.command()
38
+ @click.option("--limite", default=LOSS_LIMIT, show_default=True)
39
+ @click.option("--intervalo", default=INTERVALO, show_default=True)
40
+ @click.option("--dry-run", is_flag=True, help="Simula execução sem enviar ordens.")
41
+ def start_cmd(limite: float, intervalo: int, dry_run: bool) -> None:
42
+ """
43
+ Inicia o monitor de risco em background.
44
+
45
+ Parameters
46
+ ----------
47
+ limite : float
48
+ Limite máximo de prejuízo diário permitido.
49
+ intervalo : int
50
+ Intervalo (segundos) entre verificações de risco.
51
+ dry_run : bool
52
+ Executa em modo simulação (sem enviar ordens ao MT5).
53
+ """
54
+
55
+ if os.path.exists(PID_FILE):
56
+ click.echo("Monitor já está rodando.")
57
+ log.warning("[START] Monitor já ativo | pid_file=%s", PID_FILE)
58
+ return
59
+
60
+ cmd = [
61
+ sys.executable,
62
+ "-m",
63
+ "mtcli_risco.services.run_monitor",
64
+ str(limite),
65
+ str(intervalo),
66
+ str(int(dry_run)),
67
+ ]
68
+
69
+ log.info(
70
+ "[START] Iniciando monitor | limite=%.2f intervalo=%ss dry_run=%s",
71
+ limite,
72
+ intervalo,
73
+ dry_run,
74
+ )
75
+
76
+ subprocess.Popen(cmd)
77
+
78
+ click.echo("Monitor iniciado em background.")
@@ -0,0 +1,57 @@
1
+ """
2
+ Comando status.
3
+
4
+ Exibe o estado atual do monitor de risco.
5
+
6
+ O comando verifica:
7
+
8
+ 1. Existência do arquivo PID
9
+ 2. Presença do heartbeat
10
+ 3. Tempo desde o último heartbeat
11
+
12
+ Isso permite detectar se o monitor:
13
+
14
+ - está rodando
15
+ - travou
16
+ - ou foi encerrado.
17
+ """
18
+
19
+ import os
20
+ import time
21
+ import click
22
+
23
+ from mtcli.logger import setup_logger
24
+ from mtcli.conf import PID_FILE, HEARTBEAT_FILE
25
+
26
+ log = setup_logger("risco")
27
+
28
+
29
+ @click.command()
30
+ def status_cmd() -> None:
31
+ """
32
+ Exibe o status atual do monitor de risco.
33
+ """
34
+
35
+ if not os.path.exists(PID_FILE):
36
+ click.echo("Monitor não está rodando.")
37
+ log.info("[STATUS] Monitor inativo")
38
+ return
39
+
40
+ with open(PID_FILE) as f:
41
+ pid = f.read().strip()
42
+
43
+ if os.path.exists(HEARTBEAT_FILE):
44
+
45
+ with open(HEARTBEAT_FILE) as f:
46
+ ts = float(f.read())
47
+
48
+ age = int(time.time() - ts)
49
+
50
+ click.echo(f"Monitor ativo (PID {pid})")
51
+ click.echo(f"Último heartbeat: {age}s")
52
+
53
+ log.debug("[STATUS] Monitor ativo | pid=%s heartbeat_age=%ss", pid, age)
54
+
55
+ else:
56
+ click.echo(f"Monitor rodando (PID {pid})")
57
+ log.warning("[STATUS] PID encontrado sem heartbeat | pid=%s", pid)
@@ -0,0 +1,29 @@
1
+ """
2
+ Comando stop.
3
+
4
+ Solicita a parada do monitor de risco.
5
+
6
+ O comando cria um arquivo de sinal (`STOP_FILE`) que será
7
+ detectado pelo loop de monitoramento, permitindo uma
8
+ finalização controlada do processo.
9
+ """
10
+
11
+ import click
12
+ from mtcli.logger import setup_logger
13
+ from mtcli.conf import STOP_FILE
14
+
15
+ log = setup_logger("risco")
16
+
17
+
18
+ @click.command()
19
+ def stop_cmd() -> None:
20
+ """
21
+ Envia sinal de parada para o monitor de risco.
22
+ """
23
+
24
+ with open(STOP_FILE, "w") as f:
25
+ f.write("stop")
26
+
27
+ log.info("[STOP] Sinal de parada enviado")
28
+
29
+ click.echo("Sinal de parada enviado.")
@@ -17,7 +17,7 @@ STATUS_FILE = os.getenv(
17
17
  INTERVALO = int(
18
18
  os.getenv(
19
19
  "INTERVALO",
20
- config["DEFAULT"].getint("intervalo", fallback=60),
20
+ config["DEFAULT"].getint("intervalo", fallback=1),
21
21
  )
22
22
  )
23
23
 
@@ -1,117 +1,117 @@
1
- """
2
- Model de controle de risco.
3
-
4
- Responsável por:
5
- - persistência de estado diário
6
- - verificação de limite de prejuízo
7
- - encerramento de posições
8
- - cancelamento de ordens
9
- """
10
-
11
- import json
12
- import os
13
- from datetime import date
14
- import MetaTrader5 as mt5
15
- from mtcli.logger import setup_logger
16
- from mtcli.mt5_context import mt5_conexao
17
- from .trades_model import calcular_lucro_total_dia
18
-
19
- log = setup_logger()
20
-
21
-
22
- def carregar_estado(status_file: str) -> dict:
23
- """
24
- Carrega o estado persistido do controle de risco.
25
- """
26
- if os.path.exists(status_file):
27
- with open(status_file, "r") as f:
28
- log.info(f"[ESTADO] Carregando {status_file}")
29
- return json.load(f)
30
- return {"data": "", "bloqueado": False}
31
-
32
-
33
- def salvar_estado(status_file: str, data: date, bloqueado: bool) -> None:
34
- """
35
- Persiste o estado diário do controle de risco.
36
- """
37
- with open(status_file, "w") as f:
38
- json.dump(
39
- {"data": data.isoformat(), "bloqueado": bloqueado},
40
- f,
41
- indent=2,
42
- )
43
- log.info(f"[ESTADO] Salvo | data={data.isoformat()} bloqueado={bloqueado}")
44
-
45
-
46
- def encerrar_todas_posicoes() -> None:
47
- """
48
- Encerra todas as posições abertas no MT5.
49
- """
50
- with mt5_conexao():
51
- positions = mt5.positions_get()
52
-
53
- if not positions:
54
- log.info("[MT5] Nenhuma posição aberta.")
55
- return
56
-
57
- for pos in positions:
58
- tipo_oposto = (
59
- mt5.ORDER_TYPE_SELL
60
- if pos.type == mt5.ORDER_TYPE_BUY
61
- else mt5.ORDER_TYPE_BUY
62
- )
63
-
64
- ordem = {
65
- "action": mt5.TRADE_ACTION_DEAL,
66
- "symbol": pos.symbol,
67
- "volume": pos.volume,
68
- "type": tipo_oposto,
69
- "position": pos.ticket,
70
- "deviation": 10,
71
- "magic": 1000,
72
- "comment": "Fechamento por limite de risco",
73
- }
74
-
75
- log.warning(f"[MT5] Encerrando posição {pos.ticket}")
76
- resultado = mt5.order_send(ordem)
77
-
78
- if not resultado or resultado.retcode != mt5.TRADE_RETCODE_DONE:
79
- log.error(f"[MT5] Falha ao fechar {pos.ticket}: {resultado}")
80
- else:
81
- log.info(f"[MT5] Posição {pos.ticket} encerrada.")
82
-
83
-
84
- def cancelar_todas_ordens() -> None:
85
- """
86
- Cancela todas as ordens pendentes.
87
- """
88
- with mt5_conexao():
89
- ordens = mt5.orders_get()
90
-
91
- if not ordens:
92
- log.info("[MT5] Nenhuma ordem pendente.")
93
- return
94
-
95
- for ordem in ordens:
96
- resultado = mt5.order_delete(ordem.ticket)
97
- if not resultado or resultado.retcode != mt5.TRADE_RETCODE_DONE:
98
- log.error(f"[MT5] Falha ao cancelar ordem {ordem.ticket}")
99
- else:
100
- log.info(f"[MT5] Ordem {ordem.ticket} cancelada.")
101
-
102
-
103
- def risco_excedido(limite: float) -> bool:
104
- """
105
- Verifica se o prejuízo diário ultrapassou o limite configurado.
106
- """
107
- try:
108
- total = calcular_lucro_total_dia()
109
- if total <= limite:
110
- log.warning(
111
- f"[RISCO] Excedido | resultado={total:.2f} limite={limite:.2f}"
112
- )
113
- return True
114
- return False
115
- except Exception as exc:
116
- log.error(f"[RISCO] Erro ao verificar limite: {exc}")
117
- return False
1
+ """
2
+ Model de controle de risco.
3
+
4
+ Responsável por:
5
+ - persistência de estado diário
6
+ - verificação de limite de prejuízo
7
+ - encerramento de posições
8
+ - cancelamento de ordens
9
+ """
10
+
11
+ import json
12
+ import os
13
+ from datetime import date
14
+ import MetaTrader5 as mt5
15
+ from mtcli.logger import setup_logger
16
+ from mtcli.mt5_context import mt5_conexao
17
+ from .trades_model import calcular_lucro_total_dia
18
+
19
+ log = setup_logger("risco")
20
+
21
+
22
+ def carregar_estado(status_file: str) -> dict:
23
+ """
24
+ Carrega o estado persistido do controle de risco.
25
+ """
26
+ if os.path.exists(status_file):
27
+ with open(status_file, "r") as f:
28
+ log.info(f"[ESTADO] Carregando {status_file}")
29
+ return json.load(f)
30
+ return {"data": "", "bloqueado": False}
31
+
32
+
33
+ def salvar_estado(status_file: str, data: date, bloqueado: bool) -> None:
34
+ """
35
+ Persiste o estado diário do controle de risco.
36
+ """
37
+ with open(status_file, "w") as f:
38
+ json.dump(
39
+ {"data": data.isoformat(), "bloqueado": bloqueado},
40
+ f,
41
+ indent=2,
42
+ )
43
+ log.info(f"[ESTADO] Salvo | data={data.isoformat()} bloqueado={bloqueado}")
44
+
45
+
46
+ def encerrar_todas_posicoes() -> None:
47
+ """
48
+ Encerra todas as posições abertas no MT5.
49
+ """
50
+ with mt5_conexao():
51
+ positions = mt5.positions_get()
52
+
53
+ if not positions:
54
+ log.info("[MT5] Nenhuma posição aberta.")
55
+ return
56
+
57
+ for pos in positions:
58
+ tipo_oposto = (
59
+ mt5.ORDER_TYPE_SELL
60
+ if pos.type == mt5.ORDER_TYPE_BUY
61
+ else mt5.ORDER_TYPE_BUY
62
+ )
63
+
64
+ ordem = {
65
+ "action": mt5.TRADE_ACTION_DEAL,
66
+ "symbol": pos.symbol,
67
+ "volume": pos.volume,
68
+ "type": tipo_oposto,
69
+ "position": pos.ticket,
70
+ "deviation": 10,
71
+ "magic": 1000,
72
+ "comment": "Fechamento por limite de risco",
73
+ }
74
+
75
+ log.warning(f"[MT5] Encerrando posição {pos.ticket}")
76
+ resultado = mt5.order_send(ordem)
77
+
78
+ if not resultado or resultado.retcode != mt5.TRADE_RETCODE_DONE:
79
+ log.error(f"[MT5] Falha ao fechar {pos.ticket}: {resultado}")
80
+ else:
81
+ log.info(f"[MT5] Posição {pos.ticket} encerrada.")
82
+
83
+
84
+ def cancelar_todas_ordens() -> None:
85
+ """
86
+ Cancela todas as ordens pendentes.
87
+ """
88
+ with mt5_conexao():
89
+ ordens = mt5.orders_get()
90
+
91
+ if not ordens:
92
+ log.info("[MT5] Nenhuma ordem pendente.")
93
+ return
94
+
95
+ for ordem in ordens:
96
+ resultado = mt5.order_delete(ordem.ticket)
97
+ if not resultado or resultado.retcode != mt5.TRADE_RETCODE_DONE:
98
+ log.error(f"[MT5] Falha ao cancelar ordem {ordem.ticket}")
99
+ else:
100
+ log.info(f"[MT5] Ordem {ordem.ticket} cancelada.")
101
+
102
+
103
+ def risco_excedido(limite: float) -> bool:
104
+ """
105
+ Verifica se o prejuízo diário ultrapassou o limite configurado.
106
+ """
107
+ try:
108
+ total = calcular_lucro_total_dia()
109
+ if total <= limite:
110
+ log.warning(
111
+ f"[RISCO] Excedido | resultado={total:.2f} limite={limite:.2f}"
112
+ )
113
+ return True
114
+ return False
115
+ except Exception as exc:
116
+ log.error(f"[RISCO] Erro ao verificar limite: {exc}")
117
+ return False
@@ -12,7 +12,7 @@ import subprocess
12
12
  from mtcli.logger import setup_logger
13
13
  from mtcli_risco.conf import MT5_TERMINAL_PATH
14
14
 
15
- log = setup_logger()
15
+ log = setup_logger("risco")
16
16
 
17
17
  FIREWALL_RULE_NAME = "MT5_HARDSTOP_BLOCK"
18
18