Загрузка данных
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import time
from datetime import datetime
# ====================== НАСТРОЙКИ ======================
SYMBOLS_BASE = ["EURUSD", "GBPUSD", "USDJPY", "AUDUSD", "USDCAD", "NZDUSD"]
TIMEFRAME_TREND = mt5.TIMEFRAME_H1 # тренд
TIMEFRAME_ENTRY = mt5.TIMEFRAME_M15 # вход (чаще сигналы)
EMA_FAST = 20
EMA_SLOW = 50
RISK_PERCENT = 0.7 # риск на сделку %
SL_ATR_MULT = 1.5
TP_RR = 1.8
MAGIC = 20261022
# =======================================================
def log(msg):
print(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}")
def initialize():
if not mt5.initialize():
log(f"Ошибка инициализации MT5: {mt5.last_error()}")
return False
log("MT5 успешно подключен")
account = mt5.account_info()
if account:
log(f"Счёт: {account.login} | Баланс: {account.balance:.2f} | Сервер: {account.server}")
return True
def find_symbol(base_name):
"""Ищет реальный символ у брокера (EURUSD, EURUSD.a, EURUSDm и т.д.)"""
symbols = mt5.symbols_get()
if symbols is None:
return None
candidates = [
base_name,
base_name + ".a",
base_name + "m",
base_name + ".m",
base_name + "pro",
base_name + ".pro",
base_name + "#",
]
available = {s.name: s for s in symbols}
for name in candidates:
if name in available and available[name].visible:
return name
# пробуем включить
if name in available:
mt5.symbol_select(name, True)
return name
return None
def get_df(symbol, timeframe, bars=300):
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, bars)
if rates is None or len(rates) < 100:
return None
df = pd.DataFrame(rates)
df['ema_fast'] = df['close'].ewm(span=EMA_FAST, adjust=False).mean()
df['ema_slow'] = df['close'].ewm(span=EMA_SLOW, adjust=False).mean()
# ATR
high_low = df['high'] - df['low']
high_close = np.abs(df['high'] - df['close'].shift())
low_close = np.abs(df['low'] - df['close'].shift())
tr = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
df['atr'] = tr.rolling(14).mean()
return df
def get_trend(symbol):
df = get_df(symbol, TIMEFRAME_TREND)
if df is None:
return None
last = df.iloc[-1]
if last['close'] > last['ema_slow'] and last['ema_fast'] > last['ema_slow']:
return "bull"
elif last['close'] < last['ema_slow'] and last['ema_fast'] < last['ema_slow']:
return "bear"
return "flat"
def check_signal(symbol, trend):
df = get_df(symbol, TIMEFRAME_ENTRY)
if df is None:
return None
prev = df.iloc[-2]
last = df.iloc[-1]
# Быстрый кроссовер
if trend == "bull":
if prev['ema_fast'] <= prev['ema_slow'] and last['ema_fast'] > last['ema_slow']:
return "buy"
elif trend == "bear":
if prev['ema_fast'] >= prev['ema_slow'] and last['ema_fast'] < last['ema_slow']:
return "sell"
return None
def calculate_lot(symbol, sl_distance_price):
account = mt5.account_info()
if account is None:
return 0.01
risk_money = account.balance * (RISK_PERCENT / 100.0)
symbol_info = mt5.symbol_info(symbol)
if symbol_info is None:
return 0.01
tick_value = symbol_info.trade_tick_value
tick_size = symbol_info.trade_tick_size
if tick_size == 0 or tick_value == 0:
return 0.01
points = sl_distance_price / tick_size
if points == 0:
return 0.01
lot = risk_money / (points * tick_value)
lot = max(symbol_info.volume_min, min(lot, symbol_info.volume_max))
lot = round(lot / symbol_info.volume_step) * symbol_info.volume_step
return round(lot, 2)
def has_position(symbol):
positions = mt5.positions_get(symbol=symbol)
if not positions:
return False
for p in positions:
if p.magic == MAGIC:
return True
return False
def open_trade(symbol, signal):
if has_position(symbol):
log(f"{symbol}: уже есть позиция, пропускаем")
return
df = get_df(symbol, TIMEFRAME_ENTRY)
if df is None:
return
atr = df['atr'].iloc[-1]
tick = mt5.symbol_info_tick(symbol)
if tick is None:
log(f"{symbol}: не удалось получить тик")
return
if signal == "buy":
price = tick.ask
sl = price - atr * SL_ATR_MULT
tp = price + (price - sl) * TP_RR
order_type = mt5.ORDER_TYPE_BUY
else:
price = tick.bid
sl = price + atr * SL_ATR_MULT
tp = price - (sl - price) * TP_RR
order_type = mt5.ORDER_TYPE_SELL
lot = calculate_lot(symbol, abs(price - sl))
request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": lot,
"type": order_type,
"price": price,
"sl": round(sl, 5),
"tp": round(tp, 5),
"deviation": 30,
"magic": MAGIC,
"comment": f"Bot {signal}",
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_IOC,
}
result = mt5.order_send(request)
if result.retcode == mt5.TRADE_RETCODE_DONE:
log(f"✓ ОТКРЫЛ {signal.upper()} {symbol} | лот {lot} | цена {price:.5f} | SL {sl:.5f} | TP {tp:.5f}")
else:
log(f"✗ Ошибка открытия {symbol}: {result.retcode} — {result.comment}")
def main():
if not initialize():
return
log("=" * 55)
log("Бот запущен | M15 вход + H1 тренд | подробные логи")
log("=" * 55)
# Находим реальные символы
real_symbols = {}
for base in SYMBOLS_BASE:
found = find_symbol(base)
if found:
real_symbols[base] = found
log(f"Символ найден: {base} → {found}")
else:
log(f"Символ НЕ найден: {base}")
if not real_symbols:
log("Не найдено ни одного символа. Проверь брокера.")
return
while True:
try:
log("-" * 40)
for base, symbol in real_symbols.items():
trend = get_trend(symbol)
log(f"{symbol}: тренд = {trend}")
if trend in ["bull", "bear"]:
signal = check_signal(symbol, trend)
if signal:
log(f"{symbol}: !!! СИГНАЛ {signal.upper()} !!!")
open_trade(symbol, signal)
else:
log(f"{symbol}: сигнала нет")
else:
log(f"{symbol}: боковик, пропускаем")
time.sleep(20) # проверка каждые 20 секунд
except Exception as e:
log(f"Ошибка в цикле: {e}")
time.sleep(10)
if __name__ == "__main__":
main()