Em posts recentes, ensinamos diversas técnicas de trading utilizando o Python, bem como também mostramos como utilizar o MetaTrader5 para a importação de dados de ativos financeiros e a realização de requisições de ordem e venda com a linguagem. No post de hoje, estaremos interessados em criar um ambiente automatizado, que pretende criar as requisições de acordo com os sinais de um sistema de trading em tempo real.
O objetivo será criar um ambiente totalmente automatizado, portanto, será necessário usar diversas opções de funções e métodos disponíveis na biblioteca MetaTrader 5 do Python, de forma que atenda os principais requisitos de criação de ordem e venda.
Iremos criar esse ambiente em uma classe definida com diversas funções em um arquivo .py. Após isso, utilizaremos um arquivo .ipnyb para definir o indicador utilizado, no caso, o RSI, para definir as ordens de compra e venda. Os dois arquivos são separados pelos blocos de código abaixo.
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
from datetime import datetime
class MT5:
max_price = dict()
min_price = dict()
summary = None
def get_ticks(symbol, number_of_data = 10000):
# Data e horário atual
from_date = datetime.now()
# Extrai os ticks (instrumentos financeiros)
ticks = mt5.copy_ticks_from(symbol, from_date, number_of_data, mt5.COPY_TICKS_ALL)
# Transforma a tupla em data frame
df_ticks = pd.DataFrame(ticks)
# Converte a data em datetime
df_ticks["time"] = pd.to_datetime(df_ticks["time"], unit="s")
# Coloca a data no index
df_ticks = df_ticks.set_index("time")
return df_ticks
def get_rates(symbol, number_of_data = 10000, timeframe=mt5.TIMEFRAME_D1):
# Cria a data atual
from_date = datetime.now()
# Extrai os ticks (instrumentos financeiros)
rates = mt5.copy_rates_from(symbol, timeframe, from_date, number_of_data)
# Transforma a tupla em data frame
df_rates = pd.DataFrame(rates)
# Converte a data em datetime
df_rates["time"] = pd.to_datetime(df_rates["time"], unit="s")
# Coloca a data no index
df_rates = df_rates.set_index("time")
return df_rates
def risk_reward_threshold(symbol, buy=True, risk=0.01, reward=0.02):
# Extrai o leverage (alavancagem)
leverage = mt5.account_info().leverage
# Computa o preço
price = mt5.symbol_info(symbol).ask
# Extrai o número de decimais
nb_decimal = str(price)[::-1].find(".")
# Computa as variação em porcentagem
var_down = risk/leverage
var_up = reward/leverage
# Acha o limite do TP e SL em preço absoluto
if buy:
price = mt5.symbol_info(symbol).ask
# Computa as variações em preço absoluto
price_var_down = var_down*price
price_var_up = var_up * price
tp = np.round(price + price_var_up, nb_decimal)
sl = np.round(price - price_var_down, nb_decimal)
else:
price = mt5.symbol_info(symbol).bid
# Computa as variações em preço absoluto
price_var_down = var_down*price
price_var_up = var_up * price
tp = np.round(price - price_var_up, nb_decimal)
sl = np.round(price + price_var_down, nb_decimal)
return tp, sl
def find_filling_mode(symbol):
for i in range(2):
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": mt5.symbol_info(symbol).volume_min,
"type": mt5.ORDER_TYPE_BUY,
"price": mt5.symbol_info_tick(symbol).ask,
"type_filling": i,
"type_time": mt5.ORDER_TIME_GTC}
result = mt5.order_check(request)
if result.comment == "Feito":
break
return i
def send_order(symbol, lot, buy, sell, id_position=None, pct_tp=0.02, pct_sl=0.01, comment=" No specific comment", magic=0):
# Inicializa o mt5 com o Python
mt5.initialize()
# Extrai o filling_mode
filling_type = MT5.find_filling_mode(symbol)
""" ABRE O TRADE """
if buy and id_position==None:
tp, sl = MT5.risk_reward_threshold(symbol, buy=True, risk=pct_sl, reward=pct_tp)
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": mt5.ORDER_TYPE_BUY,
"price": mt5.symbol_info_tick(symbol).ask,
"deviation": 10,
"tp": tp,
"sl": sl,
"magic": magic,
"comment": comment,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC}
result = mt5.order_send(request)
print(mt5.symbol_info_tick(symbol).ask, tp, sl)
return result
if sell and id_position==None:
tp, sl = MT5.risk_reward_threshold(symbol, buy=False, risk=pct_sl, reward=pct_tp)
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": mt5.ORDER_TYPE_SELL,
"price": mt5.symbol_info_tick(symbol).bid,
"deviation": 10,
"tp": tp,
"sl": sl,
"magic": magic,
"comment": comment,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC}
result = mt5.order_send(request)
print(mt5.symbol_info_tick(symbol).bid, tp, sl)
return result
""" FECHA O TRADE """
if buy and id_position!=None:
request = {
"position": id_position,
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": mt5.ORDER_TYPE_SELL,
"price": mt5.symbol_info_tick(symbol).bid,
"deviation": 10,
"magic": magic,
"comment": comment,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC}
result = mt5.order_send(request)
return result
if sell and id_position!=None:
request = {
"position": id_position,
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": mt5.ORDER_TYPE_BUY,
"price": mt5.symbol_info_tick(symbol).ask,
"deviation": 10,
"magic": magic,
"comment": comment,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC}
result = mt5.order_send(request)
return result
def resume():
""" Retorna as posições atuais. Posição=0 --> Compra """
# Define o nome das colunas que serão criadas
colonnes = ["ticket", "position", "symbol", "volume", "magic", "profit", "price", "tp", "sl","trade_size"]
# Pega as posições em aberto
liste = mt5.positions_get()
# Cria um dataframe vazio
summary = pd.DataFrame()
# Cria o loop para adicionar cada linha no dataframe
for element in liste:
element_pandas = pd.DataFrame([element.ticket, element.type, element.symbol, element.volume, element.magic,
element.profit, element.price_open, element.tp,
element.sl, mt5.symbol_info(element.symbol).trade_contract_size],
index=colonnes).transpose()
summary = pd.concat((summary, element_pandas), axis=0)
try:
summary["profit %"] = summary.profit / (summary.price * summary.trade_size * summary.volume)
summary = summary.reset_index(drop=True)
except:
pass
return summary
def trailing_stop_loss():
# Extrai as posições abertas atuais
MT5.summary = MT5.resume()
# Verificaçã: Tem alguma posição em aberto?
if MT5.summary.shape[0] >0:
for i in range(MT5.summary.shape[0]):
# Extrai as informação
row = MT5.summary.iloc[i]
symbol = row["symbol"]
""" CASO 1: Altera dinâmicamente o stop loss para uma ORDEM DE COMPRA """
# Trailing stop loss para um posição de compra
if row["position"] == 0:
if symbol not in MT5.max_price.keys():
MT5.max_price[symbol]=row["price"]
# Extrai o preço atual
current_price = (mt5.symbol_info(symbol).ask + mt5.symbol_info(symbol).bid ) / 2
# Computa a distância entre o preço atual e preço máximo
from_sl_to_curent_price = current_price - row["sl"]
from_sl_to_max_price = MT5.max_price[symbol] - row["sl"]
# Se o preço atual é maior que o preço máximo anterior --> novo preço máximo
if current_price > MT5.max_price[symbol]:
MT5.max_price[symbol] = current_price
# Encontra a diferença entre a diferença do preço menor e máximo
if from_sl_to_curent_price > from_sl_to_max_price:
difference = from_sl_to_curent_price - from_sl_to_max_price
# Seta filling mode
filling_type = mt5.symbol_info(symbol).filling_mode
# Seta o point
point = mt5.symbol_info(symbol).point
# Altera o sl
request = {
"action": mt5.TRADE_ACTION_SLTP,
"symbol": symbol,
"position": row["ticket"],
"volume": row["volume"],
"type": mt5.ORDER_TYPE_BUY,
"price": row["price"],
"sl": row["sl"] + difference,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC,
}
information = mt5.order_send(request)
print(information)
""" CASO 2: Altera dinâmicamente o stop loss para uma ORDEM DE VENDA """
# Trailing stop loss para uma ordem de venda
if row["position"] == 1:
if symbol not in MT5.min_price.keys():
MT5.min_price[symbol]=row["price"]
# Extrai o preço atual
current_price = (mt5.symbol_info(symbol).ask + mt5.symbol_info(symbol).bid ) / 2
# Computa a distância entre o preço atual e preço máximo
from_sl_to_curent_price = row["sl"] - current_price
from_sl_to_min_price = row["sl"] - MT5.min_price[symbol]
# Se o preço atual é maior que o preço máximo anterior --> novo preço máximo
if current_price < MT5.min_price[symbol]:
MT5.min_price[symbol] = current_price
# Encontra a diferença entre a diferença do preço menor e máximo
if from_sl_to_curent_price > from_sl_to_min_price:
difference = from_sl_to_curent_price - from_sl_to_min_price
# Seta filling mode
filling_type = mt5.symbol_info(symbol).filling_mode
# Seta o point
point = mt5.symbol_info(symbol).point
# Altera o sl
request = {
"action": mt5.TRADE_ACTION_SLTP,
"symbol": symbol,
"position": row["ticket"],
"volume": row["volume"],
"type": mt5.ORDER_TYPE_SELL,
"price": row["price"],
"sl": row["sl"] - difference,
"type_filling": filling_type,
"type_time": mt5.ORDER_TIME_GTC,
}
information = mt5.order_send(request)
print(information)
def verif_tsl():
if len(MT5.summary)>0:
buy_open_positions = MT5.summary.loc[MT5.summary["position"]==0]["symbol"]
sell_open_positions = MT5.summary.loc[MT5.summary["position"]==0]["symbol"]
else:
buy_open_positions = []
sell_open_positions = []
""" SE FECHAR UMA DAS POSIÇÃO É NECESSÁRIO DELETAR O PREÇO NO DICIONÁRIO DE PREÇO MAX E MIN """
if len(MT5.max_price) != len(buy_open_positions) and len(buy_open_positions) >0:
symbol_to_delete = []
for symbol in MT5.max_price.keys():
if symbol not in list(buy_open_positions):
symbol_to_delete.append(symbol)
for symbol in symbol_to_delete:
del MT5.max_price[symbol]
if len(MT5.min_price) != len(sell_open_positions) and len(sell_open_positions) >0:
symbol_to_delete = []
for symbol in MT5.min_price.keys():
if symbol not in list(sell_open_positions):
symbol_to_delete.append(symbol)
for symbol in symbol_to_delete:
del MT5.min_price[symbol]
if len(buy_open_positions) == 0:
MT5.max_price={}
if len(sell_open_positions) == 0:
MT5.min_price={}
def run(symbol, buy, sell, lot, pct_tp=0.02, pct_sl=0.01, comment="", magic=23400):
# Inicializa
mt5.initialize()
# Escolhe o símbolo
print("------------------------------------------------------------------")
print("Date: ", datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "\tSYMBOL:", symbol)
# Inicializa
ouvertures = MT5.resume()
# Compra ou Vende
print(f"BUY: {buy} \t SELL: {sell}")
""" Fecha o trade eventualmente """
# Tipo de extração do preço
try:
position = ouvertures.loc[ouvertures["symbol"] == symbol].values[0][1]
identifier = ouvertures.loc[ouvertures["symbol"] == symbol].values[0][0]
except:
position = None
identifier = None
if position!=None:
print(f"POSITION: {position} \t ID: {identifier}")
# Verifica os trades
if buy == True and position == 0:
buy = False
elif buy == False and position == 0:
before = mt5.account_info().balance
res = MT5.send_order(symbol, lot, True, False, id_position=identifier,pct_tp=pct_tp, pct_sl=pct_sl, comment=" No specific comment", magic=0)
after = mt5.account_info().balance
print(f"CLOSE BUY POSITION: {res.comment}")
pct = np.round(100*(after-before)/before, 3)
if res.comment != "Request executed":
print("WARNINGS", res.comment)
elif sell == True and position == 1:
sell = False
elif sell == False and position == 1:
before = mt5.account_info().balance
res = MT5.send_order(symbol, lot, False, True, id_position=identifier,pct_tp=pct_tp, pct_sl=pct_sl, comment=" No specific comment", magic=0)
print(f"CLOSE SELL POSITION: {res.comment}")
after = mt5.account_info().balance
pct = np.round(100*(after-before)/before, 3)
if res.comment != "Request executed":
print("WARNINGS", res.comment)
else:
pass
""" Compra ou Vende """
if buy == True:
res = MT5.send_order(symbol, lot, True, False, id_position=None,pct_tp=pct_tp, pct_sl=pct_sl, comment=" No specific comment", magic=0)
print(f"OPEN BUY POSITION: {res.comment}")
if res.comment != "Request executed":
print("WARNINGS", res.comment)
if sell == True:
res = MT5.send_order(symbol, lot, False, True, id_position=None,pct_tp=pct_tp, pct_sl=pct_sl, comment=" No specific comment", magic=0)
print(f"OPEN SELL POSITION: {res.comment}")
if res.comment != "Request executed":
print("WARNINGS", res.comment)
print("------------------------------------------------------------------")
<pre></pre>
<pre>
</pre>
# Importa as bibliotecas
import talib as ta
import time
from datetime import datetime
import numpy as np
import pandas as pd
from algo_mt5 import *
import MetaTrader5 as mt5
mt5.initialize()
# Define o indicador (rsi)
def rsi(symbol):
overbuy = 70
oversell = 30
neutral = 50
df = MT5.get_data(symbol, 3500).dropna()
df["rsi"] = ta.RSI(df["close"], timeperiod=14)
today = df["rsi"].iloc[-1]
# Long buy signal
buy = (today > neutral) & (today < overbuy)
# Short selling signal
sell = (today < neutral) & (today > oversell)
return buy, sell
# Define os ativos financeiros
symbols_list = {
"Petrobras": ["PETR4", 1]}
# Constrói a mensagem
current_account_info = mt5.account_info()
print("------------------------------------------------------------------")
print(f"Login: {mt5.account_info().login} \tserver: {mt5.account_info().server}")
print(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Balance: {current_account_info.balance} USD, \t Equity: {current_account_info.equity} USD, \t Profit: {current_account_info.profit} USD")
print(f"Run time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("------------------------------------------------------------------")
# Em qual dia e horário irá rodar o algortimo?
launching = [[0, "00:15:59"],
[0, "15:59:50"],
[1, "15:59:50"],
[2, "15:59:50"],
[3, "15:59:50"],
[4, "15:59:50"]]
# Lança o algoritmo
while True:
# TSL
MT5.trailing_stop_loss()
MT5.verif_tsl()
# Verficação para o lançamento
current_time = [datetime.now().weekday(), datetime.now().strftime("%H:%M:%S")]
if current_time in launching:
is_time = True
else:
is_time = False
# is_time=True # Apenas para rodar o trading AGORA
if is_time:
# Abre os trades
for asset in symbols_list.keys():
# Inicializa os inputs
symbol = symbols_list[asset][0]
lot = symbols_list[asset][1]
selected = mt5.symbol_select(symbol)
if not selected:
print(f"\nERROR - Failed to select '{symbol}' in MetaTrader 5 with error :",mt5.last_error())
else:
# Cria os sinais
buy, sell = rsi(symbol)
# Roda o algoritimo
MT5.run(symbol, buy, sell, lot, pct_tp=0.1, pct_sl=0.05)
______________________________________
Quer saber mais?
Veja nosso curso de Python para Investimentos.
________________________________
Referências
Inglese, Lucas. Python for Finance and Algorithmic trading (2nd edition): Machine Learning, Deep Learning, Time series Analysis, Risk and Portfolio Management for MetaTrader™5 Live Trading