mtcli-risco 2.2.2__py3-none-any.whl → 2.3.0.dev0__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.
- mtcli_risco/cli.py +21 -0
- mtcli_risco/commands/__init__.py +0 -2
- mtcli_risco/commands/checar.py +26 -22
- mtcli_risco/commands/monitorar.py +23 -19
- mtcli_risco/commands/trades.py +26 -31
- mtcli_risco/models/checar_model.py +117 -102
- mtcli_risco/models/trades_model.py +62 -77
- mtcli_risco/plugin.py +12 -11
- {mtcli_risco-2.2.2.dist-info → mtcli_risco-2.3.0.dev0.dist-info}/METADATA +6 -4
- mtcli_risco-2.3.0.dev0.dist-info/RECORD +17 -0
- {mtcli_risco-2.2.2.dist-info → mtcli_risco-2.3.0.dev0.dist-info}/WHEEL +1 -1
- mtcli_risco-2.3.0.dev0.dist-info/entry_points.txt +3 -0
- mtcli_risco-2.2.2.dist-info/RECORD +0 -16
- mtcli_risco-2.2.2.dist-info/entry_points.txt +0 -3
- {mtcli_risco-2.2.2.dist-info → mtcli_risco-2.3.0.dev0.dist-info/licenses}/LICENSE +0 -0
mtcli_risco/cli.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import click
|
|
2
|
+
from .commands.checar import checar_cmd
|
|
3
|
+
from .commands.monitorar import monitorar_cmd
|
|
4
|
+
from .commands.trades import trades_cmd
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.group()
|
|
8
|
+
@click.version_option(package_name="mtcli-risco")
|
|
9
|
+
def risco_cli():
|
|
10
|
+
"""
|
|
11
|
+
Plugin mtcli-risco.
|
|
12
|
+
|
|
13
|
+
Conjunto de comandos para gerenciamento e controle de risco diário
|
|
14
|
+
baseado em lucro/prejuízo no MetaTrader 5.
|
|
15
|
+
"""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
risco_cli.add_command(checar_cmd, name="checar")
|
|
20
|
+
risco_cli.add_command(monitorar_cmd, name="monitorar")
|
|
21
|
+
risco_cli.add_command(trades_cmd, name="trades")
|
mtcli_risco/commands/__init__.py
CHANGED
mtcli_risco/commands/checar.py
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Comando checar.
|
|
3
|
+
|
|
4
|
+
Verifica se o limite diário de prejuízo foi atingido e,
|
|
5
|
+
se necessário, encerra posições e bloqueia novas ordens.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
import click
|
|
4
9
|
from datetime import date
|
|
@@ -13,59 +18,58 @@ from mtcli_risco.models.checar_model import (
|
|
|
13
18
|
cancelar_todas_ordens,
|
|
14
19
|
)
|
|
15
20
|
|
|
16
|
-
|
|
17
21
|
log = setup_logger()
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
@click.command(
|
|
21
25
|
"checar",
|
|
22
|
-
help="Verifica se o limite diário de prejuízo foi atingido
|
|
26
|
+
help="Verifica se o limite diário de prejuízo foi atingido.",
|
|
23
27
|
)
|
|
24
28
|
@click.version_option(package_name="mtcli-risco")
|
|
25
29
|
@click.option(
|
|
26
30
|
"--limite",
|
|
27
31
|
"-l",
|
|
28
32
|
default=LOSS_LIMIT,
|
|
29
|
-
help="Limite de perda diária (ex: -500)
|
|
33
|
+
help="Limite de perda diária (ex: -500).",
|
|
30
34
|
)
|
|
31
35
|
@click.option(
|
|
32
36
|
"--lucro",
|
|
33
37
|
is_flag=True,
|
|
34
38
|
default=False,
|
|
35
|
-
help="Exibe o lucro total do dia
|
|
39
|
+
help="Exibe o lucro total do dia e encerra.",
|
|
36
40
|
)
|
|
37
|
-
def
|
|
38
|
-
"""
|
|
41
|
+
def checar_cmd(limite: float, lucro: bool):
|
|
42
|
+
"""
|
|
43
|
+
Executa uma verificação pontual do risco diário.
|
|
44
|
+
"""
|
|
39
45
|
if lucro:
|
|
40
|
-
|
|
41
|
-
click.echo(f"Lucro total do dia: {
|
|
42
|
-
log.info(f"Lucro total do dia: {
|
|
46
|
+
total = calcular_lucro_total_dia()
|
|
47
|
+
click.echo(f"Lucro total do dia: {total:.2f}")
|
|
48
|
+
log.info(f"[RISCO] Lucro total do dia: {total:.2f}")
|
|
43
49
|
return
|
|
44
50
|
|
|
45
|
-
estado = carregar_estado(STATUS_FILE)
|
|
46
51
|
hoje = date.today()
|
|
52
|
+
estado = carregar_estado(STATUS_FILE)
|
|
47
53
|
|
|
54
|
+
# Reset diário
|
|
48
55
|
if estado["data"] != hoje.isoformat():
|
|
56
|
+
log.info("[ESTADO] Novo dia detectado, resetando bloqueio.")
|
|
49
57
|
estado["bloqueado"] = False
|
|
58
|
+
salvar_estado(STATUS_FILE, hoje, False)
|
|
50
59
|
|
|
51
60
|
if estado["bloqueado"]:
|
|
52
|
-
click.echo("
|
|
61
|
+
click.echo("Sistema bloqueado hoje por limite de risco.")
|
|
62
|
+
log.warning("[RISCO] Sistema já bloqueado hoje.")
|
|
53
63
|
return
|
|
54
64
|
|
|
55
65
|
if risco_excedido(limite):
|
|
56
66
|
click.echo(
|
|
57
|
-
f"Limite {limite:.2f} excedido. Encerrando posições e bloqueando
|
|
67
|
+
f"Limite {limite:.2f} excedido. Encerrando posições e bloqueando ordens."
|
|
58
68
|
)
|
|
59
|
-
log.
|
|
69
|
+
log.warning(f"[RISCO] Limite {limite:.2f} excedido.")
|
|
60
70
|
encerrar_todas_posicoes()
|
|
61
71
|
cancelar_todas_ordens()
|
|
62
|
-
|
|
72
|
+
salvar_estado(STATUS_FILE, hoje, True)
|
|
63
73
|
else:
|
|
64
74
|
click.echo("Dentro do limite de risco.")
|
|
65
|
-
log.info(f"
|
|
66
|
-
|
|
67
|
-
salvar_estado(STATUS_FILE, hoje, estado["bloqueado"])
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if __name__ == "__main__":
|
|
71
|
-
checar()
|
|
75
|
+
log.info(f"[RISCO] Dentro do limite {limite:.2f}.")
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Comando monitorar.
|
|
3
|
+
|
|
4
|
+
Monitora continuamente o risco diário em intervalos regulares.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
import time
|
|
4
8
|
import click
|
|
@@ -17,7 +21,8 @@ log = setup_logger()
|
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
@click.command(
|
|
20
|
-
"monitorar",
|
|
24
|
+
"monitorar",
|
|
25
|
+
help="Monitora continuamente o risco diário em tempo real.",
|
|
21
26
|
)
|
|
22
27
|
@click.version_option(package_name="mtcli-risco")
|
|
23
28
|
@click.option("--limite", "-l", default=LOSS_LIMIT, help="Limite de perda diária.")
|
|
@@ -25,35 +30,34 @@ log = setup_logger()
|
|
|
25
30
|
"--intervalo",
|
|
26
31
|
"-i",
|
|
27
32
|
default=INTERVALO,
|
|
28
|
-
help="Intervalo entre verificações (segundos)
|
|
33
|
+
help="Intervalo entre verificações (segundos).",
|
|
29
34
|
)
|
|
30
|
-
def
|
|
31
|
-
"""
|
|
32
|
-
|
|
35
|
+
def monitorar_cmd(limite: float, intervalo: int):
|
|
36
|
+
"""
|
|
37
|
+
Inicia o monitoramento contínuo do risco diário.
|
|
38
|
+
"""
|
|
39
|
+
click.echo(f"Monitorando risco a cada {intervalo}s | Limite: {limite:.2f}")
|
|
40
|
+
log.info(f"[MONITOR] Iniciado | limite={limite} intervalo={intervalo}s")
|
|
41
|
+
|
|
33
42
|
try:
|
|
34
43
|
while True:
|
|
35
44
|
hoje = date.today()
|
|
36
45
|
estado = carregar_estado(STATUS_FILE)
|
|
37
46
|
|
|
38
|
-
if estado
|
|
39
|
-
|
|
47
|
+
if estado["data"] != hoje.isoformat():
|
|
48
|
+
log.info("[ESTADO] Novo dia detectado, resetando bloqueio.")
|
|
40
49
|
salvar_estado(STATUS_FILE, hoje, False)
|
|
50
|
+
estado["bloqueado"] = False
|
|
41
51
|
|
|
42
|
-
if not estado
|
|
43
|
-
click.echo(f"Limite {limite} excedido. Encerrando posições.")
|
|
44
|
-
log.
|
|
52
|
+
if not estado["bloqueado"] and risco_excedido(limite):
|
|
53
|
+
click.echo(f"Limite {limite:.2f} excedido. Encerrando posições.")
|
|
54
|
+
log.warning("[RISCO] Limite excedido durante monitoramento.")
|
|
45
55
|
encerrar_todas_posicoes()
|
|
46
56
|
cancelar_todas_ordens()
|
|
47
57
|
salvar_estado(STATUS_FILE, hoje, True)
|
|
48
|
-
elif estado.get("bloqueado"):
|
|
49
|
-
click.echo(f"O limite {limite} foi excedido. Sistema bloqueado hoje.")
|
|
50
|
-
else:
|
|
51
|
-
click.echo(f"Dentro do limite {limite}")
|
|
52
58
|
|
|
53
59
|
time.sleep(intervalo)
|
|
60
|
+
|
|
54
61
|
except KeyboardInterrupt:
|
|
55
62
|
click.echo("Monitoramento interrompido.")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if __name__ == "__main__":
|
|
59
|
-
monitorar()
|
|
63
|
+
log.info("[MONITOR] Interrompido pelo usuário.")
|
mtcli_risco/commands/trades.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Comando trades.
|
|
3
|
+
|
|
4
|
+
Exibe lucros realizados, abertos e totais do dia.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
import click
|
|
4
8
|
from mtcli.logger import setup_logger
|
|
@@ -8,49 +12,40 @@ from mtcli_risco.models.trades_model import (
|
|
|
8
12
|
calcular_lucro_total_dia,
|
|
9
13
|
)
|
|
10
14
|
|
|
11
|
-
|
|
12
15
|
log = setup_logger()
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
@click.command(
|
|
16
|
-
"trades",
|
|
19
|
+
"trades",
|
|
20
|
+
help="Exibe os lucros realizados, abertos e totais do dia.",
|
|
17
21
|
)
|
|
18
22
|
@click.version_option(package_name="mtcli-risco")
|
|
19
|
-
@click.option(
|
|
20
|
-
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
default=False,
|
|
27
|
-
help="Exibe o lucro realizado do dia.",
|
|
28
|
-
)
|
|
29
|
-
@click.option(
|
|
30
|
-
"--total", "-t", is_flag=True, default=False, help="Exibe o lucro total do dia."
|
|
31
|
-
)
|
|
32
|
-
def trades(aberto, realizado, total):
|
|
33
|
-
"""Exibe os lucros aberto, realizado e total do dia."""
|
|
34
|
-
lucro_aberto = obter_lucro_aberto()
|
|
35
|
-
lucro_realizado = calcular_lucro_realizado()
|
|
36
|
-
lucro_total = calcular_lucro_total_dia()
|
|
37
|
-
|
|
23
|
+
@click.option("--aberto", "-a", is_flag=True, help="Exibe o lucro em aberto.")
|
|
24
|
+
@click.option("--realizado", "-r", is_flag=True, help="Exibe o lucro realizado.")
|
|
25
|
+
@click.option("--total", "-t", is_flag=True, help="Exibe o lucro total.")
|
|
26
|
+
def trades_cmd(aberto: bool, realizado: bool, total: bool):
|
|
27
|
+
"""
|
|
28
|
+
Exibe informações de lucro do dia atual.
|
|
29
|
+
"""
|
|
38
30
|
if aberto:
|
|
39
|
-
|
|
31
|
+
valor = obter_lucro_aberto()
|
|
32
|
+
click.echo(f"{valor:.2f}")
|
|
40
33
|
return
|
|
41
34
|
|
|
42
35
|
if realizado:
|
|
43
|
-
|
|
36
|
+
valor = calcular_lucro_realizado()
|
|
37
|
+
click.echo(f"{valor:.2f}")
|
|
44
38
|
return
|
|
45
39
|
|
|
46
40
|
if total:
|
|
47
|
-
|
|
41
|
+
valor = calcular_lucro_total_dia()
|
|
42
|
+
click.echo(f"{valor:.2f}")
|
|
48
43
|
return
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
aberto_v = obter_lucro_aberto()
|
|
46
|
+
realizado_v = calcular_lucro_realizado()
|
|
47
|
+
total_v = aberto_v + realizado_v
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
click.echo(f"lucro em aberto: {aberto_v:.2f}")
|
|
50
|
+
click.echo(f"lucro realizado: {realizado_v:.2f}")
|
|
51
|
+
click.echo(f"lucro total: {total_v:.2f}")
|
|
@@ -1,102 +1,117 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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_risco.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,77 +1,62 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
log = setup_logger()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
log.info(f"
|
|
33
|
-
return
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
log.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
lucro_aberto = info.profit if info else 0.0
|
|
64
|
-
log.info(f"lucro aberto: {lucro_aberto:.2f}")
|
|
65
|
-
|
|
66
|
-
return lucro_aberto
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def calcular_lucro_total_dia() -> float:
|
|
70
|
-
"""Calcula o lucro total do dia."""
|
|
71
|
-
realizado = calcular_lucro_realizado()
|
|
72
|
-
aberto = obter_lucro_aberto()
|
|
73
|
-
total = realizado + aberto
|
|
74
|
-
log.info(
|
|
75
|
-
f"Lucro realizado: {realizado:.2f}, lucro aberto: {aberto:.2f}, total: {total:.2f}"
|
|
76
|
-
)
|
|
77
|
-
return total
|
|
1
|
+
"""
|
|
2
|
+
Model de cálculos de lucro diário.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import MetaTrader5 as mt5
|
|
6
|
+
from datetime import datetime, time
|
|
7
|
+
from mtcli.logger import setup_logger
|
|
8
|
+
from mtcli_risco.mt5_context import mt5_conexao
|
|
9
|
+
|
|
10
|
+
log = setup_logger()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def calcular_lucro_realizado() -> float:
|
|
14
|
+
"""
|
|
15
|
+
Calcula o lucro/prejuízo realizado no dia atual.
|
|
16
|
+
"""
|
|
17
|
+
hoje = datetime.now().date()
|
|
18
|
+
inicio = datetime.combine(hoje, time(0, 0))
|
|
19
|
+
fim = datetime.combine(hoje, time(23, 59))
|
|
20
|
+
|
|
21
|
+
with mt5_conexao():
|
|
22
|
+
deals = mt5.history_deals_get(inicio, fim)
|
|
23
|
+
|
|
24
|
+
if not deals:
|
|
25
|
+
log.info("[TRADES] Nenhum deal realizado hoje.")
|
|
26
|
+
return 0.0
|
|
27
|
+
|
|
28
|
+
lucro = sum(
|
|
29
|
+
deal.profit for deal in deals if deal.entry == mt5.DEAL_ENTRY_OUT
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
log.info(f"[TRADES] Lucro realizado: {lucro:.2f}")
|
|
33
|
+
return lucro
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def obter_lucro_aberto() -> float:
|
|
37
|
+
"""
|
|
38
|
+
Obtém o lucro/prejuízo das posições atualmente abertas.
|
|
39
|
+
"""
|
|
40
|
+
with mt5_conexao():
|
|
41
|
+
info = mt5.account_info()
|
|
42
|
+
|
|
43
|
+
if not info:
|
|
44
|
+
log.warning("[TRADES] account_info indisponível.")
|
|
45
|
+
return 0.0
|
|
46
|
+
|
|
47
|
+
log.info(f"[TRADES] Lucro em aberto: {info.profit:.2f}")
|
|
48
|
+
return info.profit
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def calcular_lucro_total_dia() -> float:
|
|
52
|
+
"""
|
|
53
|
+
Calcula o lucro total do dia (realizado + aberto).
|
|
54
|
+
"""
|
|
55
|
+
realizado = calcular_lucro_realizado()
|
|
56
|
+
aberto = obter_lucro_aberto()
|
|
57
|
+
total = realizado + aberto
|
|
58
|
+
|
|
59
|
+
log.info(
|
|
60
|
+
f"[TRADES] Total do dia | realizado={realizado:.2f} aberto={aberto:.2f}"
|
|
61
|
+
)
|
|
62
|
+
return total
|
mtcli_risco/plugin.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from .
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def register(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
"""
|
|
2
|
+
Registro do plugin mtcli-risco no mtcli principal.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .cli import risco_cli
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register(main_cli):
|
|
9
|
+
"""
|
|
10
|
+
Registra o grupo de comandos 'risco' no mtcli principal.
|
|
11
|
+
"""
|
|
12
|
+
main_cli.add_command(risco_cli, name="risco")
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: mtcli-risco
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0.dev0
|
|
4
4
|
Summary: Plugin mtcli para controle de loss diário
|
|
5
|
-
License: GPL-3.0
|
|
5
|
+
License-Expression: GPL-3.0
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Author: Valmir França da Silva
|
|
7
8
|
Author-email: vfranca3@gmail.com
|
|
8
9
|
Requires-Python: >=3.10,<3.14
|
|
9
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Dist: click (>=8.3.1,<9.0.0)
|
|
16
|
+
Requires-Dist: metatrader5 (>=5.0.5572,<6.0.0)
|
|
15
17
|
Requires-Dist: mtcli (>=1.18.1)
|
|
16
18
|
Project-URL: Documentation, https://mtcli-risco.readthedocs.io
|
|
17
19
|
Project-URL: Homepage, https://github.com/vfranca/mtcli-risco
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
mtcli_risco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
mtcli_risco/cli.py,sha256=FbKDUEro2LYcsMD53yiPopb7Wo8J6kuoAyirxGJUFIc,569
|
|
3
|
+
mtcli_risco/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
mtcli_risco/commands/checar.py,sha256=jCGfioyVZLPgfSZC6Fa_sN1rcfOgMQ4j2WiBQnUtg4U,2191
|
|
5
|
+
mtcli_risco/commands/monitorar.py,sha256=bT1yRMIwBynb1t9Bg7pQlGBkXbm3_QGqfeKOppYNfLc,2017
|
|
6
|
+
mtcli_risco/commands/trades.py,sha256=_mOm97AXhgjNghgyMRERRzTYiJI9cRe8a5UPWOYWg1g,1432
|
|
7
|
+
mtcli_risco/conf.py,sha256=5T82aUS9k9zB1I94RGL5xud-0B4iYI6CwXJoUNDotAQ,365
|
|
8
|
+
mtcli_risco/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
mtcli_risco/models/checar_model.py,sha256=qcNUEbduoWfZ-e7vZ4LeZjhud6b3_Wl6hGdrcGT112w,3507
|
|
10
|
+
mtcli_risco/models/trades_model.py,sha256=CdPqJwt7AeAEylau0_f1zsIi6sbmpXVKcK-TBmTAC6o,1559
|
|
11
|
+
mtcli_risco/mt5_context.py,sha256=lA5ZppfRsCvzrxf0Jce1ewZtt2xFln5xnZ1sArk1P4k,190
|
|
12
|
+
mtcli_risco/plugin.py,sha256=1kEG7wrtREMgn9SQan1E-2z6EMrlyYnflYAHPb8xB6I,252
|
|
13
|
+
mtcli_risco-2.3.0.dev0.dist-info/entry_points.txt,sha256=x1Xi_6K4zqXgvWeylqZ1tGPHhUjSR3ryCv5iJm8tg7E,51
|
|
14
|
+
mtcli_risco-2.3.0.dev0.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
15
|
+
mtcli_risco-2.3.0.dev0.dist-info/METADATA,sha256=0yenYIIM8F0nHoiLjj5q5hYtkC3ATwKDl3QfW6AWjpc,2528
|
|
16
|
+
mtcli_risco-2.3.0.dev0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
17
|
+
mtcli_risco-2.3.0.dev0.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
mtcli_risco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
mtcli_risco/commands/__init__.py,sha256=0AvTSqwccXKX8r9CXkEaPWQ4bj87Bo8y1BFGpMhR9hk,60
|
|
3
|
-
mtcli_risco/commands/checar.py,sha256=15NA-gRmZYXxnkdjhaF7LxJpaFkZwyz7NLEK7O5r3p4,2061
|
|
4
|
-
mtcli_risco/commands/monitorar.py,sha256=Xsj1xjkUDBrrPJeeUepvXO8iNrXp_e5VkS56s2yJ7hM,1999
|
|
5
|
-
mtcli_risco/commands/trades.py,sha256=GUUkJiMkYq0XqlQPn6tWeOz4zNjLA1dfUP5LGheulnQ,1455
|
|
6
|
-
mtcli_risco/conf.py,sha256=5T82aUS9k9zB1I94RGL5xud-0B4iYI6CwXJoUNDotAQ,365
|
|
7
|
-
mtcli_risco/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
mtcli_risco/models/checar_model.py,sha256=tv7r-lQdrXhtJo-4vlDUeMNN-FAQ2maUHR-IvccWAWg,3335
|
|
9
|
-
mtcli_risco/models/trades_model.py,sha256=4_kG0jCclKyb4HqLWLvEndvRp4kHexzJwk_0YKIGEog,2204
|
|
10
|
-
mtcli_risco/mt5_context.py,sha256=lA5ZppfRsCvzrxf0Jce1ewZtt2xFln5xnZ1sArk1P4k,190
|
|
11
|
-
mtcli_risco/plugin.py,sha256=gl7JzOyzcO3vHzoJuaZ9z-LBkq20Q5ICMviEuXW-yAk,309
|
|
12
|
-
mtcli_risco-2.2.2.dist-info/entry_points.txt,sha256=LV78AP1S7_X3zuy0HZjdRHh1kp8VM-RvpBCNf0dOiNY,57
|
|
13
|
-
mtcli_risco-2.2.2.dist-info/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
|
|
14
|
-
mtcli_risco-2.2.2.dist-info/METADATA,sha256=x-AvpdDABmzNayJQ87MOBOsEWlXw6BNmFs2wiUEgSa8,2482
|
|
15
|
-
mtcli_risco-2.2.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
16
|
-
mtcli_risco-2.2.2.dist-info/RECORD,,
|
|
File without changes
|