132 KiB
132 KiB
In [1]:
def dynamic_update_sl_tp(symbol, atr_mult=1.5, rrr_factor=None, check_tf='m5', trail_buffer=0.5, max_retries=2):
"""
Dynamisches Nachziehen von SL/TP für offene Trades.
Läuft idealerweise bei jedem neuen Kerzenende von check_tf (z.B. m5) oder bei Intrabar-ATR-Sprüngen.
"""
# load recent bars for the timeframe
df = get_rates(check_tf).iloc[-3:]
if df is None or df.empty or len(df) < 2:
return
atr = float(df['atr'].iloc[-1]) if 'atr' in df.columns else None
if atr is None or atr == 0:
return
# quick check for intrabar move (previous close -> current close)
prev = float(df['close'].iloc[-2])
curr = float(df['close'].iloc[-1])
price_move = abs(curr - prev)
# only update on new bar or large intrabar move (>= 0.5 * ATR)
if price_move < 0.5 * atr and False:
return
positions = mt.positions_get(symbol=symbol) or []
for pos in positions:
# normalize pos interface (support dict-like or object)
try:
ticket = pos.ticket
entry_price = pos.price_open
pos_type = pos.type # 0=buy,1=sell
cur_bid = mt.symbol_info_tick(symbol).bid
cur_ask = mt.symbol_info_tick(symbol).ask
except Exception:
# fallback for dict-like
ticket = pos.get('ticket', None)
entry_price = pos.get('price_open', pos.get('entry_price'))
pos_type = pos.get('type')
cur_bid = mt.symbol_info_tick(symbol).bid
cur_ask = mt.symbol_info_tick(symbol).ask
current_price = cur_bid if pos_type == 0 else cur_ask
# compute desired new SL/TP using update_trailing_sl_tp logic
# profit in ATR
profit_atr = (current_price - entry_price) / atr if pos_type == 0 else (entry_price - current_price) / atr
# base sl
if pos_type == 0:
base_sl = current_price - atr_mult * atr
new_sl = max((pos.sl or 0), base_sl)
if profit_atr > 2:
new_sl = max(new_sl, entry_price + 1.5 * atr)
elif profit_atr > 1:
new_sl = max(new_sl, entry_price + 1.0 * atr)
elif profit_atr > 0.5:
new_sl = max(new_sl, entry_price + 0.5 * atr)
new_tp = entry_price + (entry_price - new_sl) * (rrr_factor or 2.0)
else:
base_sl = current_price + atr_mult * atr
new_sl = min((pos.sl or 999999), base_sl)
if profit_atr > 2:
new_sl = min(new_sl, entry_price - 1.5 * atr)
elif profit_atr > 1:
new_sl = min(new_sl, entry_price - 1.0 * atr)
elif profit_atr > 0.5:
new_sl = min(new_sl, entry_price - 0.5 * atr)
new_tp = entry_price - (new_sl - entry_price) * (rrr_factor or 2.0)
# respect broker stops level and rounding
info = mt.symbol_info(symbol)
digits = info.digits
point = info.point
stops_level = info.trade_stops_level * point
tick = mt.symbol_info_tick(symbol)
bid, ask = tick.bid, tick.ask
if pos_type == 0:
if new_sl >= bid - stops_level:
new_sl = bid - stops_level
if new_tp <= ask + stops_level:
new_tp = ask + stops_level
else:
if new_sl <= ask + stops_level:
new_sl = ask + stops_level
if new_tp >= bid - stops_level:
new_tp = bid - stops_level
new_sl = round(new_sl, digits)
new_tp = round(new_tp, digits)
# only update if change larger than point
if (pos.sl is None or abs(new_sl - pos.sl) > point) or (pos.tp is None or abs(new_tp - pos.tp) > point):
for attempt in range(max_retries):
req = {
'action': mt.TRADE_ACTION_SLTP,
'symbol': symbol,
'position': ticket,
'sl': new_sl,
'tp': new_tp
}
res = mt.order_send(req)
if getattr(res, 'retcode', None) == mt.TRADE_RETCODE_DONE:
print(f"Updated SL/TP for {symbol} #{ticket}: SL={new_sl}, TP={new_tp}")
break
elif getattr(res, 'retcode', None) == mt.TRADE_RETCODE_INVALID_STOPS:
adjust = 2 * stops_level
if pos_type == 0:
new_sl = round(bid - adjust, digits)
new_tp = round(ask + adjust, digits)
else:
new_sl = round(ask + adjust, digits)
new_tp = round(bid - adjust, digits)
continue
else:
print('SL/TP update failed', getattr(res, 'retcode', None), getattr(res, 'comment', None))
break
def linreg_slope(series):
"""Central linear regression slope helper (returns slope as float)."""
from sklearn.linear_model import LinearRegression
import numpy as np
if series is None or len(series) < 3:
return 0.0
X = np.arange(len(series)).reshape(-1, 1)
y = series.values.reshape(-1, 1)
model = LinearRegression().fit(X, y)
return float(model.coef_[0][0])
#!pip install ta_lib-0.6.5-cp311-cp311-win_amd64.whl
#%pip install talib
#!pip install ta
from ta.trend import ADXIndicator, EMAIndicator
from ta.momentum import RSIIndicator
from talib import CDLHAMMER, CDLSHOOTINGSTAR
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
import keyring as kr
import MetaTrader5 as mt
import requests
import re
from time import sleep
import sqlite3 as db
#import matplotlib.pyplot as plt
import pandas_ta as ta
import numpy as np
from sklearn.linear_model import LinearRegression
from scipy.signal import savgol_filter
from scipy.signal import find_peaks
# login to your Trading Account - sign up in the description
mt.initialize()
login = 10800246
server = 'VantageInternational-Demo'
password = kr.get_password(server, str(login))
mt.login(login, password, server)
project = "trading-" + server[-4::1]
project = project.lower()
project
pause_trading = 0
symbols = ['XAUUSD']
#symbols = ['BTCUSD']
#'BTCUSD', 'ETHUSD',
#'XRPUSD', , 'EURNZD', 'EURUSD'
volume_dict = {
'BTCUSD' : 0.1,
'BTCUSD_short' : 0.1,
'ETHUSD' : 1.0,
'ETHUSD_short' : 1.0,
'XRPUSD': 0.1,
'XRPUSD_short': 0.1,
'XAUUSD': 0.1,
'XAUUSD_short': 0.1,
'EURUSD': 0.1,
'EURUSD_short': 0.1,
'EURNZD': 0.1,
'EURNZD_short': 0.1,
}
periods_dict = {
'BTCUSD' : ['h1', 'm30', 'm15', 'm5', 'm1'],
'ETHUSD' : ['h1', 'm30', 'm15', 'm5', 'm1'],
'XRPUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XAUUSD': [ 'm15'],
'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'EURNZD': ['m5', 'm2', 'm1'],
}
def get_symbol():
global symbols, pause_trading, periods_dict
pricemovement = {}
for s in symbols:
items = mt.symbol_info(s)
pricemovement[s] = round(items.price_change,2)
#print(s, round(items.price_change,2))
#percentage = pricemovement[max(pricemovement, key=pricemovement.get)]
#symbol = max(pricemovement, key=pricemovement.get)
#volume = volume_dict[symbol]
sorted_pricemovement = sorted(pricemovement.items(), key=lambda x:x[1], reverse=True)
converted_dict = dict(sorted_pricemovement)
print(converted_dict)
percentage = converted_dict[max(converted_dict, key=converted_dict.get)]
symbol = max(converted_dict, key=converted_dict.get)
volume = volume_dict[symbol]
print(symbol, percentage, volume)
# if percentage > 0: # and percentage < 0.8:
# periods_dict[symbol] = ['m15', 'm5', 'm2', 'm1']
# elif percentage > 0.8:
# periods_dict[symbol] = ['h1', 'm30', 'm15', 'm5', 'm1']
#
#print(periods_dict)
# if percentage > 0 and mt.positions_total() == 0:
# pause_trading = 0 #0 no puase
# return symbol, percentage, volume, periods_dict
# elif percentage < 0.1 and mt.positions_total() == 0:
# print("aktuell kein neues Symbol, pause Trading")
# pause_trading = 1 #1 pause
# return None
return symbol, percentage, volume, periods_dict
get_symbol()
def set_symbol():
global symbol, volume, volume_dict
symb = get_symbol()
if symb != None:
symbol = symb[0]
volume = volume_dict[symb[0]]
get_symbol()
set_symbol()
symbol, volume, pause_trading
pos = mt.positions_get()
for i in pos:
#if i.comment == 'Retracement Bot':
# print(i.ticket)
if bool(re.search('^BuyStop[0-9]{2}', i.comment)):
print(i.ticket)
mt.positions_total()
strategy_name = 'Retracement Bot'
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
print(p.comment)
print(pos)
def market_order(symbol, volume, order_type, deviation=20, magic=30, stoploss=0.0, take_profit=0.0,
strategy_name='Retracement Bot'):
global project, pause_trading
project_id_dict = {
'trading-demo': 'a3f3ae',
'trading-live': '747543'
}
order_type_dict = {
'buy': mt.ORDER_TYPE_BUY,
'sell': mt.ORDER_TYPE_SELL
}
price_dict = {
'buy': mt.symbol_info_tick(symbol).ask,
'sell': mt.symbol_info_tick(symbol).bid
}
buypos = []
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
buypos.append('true')
activepos = buypos.count('true')
if order_type == 'buy' and activepos == 0 and pause_trading == 0: # and mt.positions_total() == 0:
request = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": volume, # FLOAT
"type": order_type_dict[order_type],
"price": price_dict[order_type],
"sl": stoploss, # FLOAT
"tp": take_profit, # FLOAT
"deviation": deviation, # INTERGER
"magic": magic, # INTERGER
"comment": strategy_name,
"type_time": mt.ORDER_TIME_GTC,
"type_filling": mt.ORDER_FILLING_IOC, # mt.ORDER_FILLING_FOK if IOC does not work
}
requests.post('https://api.mynotifier.app', {
"apiKey": 'beafb52e-3cb6-477a-92ef-2f10bff50e20',
"message": "Es wrude ein Handel eröffnet!",
"description": "Bitte kontrolliere die Position",
"type": "info",#"info", # info, error, warning or success
"project": project_id_dict[project]
})
order_result = mt.order_send(request)
#return (order_result)
elif order_type == 'sell' and mt.positions_total() > 0:
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
# while schleife ?
positions = mt.positions_get()
ticket = p.ticket
request = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": volume, # FLOAT
"type": order_type_dict[order_type],
"price": price_dict[order_type],
"position": ticket,
"sl": stoploss, # FLOAT
"tp": take_profit, # FLOAT
"deviation": deviation, # INTERGER
"magic": magic, # INTERGER
"comment": strategy_name,
"type_time": mt.ORDER_TIME_GTC,
"type_filling": mt.ORDER_FILLING_IOC, # mt.ORDER_FILLING_FOK if IOC does not work
}
requests.post('https://api.mynotifier.app', {
"apiKey": 'beafb52e-3cb6-477a-92ef-2f10bff50e20',
"message": "Es wrude ein Handel geschlossen!",
"description": "Bitte prüfe die Position",
"type": "info",#"info", # info, error, warning or success
"project": project_id_dict[project]
})
order_result = mt.order_send(request)
#return (order_result)
debug = False
trend_dict = {
#'m5': '',
#'m10': '',
'm15': '',
#'m30': '',
#'h4': '',
}
timeframes_dict = {
'm1': mt.TIMEFRAME_M1,
'm2': mt.TIMEFRAME_M2,
'm3': mt.TIMEFRAME_M3,
'm5': mt.TIMEFRAME_M5,
'm15': mt.TIMEFRAME_M15,
'm20': mt.TIMEFRAME_M20,
'm30': mt.TIMEFRAME_M30,
'h1': mt.TIMEFRAME_H1,
'h4': mt.TIMEFRAME_H4,
'd1': mt.TIMEFRAME_D1,
}
# timeframes = {
# 'm1': mt.TIMEFRAME_M1,
# 'm2': mt.TIMEFRAME_M2,
# 'm3': mt.TIMEFRAME_M3,
# 'm5': mt.TIMEFRAME_M5,
# 'm15': mt.TIMEFRAME_M15,
# 'm20': mt.TIMEFRAME_M20,
# 'm30': mt.TIMEFRAME_M30,
# 'h1': mt.TIMEFRAME_H1,
# }
print(trend_dict)
def get_rates(periode, bars=300):
#global symbol
# OHLC abrufen
ohlc = mt.copy_rates_from_pos(symbol, timeframes_dict[periode], 0, bars)
df = pd.DataFrame(ohlc)
df['time'] = pd.to_datetime(df['time'], unit='s')
# Umwandeln in float
df["open"] = df["open"].astype(float)
df["high"] = df["high"].astype(float)
df["low"] = df["low"].astype(float)
df["close"] = df["close"].astype(float)
# ATR berechnen (klassisch 14)
df["atr"] = ta.atr(high=df["high"], low=df["low"], close=df["close"], length=14)
# leichte Glättung (optional, um Rauschen zu reduzieren)
df["atr"] = df["atr"].rolling(window=5).mean()
# Index setzen
df.set_index("time", inplace=True)
# NaNs entfernen (anfangs durch ATR-Berechnung)
df = df.dropna()
return df
get_rates('h4', 200)
def get_trend(timeframe="h4", lookback=150):
"""
Bestimme Trendrichtung per Linear Regression.
Liefert ein dict mit keys: trend (uptrend/downtrend/sideways), slope, atr, slope_threshold, price.
"""
import numpy as np
df = get_rates(timeframe, bars=lookback)
if df is None or df.empty:
return {"trend": "sideways", "slope": 0.0, "atr": 0.0, "slope_threshold": 0.0, "price": None}
slope = linreg_slope(df['close'].iloc[-lookback:])
# Ensure ATR exists
if 'atr' not in df.columns or df['atr'].isnull().all():
# fallback ATR calc
hl = df['high'] - df['low']
hc = (df['high'] - df['close'].shift()).abs()
lc = (df['low'] - df['close'].shift()).abs()
tr = pd.concat([hl, hc, lc], axis=1).max(axis=1)
atr_series = tr.rolling(14, min_periods=1).mean()
atr = float(atr_series.iloc[-1])
else:
atr = float(df['atr'].iloc[-1])
current_price = float(df['close'].iloc[-1])
slope_threshold = (atr / current_price) * 1.2 if current_price and atr else 0.0
if abs(slope) < slope_threshold:
trend = 'sideways'
else:
trend = 'uptrend' if slope > 0 else 'downtrend'
return {"trend": trend, "slope": float(slope), "atr": atr, "slope_threshold": float(slope_threshold), "price": current_price}
def get_top_down_signal(symbol=symbol):
"""
Top-Down Ansatz:
- D1 / H4: Trend bestimmen
- H1 / M30: Setup identifizieren
- M15 / M5: Einstiege optimieren (Signale ohne Tradeausführung)
"""
global trend_dict
# --- Höhere Timeframes ---
d1_trend_info = get_trend("d1", lookback=200)
h4_trend_info = get_trend("h4", lookback=150)
# --- Mittlere Timeframes für Setups ---
h1_trend_info = get_trend("h1", lookback=100)
m30_trend_info = get_trend("m30", lookback=60)
# --- Niedrigere Timeframes für Einstiege (nur Signal, kein Trade) ---
m15_trend_info = get_trend("m15", lookback=50)
m5_trend_info = get_trend("m5", lookback=20)
# --- Konsolidierte Trendanalyse ---
top_down_trend = "sideways"
if d1_trend_info["trend"] == h4_trend_info["trend"]:
top_down_trend = d1_trend_info["trend"]
# --- Setup-Bedingungen ---
setup_ready = False
if top_down_trend != "sideways":
if h1_trend_info["trend"] == top_down_trend and m30_trend_info["trend"] == top_down_trend:
setup_ready = True
# --- Einstiegsberechnung ---
entry_signal = 0
if setup_ready:
if m15_trend_info["trend"] == top_down_trend and m5_trend_info["trend"] == top_down_trend:
entry_signal = 1 if top_down_trend == "uptrend" else -1
# --- Speichern ---
trend_dict["top_down"] = {
"D1": d1_trend_info,
"H4": h4_trend_info,
"H1": h1_trend_info,
"M30": m30_trend_info,
"M15": m15_trend_info,
"M5": m5_trend_info,
"top_down_trend": top_down_trend,
"setup_ready": setup_ready,
"entry_signal": entry_signal
}
return trend_dict["top_down"]
get_trend("h1")
get_top_down_signal(symbol)
get_trend()
def get_trend_fast(period):
global trend_dict, periods_dict, symbol
df2 = get_rates(period).iloc[-200:]
df2["close_smooth"] = savgol_filter(df2.close, 15, 5) # kleinere Glättung
atr = df2.atr.iloc[-1]
# Weniger strenge Peak-Erkennung
peaks_idx, _ = find_peaks(df2.close_smooth, distance=1, width=2, prominence=atr*0.5) #evtl kleiner 0.5
troughs_idx, _ = find_peaks(-1*df2.close_smooth, distance=1, width=2, prominence=atr*0.5)
# Trend über Peaks/Troughs
if len(peaks_idx) > 0 and len(troughs_idx) > 0:
if peaks_idx[-1] > troughs_idx[-1]:
trend = "downtrend"
else:
trend = "uptrend"
else:
# Falls keine klaren Peaks gefunden wurden → Slope nutzen
slope = df2.close_smooth.diff().iloc[-5:].mean()
trend = "downtrend" if slope < 0 else "uptrend"
trend_dict[period] = trend
return trend
get_trend_fast('m15')
def set_trend():
global pause_trading, periods_dict, trend_dict
for k,v in trend_dict.items():
get_trend_fast(k)
print(k)
for k,v in reversed(trend_dict.items()):
if v == 'uptrend':
print(k,v)
periods_dict[symbol] = [k]
pause_trading = 0
print(f"Pause Trading: {pause_trading}")
break
elif v == 'downtrend':
periods_dict[symbol] = [k] #['m15']
pause_trading = 1
print(f"Pause Trading: {pause_trading}")
set_trend()
pause_trading, trend_dict, periods_dict, symbols[0]
set_trend()
trend_dict[periods_dict[symbol][0]]
periods_dict[symbol], pause_trading
def get_mt5_data(symbol=symbol, timeframe=mt.TIMEFRAME_M15, n_bars=500):
rates = mt.copy_rates_from_pos(symbol, timeframe, 0, n_bars)
df = pd.DataFrame(rates)
df["time"] = pd.to_datetime(df["time"], unit="s")
return df
def get_m5_trade_signal(symbol, atr_mult=1.5):
global trend_dict, periods_dict
# Hole die letzten M5-Daten
df = get_rates('m5').iloc[-200:]
# Smoothen
df["close_smooth_std"] = savgol_filter(df.close, 25, 5)
df["close_smooth_fast"] = savgol_filter(df.close, 15, 5)
atr = df.atr.iloc[-1]
# --- Standard-Trend ---
peaks_std, _ = find_peaks(df.close_smooth_std, distance=1, width=2, prominence=atr)
troughs_std, _ = find_peaks(-df.close_smooth_std, distance=1, width=2, prominence=atr)
if len(peaks_std) > 0 and len(troughs_std) > 0:
if peaks_std[-1] > troughs_std[-1]:
trend_standard = "downtrend"
else:
trend_standard = "uptrend"
else:
trend_standard = "neutral"
# --- Fast-Trend ---
peaks_fast, _ = find_peaks(df.close_smooth_fast, distance=1, width=2, prominence=atr*0.5)
troughs_fast, _ = find_peaks(-df.close_smooth_fast, distance=1, width=2, prominence=atr*0.5)
if len(peaks_fast) > 0 and len(troughs_fast) > 0:
if peaks_fast[-1] > troughs_fast[-1]:
trend_fast = "downtrend"
else:
trend_fast = "uptrend"
else:
slope = df.close_smooth_fast.diff().iloc[-5:].mean()
trend_fast = "downtrend" if slope < 0 else "uptrend"
# --- Kombiniertes Signal ---
signal = 0 # 0 = neutral, 1 = long, -1 = short
stop_loss = None
if trend_standard == "uptrend" and trend_fast == "uptrend":
signal = 1
stop_loss = df.close.iloc[-1] - atr_mult * atr
elif trend_standard == "downtrend" and trend_fast == "downtrend":
signal = -1
stop_loss = df.close.iloc[-1] + atr_mult * atr
# Speichern
trend_dict['m5'] = {"standard": trend_standard, "fast": trend_fast, "signal": signal}
return {
"signal": signal,
"price": df.close.iloc[-1],
"stop_loss": stop_loss,
"trends": trend_dict['m15']
}
def generate_signal(df = get_mt5_data(symbol=symbol, timeframe=timeframes_dict['m15']), confirm_window=3):
"""
Berechnet drei Signalarten:
- fast_signal: schnelle, aggressive Variante
- standard_signal: konservative Basisstrategie
- optimized_signal: zusätzliche Filter (ATR, ADX, strengerer RSI)
"""
# Indikatoren
df["ema21"] = df["close"].ewm(span=3).mean()
df["ema50"] = df["close"].ewm(span=9).mean()
df["rsi9"] = ta.rsi(df["close"], length=9)
df["rsi14"] = ta.rsi(df["close"], length=14)
df["trend"] = savgol_filter(df["close"], 25, 3)
# Zusätzliche Filterindikatoren
df["atr"] = ta.atr(df["high"], df["low"], df["close"], length=14)
df["adx"] = ta.adx(df["high"], df["low"], df["close"], length=14)["ADX_14"]
# Spalten für Signale
df["fast_signal"] = 0
df["standard_signal"] = 0
df["optimized_signal"] = 0
for i in range(1, len(df)):
# -----------------------
# FAST SIGNAL (früh/aggressiv)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["rsi9"].iloc[i] > 30
):
df.at[i, "fast_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["rsi9"].iloc[i] < 60
):
df.at[i, "fast_signal"] = -1
# -----------------------
# STANDARD SIGNAL (konservativ, ursprüngliche Logik)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] <= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] < 70
and df["rsi9"].iloc[i] > 40
and df["trend"].iloc[i] > df["trend"].iloc[i - 1]
):
df.at[i, "standard_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] >= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] > 40
and df["rsi9"].iloc[i] < 70
and df["trend"].iloc[i] < df["trend"].iloc[i - 1]
):
df.at[i, "standard_signal"] = -1
# -----------------------
# OPTIMIZED SIGNAL (mit ADX + ATR + strengerem RSI)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] <= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] < 65 # strengerer Filter
and df["rsi9"].iloc[i] > 45 # Momentum klarer
and df["adx"].iloc[i] > 20 # Trendstärke vorhanden
and df["atr"].iloc[i] > df["atr"].rolling(50).mean().iloc[i] # Volatilität über Durchschnitt
):
df.at[i, "optimized_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] >= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] > 35 # strengerer Filter unten
and df["rsi9"].iloc[i] < 55
and df["adx"].iloc[i] > 20
and df["atr"].iloc[i] > df["atr"].rolling(50).mean().iloc[i]
):
df.at[i, "optimized_signal"] = -1
return df
generate_signal()
pos = mt.positions_total()
if pos > 0:
open_positions = mt.positions_get()
trail_factor = 0.5
entry_price = open_positions[0].price_open
tp_current = open_positions[0].tp
current_price = open_positions[0].price_current
profit = open_positions[0].profit
profit = current_price - entry_price
if profit > 0:
new_tp = current_price - entry_price * trail_factor
if profit < 0:
new_tp = current_price - profit * trail_factor
if new_tp > current_price:
#update tp from open pos
new_tp
print(f"Trailing +TP for BUY {symbol}: {new_tp} CP: {current_price}")
if new_tp < current_price:
#update tp from open pos
new_tp
print(f"Trailing -TP for BUY {symbol}: {new_tp} CP: {current_price}")
#open_positions
def update_trailing_sl_tp(pos, atr, rrr=2.0, atr_mult=1.5, max_retries=2):
"""
Aktualisiert SL und TP für offene Positionen:
- ATR-basiertes Trailing
- Gewinn-stufenweises Nachziehen
- Dynamische Anpassung mit Validierung
- Fallback-Mechanismus bei RETCODE 1016
"""
symbol = pos.symbol
info = mt.symbol_info(symbol)
digits = info.digits
point = info.point
stops_level = info.trade_stops_level * point # Mindestabstand vom Broker
entry_price = pos.price_open
current_tick = mt.symbol_info_tick(symbol)
bid, ask = current_tick.bid, current_tick.ask
current_price = bid if pos.type == 0 else ask
pos_type = pos.type # 0 = BUY, 1 = SELL
# Gewinn in ATR berechnen
if pos_type == 0: # LONG
profit_atr = (current_price - entry_price) / atr
base_sl = current_price - atr_mult * atr
new_sl = max(pos.sl or 0, base_sl)
if profit_atr > 2:
new_sl = max(new_sl, entry_price + 1.5 * atr)
elif profit_atr > 1:
new_sl = max(new_sl, entry_price + 1.0 * atr)
elif profit_atr > 0.5:
new_sl = max(new_sl, entry_price + 0.5 * atr)
new_tp = entry_price + (entry_price - new_sl) * rrr
# Validierung BUY
if new_sl >= bid - stops_level:
new_sl = bid - stops_level
if new_tp <= ask + stops_level:
new_tp = ask + stops_level
else: # SHORT
profit_atr = (entry_price - current_price) / atr
base_sl = current_price + atr_mult * atr
new_sl = min(pos.sl or 999999, base_sl)
if profit_atr > 2:
new_sl = min(new_sl, entry_price - 1.5 * atr)
elif profit_atr > 1:
new_sl = min(new_sl, entry_price - 1.0 * atr)
elif profit_atr > 0.5:
new_sl = min(new_sl, entry_price - 0.5 * atr)
new_tp = entry_price - (new_sl - entry_price) * rrr
# Validierung SELL
if new_sl <= ask + stops_level:
new_sl = ask + stops_level
if new_tp >= bid - stops_level:
new_tp = bid - stops_level
# Runden auf gültige Stellen
new_sl = round(new_sl, digits)
new_tp = round(new_tp, digits)
# --- Nur updaten, wenn sich Werte geändert haben ---
if (pos.sl is None or abs(new_sl - pos.sl) > point) or \
(pos.tp is None or abs(new_tp - pos.tp) > point):
for attempt in range(max_retries):
request = {
"action": mt.TRADE_ACTION_SLTP,
"symbol": symbol,
"sl": new_sl,
"tp": new_tp,
"position": pos.ticket
}
result = mt.order_send(request)
if result.retcode == mt.TRADE_RETCODE_DONE:
print(f"🔄 Updated {symbol} | SL: {new_sl:.5f} | TP: {new_tp:.5f}")
break
elif result.retcode == mt.TRADE_RETCODE_INVALID_STOPS:
# Fallback: Stops korrigieren
print(f"⚠️ RETCODE 1016 (Invalid stops) – Versuch {attempt+1}/{max_retries}")
adjust = 2 * stops_level # mehr Abstand
if pos_type == 0: # BUY
new_sl = bid - adjust
new_tp = ask + adjust
else: # SELL
new_sl = ask + adjust
new_tp = bid - adjust
new_sl = round(new_sl, digits)
new_tp = round(new_tp, digits)
continue # retry
else:
print(f"❌ SL/TP Update Fehler: {result.retcode} ({result.comment})")
break
return {"new_sl": new_sl, "new_tp": new_tp, "profit_atr": profit_atr}
def manual_update_trailing_sl_tp(atr_mult=1.5, slope_factor=1.5):
# --- Hole die letzten M5-Daten ---
df = get_rates('m5').iloc[-200:]
# --- ATR Berechnung ---
df["hl"] = df["high"] - df["low"]
df["hc"] = (df["high"] - df["close"].shift()).abs()
df["lc"] = (df["low"] - df["close"].shift()).abs()
df["tr"] = df[["hl","hc","lc"]].max(axis=1)
df["atr"] = df["tr"].rolling(14).mean()
atr = df["atr"].iloc[-1]
current_price = df["close"].iloc[-1]
# --- Trendrichtung per Linear Regression ---
def linreg_slope(series):
X = np.arange(len(series)).reshape(-1, 1)
y = series.values.reshape(-1, 1)
model = LinearRegression().fit(X, y)
return model.coef_[0][0]
slope_long = linreg_slope(df["close"].iloc[-50:]) # 50 Balken (~4h)
slope_short = linreg_slope(df["close"].iloc[-15:]) # 15 Balken (~1h)
# --- Dynamischer Seitwärtsfilter ---
slope_threshold = slope_factor * atr / df["close"].iloc[-1]
# --- Dynamische RRR-Berechnung ---
atr_norm = atr / current_price # relative Volatilität
slope_strength = abs(slope_short) # Trendstärke aus Regression
rrr_base = 1.2
rrr_from_vol = atr_norm * 1500 # skaliert ATR-Einfluss
rrr_from_slope = slope_strength / slope_threshold # Slope-Einfluss
rrr = rrr_base + rrr_from_vol + rrr_from_slope
rrr = max(1.2, min(rrr, 3.0)) # Begrenzung
open_positions = mt.positions_get(symbol=symbol)
for pos in open_positions:
atr_value = df["atr"].iloc[-1]
update_trailing_sl_tp(pos, atr=atr_value, rrr=rrr, atr_mult=atr_mult)
manual_update_trailing_sl_tp()
def get_m5_trade_signals(symbol=symbol, atr_mult=1.5, base_rrr=2.0, atr_min=0.0005, slope_factor=1.5, place_orders=False):
"""
M5/M15 analysis-only signal generator. By default it does NOT place orders.
Set place_orders=True to allow execution (not recommended when called from top-down analysis).
Returns a dict with signal, price, stop_loss, take_profit, Risk Reward, atr and slopes.
"""
# use m15 data for setup if available, otherwise m5
df = get_rates('m15').iloc[-200:]
if df is None or df.empty or len(df) < 30:
return {"signal": 0, "reason": "not enough data"}
# ATR fallback (ensure column exists)
if 'atr' not in df.columns or df['atr'].isnull().all():
df['hl'] = df['high'] - df['low']
df['hc'] = (df['high'] - df['close'].shift()).abs()
df['lc'] = (df['low'] - df['close'].shift()).abs()
df['tr'] = df[['hl','hc','lc']].max(axis=1)
df['atr'] = df['tr'].rolling(14, min_periods=1).mean()
atr = float(df['atr'].iloc[-1])
if atr < atr_min:
return {"signal": 0, "reason": "ATR too low → sideways", "atr": atr}
slope_long = linreg_slope(df['close'].iloc[-50:])
slope_short = linreg_slope(df['close'].iloc[-15:])
slope_threshold = slope_factor * atr / float(df['close'].iloc[-1])
if abs(slope_long) < slope_threshold and abs(slope_short) < slope_threshold:
return {"signal": 0, "reason": "Trend flat → sideways", "atr": atr,
"slope_long": slope_long, "slope_short": slope_short}
trend_standard = 'uptrend' if slope_long > 0 else 'downtrend'
trend_fast = 'uptrend' if slope_short > 0 else 'downtrend'
current_price = float(df['close'].iloc[-1])
# adaptive RRR
atr_norm = atr / current_price
slope_strength = abs(slope_short)
rrr_base = 1.2
rrr_from_vol = atr_norm * 1500
rrr_from_slope = slope_strength / (slope_threshold if slope_threshold!=0 else 1e-9)
rrr = rrr_base + rrr_from_vol + rrr_from_slope
rrr = max(1.2, min(rrr, 3.0))
stop_loss = None
take_profit = None
signal = 0
if trend_standard == 'uptrend' and trend_fast == 'uptrend':
signal = 1
stop_loss = current_price - atr_mult * atr
take_profit = current_price + atr_mult * atr * rrr
if place_orders:
market_order(symbol, volume_dict.get(symbol, 0.0), 'buy', stoploss=stop_loss, take_profit=take_profit)
elif trend_standard == 'downtrend' and trend_fast == 'downtrend':
signal = -1
stop_loss = current_price + atr_mult * atr
take_profit = current_price - atr_mult * atr * rrr
if place_orders:
market_order(symbol, volume_dict.get(symbol, 0.0), 'sell', stoploss=stop_loss, take_profit=take_profit)
return {
'signal': signal,
'price': current_price,
'stop_loss': stop_loss,
'take_profit': take_profit,
'Risk Reward': rrr,
'atr': atr,
'slope_long': slope_long,
'slope_short': slope_short,
'trend_standard': trend_standard,
'trend_fast': trend_fast
}
def execute_m5_trade(symbol=symbol, atr_mult=1.5, base_rrr=2.0):
"""Execute trade only if Top-Down approves. Uses get_top_down_signal and get_m5_trade_signals (analysis).
"""
global volume_dict, pause_trading
if pause_trading == 1:
print('Trading paused, skipping execution')
return None
top = get_top_down_signal(symbol)
if not top.get('setup_ready') or top.get('entry_signal',0) == 0:
print('Top-Down not ready or no entry signal, aborting execution')
return None
# get m5 analysis (no auto-order)
m5 = get_m5_trade_signals(symbol=symbol, atr_mult=atr_mult, base_rrr=base_rrr, place_orders=False)
if m5.get('signal',0) == 0:
print('M5 analysis returned no entry, abort')
return None
# execute using the M5-calculated SL/TP
direction = 'buy' if m5['signal'] == 1 else 'sell'
print(f'Executing {direction} {symbol} @ {m5["price"]} SL={m5["stop_loss"]} TP={m5["take_profit"]}')
market_order(symbol, volume_dict.get(symbol,0.0), direction, stoploss=m5['stop_loss'], take_profit=m5['take_profit'])
# after entry, run dynamic trailing update once
dynamic_update_sl_tp(symbol, atr_mult=atr_mult, rrr_factor=m5['Risk Reward'], check_tf='m5')
return {'executed': True, 'direction': direction, 'm5': m5, 'topdown': top}
execute_m5_trade()
from apscheduler.schedulers.background import BackgroundScheduler
import time
scheduler = BackgroundScheduler()
#scheduler.add_job(main, 'date', run_date='2025-03-07 14:29:50')
#scheduler.add_job(decide_order, 'interval', minutes=1) #intervall
#scheduler.add_job(set_symbol, 'interval', minutes=30)
#scheduler.add_job(set_trend, 'interval', minutes=1)
#scheduler.add_job(decide_order, 'interval', minutes=1)
#scheduler.add_job(decide_order, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*') #cron
#scheduler.add_job(get_buy_sell_signal, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
#scheduler.add_job(get_m5_trade_signals, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
scheduler.add_job(execute_m5_trade, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
scheduler.add_job(manual_update_trailing_sl_tp, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*') #cron
#scheduler.add_job(export_marketview, 'cron', year="*", month='*', day_of_week='mon, tue, wed; thu, fri', hour='8-22', minute=00)
#scheduler.add_job(pause_trading, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour=22, minute=00) #cron
scheduler.start()
scheduler.get_jobs()
scheduler.remove_all_jobs()
scheduler.shutdown()In [2]:
#%pip install talibIn [3]:
#!pip install ta
from ta.trend import ADXIndicator, EMAIndicator
from ta.momentum import RSIIndicator
from talib import CDLHAMMER, CDLSHOOTINGSTARIn [4]:
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
import keyring as kr
import MetaTrader5 as mt
import requests
import re
from time import sleep
import sqlite3 as db
#import matplotlib.pyplot as plt
import pandas_ta as ta
import numpy as np
from sklearn.linear_model import LinearRegression
from scipy.signal import savgol_filter
from scipy.signal import find_peaks
In [5]:
# login to your Trading Account - sign up in the description
mt.initialize()
login = 10800246
server = 'VantageInternational-Demo'
password = kr.get_password(server, str(login))
mt.login(login, password, server)Out [5]:
True
In [6]:
project = "trading-" + server[-4::1]
project = project.lower()
projectOut [6]:
'trading-demo'
In [7]:
pause_trading = 0
In [8]:
symbols = ['XAUUSD']
#symbols = ['BTCUSD']
#'BTCUSD', 'ETHUSD',
#'XRPUSD', , 'EURNZD', 'EURUSD'In [9]:
volume_dict = {
'BTCUSD' : 0.1,
'BTCUSD_short' : 0.1,
'ETHUSD' : 1.0,
'ETHUSD_short' : 1.0,
'XRPUSD': 0.1,
'XRPUSD_short': 0.1,
'XAUUSD': 0.1,
'XAUUSD_short': 0.1,
'EURUSD': 0.1,
'EURUSD_short': 0.1,
'EURNZD': 0.1,
'EURNZD_short': 0.1,
}In [10]:
periods_dict = {
'BTCUSD' : ['h1', 'm30', 'm15', 'm5', 'm1'],
'ETHUSD' : ['h1', 'm30', 'm15', 'm5', 'm1'],
'XRPUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XAUUSD': [ 'm15'],
'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'EURNZD': ['m5', 'm2', 'm1'],
}In [11]:
def get_symbol():
global symbols, pause_trading, periods_dict
pricemovement = {}
for s in symbols:
items = mt.symbol_info(s)
pricemovement[s] = round(items.price_change,2)
#print(s, round(items.price_change,2))
#percentage = pricemovement[max(pricemovement, key=pricemovement.get)]
#symbol = max(pricemovement, key=pricemovement.get)
#volume = volume_dict[symbol]
sorted_pricemovement = sorted(pricemovement.items(), key=lambda x:x[1], reverse=True)
converted_dict = dict(sorted_pricemovement)
print(converted_dict)
percentage = converted_dict[max(converted_dict, key=converted_dict.get)]
symbol = max(converted_dict, key=converted_dict.get)
volume = volume_dict[symbol]
print(symbol, percentage, volume)
# if percentage > 0: # and percentage < 0.8:
# periods_dict[symbol] = ['m15', 'm5', 'm2', 'm1']
# elif percentage > 0.8:
# periods_dict[symbol] = ['h1', 'm30', 'm15', 'm5', 'm1']
#
#print(periods_dict)
# if percentage > 0 and mt.positions_total() == 0:
# pause_trading = 0 #0 no puase
# return symbol, percentage, volume, periods_dict
# elif percentage < 0.1 and mt.positions_total() == 0:
# print("aktuell kein neues Symbol, pause Trading")
# pause_trading = 1 #1 pause
# return None
return symbol, percentage, volume, periods_dictIn [12]:
get_symbol()Out [12]:
{'XAUUSD': 1.08}
XAUUSD 1.08 0.1
('XAUUSD',
1.08,
0.1,
{'BTCUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'ETHUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XRPUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XAUUSD': ['m15'],
'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'EURNZD': ['m5', 'm2', 'm1']})In [13]:
def set_symbol():
global symbol, volume, volume_dict
symb = get_symbol()
if symb != None:
symbol = symb[0]
volume = volume_dict[symb[0]]
In [14]:
get_symbol()Out [14]:
{'XAUUSD': 1.08}
XAUUSD 1.08 0.1
('XAUUSD',
1.08,
0.1,
{'BTCUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'ETHUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XRPUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XAUUSD': ['m15'],
'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'EURNZD': ['m5', 'm2', 'm1']})In [15]:
set_symbol()
{'XAUUSD': 1.08}
XAUUSD 1.08 0.1
In [16]:
symbol, volume, pause_tradingOut [16]:
('XAUUSD', 0.1, 0)In [17]:
pos = mt.positions_get()
for i in pos:
#if i.comment == 'Retracement Bot':
# print(i.ticket)
if bool(re.search('^BuyStop[0-9]{2}', i.comment)):
print(i.ticket)
mt.positions_total()Out [17]:
1
In [18]:
strategy_name = 'Retracement Bot'
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
print(p.comment)
print(pos)Retracement Bot (TradePosition(ticket=382221755, time=1756930501, time_msc=1756930501001, time_update=1756930501, time_update_msc=1756930501001, type=0, magic=30, identifier=382221755, reason=3, volume=0.1, price_open=3572.38, sl=3569.69, tp=3580.45, price_current=3572.1, swap=0.0, profit=-2.8, symbol='XAUUSD', comment='Retracement Bot', external_id=''),)
In [19]:
def market_order(symbol, volume, order_type, deviation=20, magic=30, stoploss=0.0, take_profit=0.0,
strategy_name='Retracement Bot'):
global project, pause_trading
project_id_dict = {
'trading-demo': 'a3f3ae',
'trading-live': '747543'
}
order_type_dict = {
'buy': mt.ORDER_TYPE_BUY,
'sell': mt.ORDER_TYPE_SELL
}
price_dict = {
'buy': mt.symbol_info_tick(symbol).ask,
'sell': mt.symbol_info_tick(symbol).bid
}
buypos = []
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
buypos.append('true')
activepos = buypos.count('true')
if order_type == 'buy' and activepos == 0 and pause_trading == 0: # and mt.positions_total() == 0:
request = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": volume, # FLOAT
"type": order_type_dict[order_type],
"price": price_dict[order_type],
"sl": stoploss, # FLOAT
"tp": take_profit, # FLOAT
"deviation": deviation, # INTERGER
"magic": magic, # INTERGER
"comment": strategy_name,
"type_time": mt.ORDER_TIME_GTC,
"type_filling": mt.ORDER_FILLING_IOC, # mt.ORDER_FILLING_FOK if IOC does not work
}
requests.post('https://api.mynotifier.app', {
"apiKey": 'beafb52e-3cb6-477a-92ef-2f10bff50e20',
"message": "Es wrude ein Handel eröffnet!",
"description": "Bitte kontrolliere die Position",
"type": "info",#"info", # info, error, warning or success
"project": project_id_dict[project]
})
order_result = mt.order_send(request)
#return (order_result)
elif order_type == 'sell' and mt.positions_total() > 0:
pos = mt.positions_get()
for p in pos:
if p.comment == strategy_name:
# while schleife ?
positions = mt.positions_get()
ticket = p.ticket
request = {
"action": mt.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": volume, # FLOAT
"type": order_type_dict[order_type],
"price": price_dict[order_type],
"position": ticket,
"sl": stoploss, # FLOAT
"tp": take_profit, # FLOAT
"deviation": deviation, # INTERGER
"magic": magic, # INTERGER
"comment": strategy_name,
"type_time": mt.ORDER_TIME_GTC,
"type_filling": mt.ORDER_FILLING_IOC, # mt.ORDER_FILLING_FOK if IOC does not work
}
requests.post('https://api.mynotifier.app', {
"apiKey": 'beafb52e-3cb6-477a-92ef-2f10bff50e20',
"message": "Es wrude ein Handel geschlossen!",
"description": "Bitte prüfe die Position",
"type": "info",#"info", # info, error, warning or success
"project": project_id_dict[project]
})
order_result = mt.order_send(request)
#return (order_result)
In [20]:
debug = FalseIn [21]:
trend_dict = {
#'m5': '',
#'m10': '',
'm15': '',
#'m30': '',
#'h4': '',
}In [22]:
timeframes_dict = {
'm1': mt.TIMEFRAME_M1,
'm2': mt.TIMEFRAME_M2,
'm3': mt.TIMEFRAME_M3,
'm5': mt.TIMEFRAME_M5,
'm15': mt.TIMEFRAME_M15,
'm20': mt.TIMEFRAME_M20,
'm30': mt.TIMEFRAME_M30,
'h1': mt.TIMEFRAME_H1,
'h4': mt.TIMEFRAME_H4,
'd1': mt.TIMEFRAME_D1,
}
# timeframes = {
# 'm1': mt.TIMEFRAME_M1,
# 'm2': mt.TIMEFRAME_M2,
# 'm3': mt.TIMEFRAME_M3,
# 'm5': mt.TIMEFRAME_M5,
# 'm15': mt.TIMEFRAME_M15,
# 'm20': mt.TIMEFRAME_M20,
# 'm30': mt.TIMEFRAME_M30,
# 'h1': mt.TIMEFRAME_H1,
# }In [23]:
print(trend_dict){'m15': ''}
In [24]:
def get_rates(periode, bars=300):
#global symbol
# OHLC abrufen
ohlc = mt.copy_rates_from_pos(symbol, timeframes_dict[periode], 0, bars)
df = pd.DataFrame(ohlc)
df['time'] = pd.to_datetime(df['time'], unit='s')
# Umwandeln in float
df["open"] = df["open"].astype(float)
df["high"] = df["high"].astype(float)
df["low"] = df["low"].astype(float)
df["close"] = df["close"].astype(float)
# ATR berechnen (klassisch 14)
df["atr"] = ta.atr(high=df["high"], low=df["low"], close=df["close"], length=14)
# leichte Glättung (optional, um Rauschen zu reduzieren)
df["atr"] = df["atr"].rolling(window=5).mean()
# Index setzen
df.set_index("time", inplace=True)
# NaNs entfernen (anfangs durch ATR-Berechnung)
df = df.dropna()
return df
In [25]:
get_rates('h4', 200)Out [25]:
| open | high | low | close | tick_volume | spread | real_volume | atr | |
|---|---|---|---|---|---|---|---|---|
| time | ||||||||
| 2025-07-23 16:00:00 | 3419.98 | 3420.52 | 3381.47 | 3387.38 | 89692 | 19 | 0 | 13.861980 |
| 2025-07-23 20:00:00 | 3387.33 | 3395.94 | 3385.74 | 3386.78 | 49534 | 19 | 0 | 14.128410 |
| 2025-07-24 00:00:00 | 3388.01 | 3393.16 | 3386.48 | 3391.44 | 18496 | 19 | 0 | 14.305667 |
| 2025-07-24 04:00:00 | 3391.44 | 3393.36 | 3374.71 | 3382.57 | 56315 | 19 | 0 | 14.547405 |
| 2025-07-24 08:00:00 | 3382.56 | 3382.92 | 3365.82 | 3369.68 | 54663 | 19 | 0 | 14.818019 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2025-09-03 04:00:00 | 3536.07 | 3545.89 | 3529.36 | 3536.81 | 59697 | 20 | 0 | 18.731740 |
| 2025-09-03 08:00:00 | 3536.82 | 3541.19 | 3526.93 | 3539.99 | 58476 | 20 | 0 | 19.107330 |
| 2025-09-03 12:00:00 | 3540.04 | 3551.43 | 3532.29 | 3550.56 | 63000 | 20 | 0 | 19.007235 |
| 2025-09-03 16:00:00 | 3550.60 | 3572.57 | 3549.38 | 3572.43 | 87375 | 20 | 0 | 18.985718 |
| 2025-09-03 20:00:00 | 3572.45 | 3572.98 | 3570.24 | 3572.12 | 4428 | 20 | 0 | 18.713310 |
182 rows × 8 columns
In [26]:
def get_trend(timeframe="h4", lookback=150):
"""
Bestimme Trendrichtung per Linear Regression.
Liefert:
- trend: "uptrend" / "downtrend" / "sideways"
- slope: numerischer Wert der Regression
- atr: ATR des Zeitraums
- slope_threshold: dynamische Schwelle für Seitwärtsbewegungen
"""
df = get_rates(timeframe, lookback)
if df.empty:
return {"trend": "sideways", "slope": 0, "atr": 0, "slope_threshold": 0}
# Linear Regression
X = np.arange(len(df)).reshape(-1, 1)
y = df["close"].values.reshape(-1, 1)
slope = LinearRegression().fit(X, y).coef_[0][0]
# ATR
df["hl"] = df["high"] - df["low"]
df["hc"] = (df["high"] - df["close"].shift()).abs()
df["lc"] = (df["low"] - df["close"].shift()).abs()
df["tr"] = df[["hl","hc","lc"]].max(axis=1)
atr = df["tr"].rolling(14).mean().iloc[-1]
slope_threshold = (atr / df["close"].iloc[-1]) * 1.2
if abs(slope) < slope_threshold:
trend = "sideways"
else:
trend = "uptrend" if slope > 0 else "downtrend"
return {
"trend": trend,
"slope": slope,
"atr": atr,
"slope_threshold": slope_threshold
}
In [27]:
def get_top_down_signal(symbol=symbol):
"""
Top-Down Ansatz:
- D1 / H4: Trend bestimmen
- H1 / M30: Setup identifizieren
- M15 / M5: Einstiege optimieren (Signale ohne Tradeausführung)
"""
global trend_dict
# --- Höhere Timeframes ---
d1_trend_info = get_trend("d1", lookback=200)
h4_trend_info = get_trend("h4", lookback=150)
# --- Mittlere Timeframes für Setups ---
h1_trend_info = get_trend("h1", lookback=100)
m30_trend_info = get_trend("m30", lookback=60)
# --- Niedrigere Timeframes für Einstiege (nur Signal, kein Trade) ---
m15_trend_info = get_trend("m15", lookback=50)
m5_trend_info = get_trend("m5", lookback=20)
# --- Konsolidierte Trendanalyse ---
top_down_trend = "sideways"
if d1_trend_info["trend"] == h4_trend_info["trend"]:
top_down_trend = d1_trend_info["trend"]
# --- Setup-Bedingungen ---
setup_ready = False
if top_down_trend != "sideways":
if h1_trend_info["trend"] == top_down_trend and m30_trend_info["trend"] == top_down_trend:
setup_ready = True
# --- Einstiegsberechnung ---
entry_signal = 0
if setup_ready:
if m15_trend_info["trend"] == top_down_trend and m5_trend_info["trend"] == top_down_trend:
entry_signal = 1 if top_down_trend == "uptrend" else -1
# --- Speichern ---
trend_dict["top_down"] = {
"D1": d1_trend_info,
"H4": h4_trend_info,
"H1": h1_trend_info,
"M30": m30_trend_info,
"M15": m15_trend_info,
"M5": m5_trend_info,
"top_down_trend": top_down_trend,
"setup_ready": setup_ready,
"entry_signal": entry_signal
}
return trend_dict["top_down"]
In [28]:
get_trend("h1")Out [28]:
{'trend': 'uptrend',
'slope': 1.4477744934856227,
'atr': 9.130714285714314,
'slope_threshold': 0.0030673261656543383}In [56]:
get_top_down_signal(symbol)Out [56]:
{'D1': {'trend': 'uptrend',
'slope': 4.621913228515892,
'atr': 39.36499999999988,
'slope_threshold': 0.013270331238569827},
'H4': {'trend': 'uptrend',
'slope': 0.8813408347377812,
'atr': 20.499285714285698,
'slope_threshold': 0.006910512170269389},
'H1': {'trend': 'uptrend',
'slope': 1.8054590176423853,
'atr': 9.649999999999993,
'slope_threshold': 0.0032531105411456656},
'M30': {'trend': 'uptrend',
'slope': 1.0648326715825298,
'atr': 6.73500000000003,
'slope_threshold': 0.0022704351807892403},
'M15': {'trend': 'uptrend',
'slope': 0.3136986803519024,
'atr': 4.114285714285676,
'slope_threshold': 0.0013869664483344836},
'M5': {'trend': 'downtrend',
'slope': -0.909999999999854,
'atr': nan,
'slope_threshold': nan},
'top_down_trend': 'uptrend',
'setup_ready': True,
'entry_signal': 0}In [30]:
get_trend()Out [30]:
{'trend': 'uptrend',
'slope': 0.8855957903085263,
'atr': 19.30285714285713,
'slope_threshold': 0.006484504599909453}In [31]:
def get_trend_fast(period):
global trend_dict, periods_dict, symbol
df2 = get_rates(period).iloc[-200:]
df2["close_smooth"] = savgol_filter(df2.close, 15, 5) # kleinere Glättung
atr = df2.atr.iloc[-1]
# Weniger strenge Peak-Erkennung
peaks_idx, _ = find_peaks(df2.close_smooth, distance=1, width=2, prominence=atr*0.5) #evtl kleiner 0.5
troughs_idx, _ = find_peaks(-1*df2.close_smooth, distance=1, width=2, prominence=atr*0.5)
# Trend über Peaks/Troughs
if len(peaks_idx) > 0 and len(troughs_idx) > 0:
if peaks_idx[-1] > troughs_idx[-1]:
trend = "downtrend"
else:
trend = "uptrend"
else:
# Falls keine klaren Peaks gefunden wurden → Slope nutzen
slope = df2.close_smooth.diff().iloc[-5:].mean()
trend = "downtrend" if slope < 0 else "uptrend"
trend_dict[period] = trend
return trend
In [32]:
get_trend_fast('m15')Out [32]:
'uptrend'
In [33]:
def set_trend():
global pause_trading, periods_dict, trend_dict
for k,v in trend_dict.items():
get_trend_fast(k)
print(k)
for k,v in reversed(trend_dict.items()):
if v == 'uptrend':
print(k,v)
periods_dict[symbol] = [k]
pause_trading = 0
print(f"Pause Trading: {pause_trading}")
break
elif v == 'downtrend':
periods_dict[symbol] = [k] #['m15']
pause_trading = 1
print(f"Pause Trading: {pause_trading}")In [34]:
set_trend()m15
[1;31m---------------------------------------------------------------------------[0m [1;31mKeyError[0m Traceback (most recent call last) Cell [1;32mIn[34], line 1[0m [1;32m----> 1[0m set_trend() Cell [1;32mIn[33], line 6[0m, in [0;36mset_trend[1;34m()[0m [0;32m 3[0m [38;5;28;01mglobal[39;00m pause_trading, periods_dict, trend_dict [0;32m 5[0m [38;5;28;01mfor[39;00m k,v [38;5;129;01min[39;00m trend_dict[38;5;241m.[39mitems(): [1;32m----> 6[0m get_trend_fast(k) [0;32m 7[0m [38;5;28mprint[39m(k) [0;32m 9[0m [38;5;28;01mfor[39;00m k,v [38;5;129;01min[39;00m [38;5;28mreversed[39m(trend_dict[38;5;241m.[39mitems()): Cell [1;32mIn[31], line 4[0m, in [0;36mget_trend_fast[1;34m(period)[0m [0;32m 1[0m [38;5;28;01mdef[39;00m [38;5;21mget_trend_fast[39m(period): [0;32m 2[0m [38;5;28;01mglobal[39;00m trend_dict, periods_dict, symbol [1;32m----> 4[0m df2 [38;5;241m=[39m get_rates(period)[38;5;241m.[39miloc[[38;5;241m-[39m[38;5;241m200[39m:] [0;32m 5[0m df2[[38;5;124m"[39m[38;5;124mclose_smooth[39m[38;5;124m"[39m] [38;5;241m=[39m savgol_filter(df2[38;5;241m.[39mclose, [38;5;241m15[39m, [38;5;241m5[39m) [38;5;66;03m# kleinere Glättung[39;00m [0;32m 7[0m atr [38;5;241m=[39m df2[38;5;241m.[39matr[38;5;241m.[39miloc[[38;5;241m-[39m[38;5;241m1[39m] Cell [1;32mIn[24], line 5[0m, in [0;36mget_rates[1;34m(periode, bars)[0m [0;32m 1[0m [38;5;28;01mdef[39;00m [38;5;21mget_rates[39m(periode, bars[38;5;241m=[39m[38;5;241m300[39m): [0;32m 2[0m [38;5;66;03m#global symbol[39;00m [0;32m 3[0m [0;32m 4[0m [38;5;66;03m# OHLC abrufen[39;00m [1;32m----> 5[0m ohlc [38;5;241m=[39m mt[38;5;241m.[39mcopy_rates_from_pos(symbol, timeframes_dict[periode], [38;5;241m0[39m, bars) [0;32m 6[0m df [38;5;241m=[39m pd[38;5;241m.[39mDataFrame(ohlc) [0;32m 7[0m df[[38;5;124m'[39m[38;5;124mtime[39m[38;5;124m'[39m] [38;5;241m=[39m pd[38;5;241m.[39mto_datetime(df[[38;5;124m'[39m[38;5;124mtime[39m[38;5;124m'[39m], unit[38;5;241m=[39m[38;5;124m'[39m[38;5;124ms[39m[38;5;124m'[39m) [1;31mKeyError[0m: 'top_down'
In [ ]:
pause_trading, trend_dict, periods_dict, symbols[0](0,
{'m15': 'uptrend',
'top_down': {'D1': {'trend': 'uptrend',
'slope': 4.621913228515892,
'atr': 39.36499999999988,
'slope_threshold': 0.013270331238569827},
'H4': {'trend': 'uptrend',
'slope': 0.8813408347377812,
'atr': 20.499285714285698,
'slope_threshold': 0.006910512170269389},
'H1': {'trend': 'uptrend',
'slope': 1.8054590176423853,
'atr': 9.649999999999993,
'slope_threshold': 0.0032531105411456656},
'M30': {'trend': 'uptrend',
'slope': 1.0648326715825298,
'atr': 6.73500000000003,
'slope_threshold': 0.0022704351807892403},
'M15': {'trend': 'uptrend',
'slope': 0.3136986803519024,
'atr': 4.114285714285676,
'slope_threshold': 0.0013869664483344836},
'M5': {'trend': 'downtrend',
'slope': -0.909999999999854,
'atr': nan,
'slope_threshold': nan},
'top_down_trend': 'uptrend',
'setup_ready': True,
'entry_signal': 0},
'm5': {'standard': 'downtrend', 'fast': 'downtrend', 'signal': -1}},
{'BTCUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'ETHUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XRPUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'XAUUSD': ['m15'],
'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],
'EURNZD': ['m5', 'm2', 'm1']},
'XAUUSD')Kein Top-Down-Setup vorhanden. Kein Trade.
In [36]:
set_trend()m15
[1;31m---------------------------------------------------------------------------[0m [1;31mKeyError[0m Traceback (most recent call last) Cell [1;32mIn[36], line 1[0m [1;32m----> 1[0m set_trend() Cell [1;32mIn[33], line 6[0m, in [0;36mset_trend[1;34m()[0m [0;32m 3[0m [38;5;28;01mglobal[39;00m pause_trading, periods_dict, trend_dict [0;32m 5[0m [38;5;28;01mfor[39;00m k,v [38;5;129;01min[39;00m trend_dict[38;5;241m.[39mitems(): [1;32m----> 6[0m get_trend_fast(k) [0;32m 7[0m [38;5;28mprint[39m(k) [0;32m 9[0m [38;5;28;01mfor[39;00m k,v [38;5;129;01min[39;00m [38;5;28mreversed[39m(trend_dict[38;5;241m.[39mitems()): Cell [1;32mIn[31], line 4[0m, in [0;36mget_trend_fast[1;34m(period)[0m [0;32m 1[0m [38;5;28;01mdef[39;00m [38;5;21mget_trend_fast[39m(period): [0;32m 2[0m [38;5;28;01mglobal[39;00m trend_dict, periods_dict, symbol [1;32m----> 4[0m df2 [38;5;241m=[39m get_rates(period)[38;5;241m.[39miloc[[38;5;241m-[39m[38;5;241m200[39m:] [0;32m 5[0m df2[[38;5;124m"[39m[38;5;124mclose_smooth[39m[38;5;124m"[39m] [38;5;241m=[39m savgol_filter(df2[38;5;241m.[39mclose, [38;5;241m15[39m, [38;5;241m5[39m) [38;5;66;03m# kleinere Glättung[39;00m [0;32m 7[0m atr [38;5;241m=[39m df2[38;5;241m.[39matr[38;5;241m.[39miloc[[38;5;241m-[39m[38;5;241m1[39m] Cell [1;32mIn[24], line 5[0m, in [0;36mget_rates[1;34m(periode, bars)[0m [0;32m 1[0m [38;5;28;01mdef[39;00m [38;5;21mget_rates[39m(periode, bars[38;5;241m=[39m[38;5;241m300[39m): [0;32m 2[0m [38;5;66;03m#global symbol[39;00m [0;32m 3[0m [0;32m 4[0m [38;5;66;03m# OHLC abrufen[39;00m [1;32m----> 5[0m ohlc [38;5;241m=[39m mt[38;5;241m.[39mcopy_rates_from_pos(symbol, timeframes_dict[periode], [38;5;241m0[39m, bars) [0;32m 6[0m df [38;5;241m=[39m pd[38;5;241m.[39mDataFrame(ohlc) [0;32m 7[0m df[[38;5;124m'[39m[38;5;124mtime[39m[38;5;124m'[39m] [38;5;241m=[39m pd[38;5;241m.[39mto_datetime(df[[38;5;124m'[39m[38;5;124mtime[39m[38;5;124m'[39m], unit[38;5;241m=[39m[38;5;124m'[39m[38;5;124ms[39m[38;5;124m'[39m) [1;31mKeyError[0m: 'top_down'
In [37]:
trend_dict[periods_dict[symbol][0]]Out [37]:
'uptrend'
In [38]:
periods_dict[symbol], pause_tradingOut [38]:
(['m15'], 0)
In [39]:
def get_mt5_data(symbol=symbol, timeframe=mt.TIMEFRAME_M15, n_bars=500):
rates = mt.copy_rates_from_pos(symbol, timeframe, 0, n_bars)
df = pd.DataFrame(rates)
df["time"] = pd.to_datetime(df["time"], unit="s")
return dfIn [40]:
def get_m5_trade_signal(symbol, atr_mult=1.5):
global trend_dict, periods_dict
# Hole die letzten M5-Daten
df = get_rates('m5').iloc[-200:]
# Smoothen
df["close_smooth_std"] = savgol_filter(df.close, 25, 5)
df["close_smooth_fast"] = savgol_filter(df.close, 15, 5)
atr = df.atr.iloc[-1]
# --- Standard-Trend ---
peaks_std, _ = find_peaks(df.close_smooth_std, distance=1, width=2, prominence=atr)
troughs_std, _ = find_peaks(-df.close_smooth_std, distance=1, width=2, prominence=atr)
if len(peaks_std) > 0 and len(troughs_std) > 0:
if peaks_std[-1] > troughs_std[-1]:
trend_standard = "downtrend"
else:
trend_standard = "uptrend"
else:
trend_standard = "neutral"
# --- Fast-Trend ---
peaks_fast, _ = find_peaks(df.close_smooth_fast, distance=1, width=2, prominence=atr*0.5)
troughs_fast, _ = find_peaks(-df.close_smooth_fast, distance=1, width=2, prominence=atr*0.5)
if len(peaks_fast) > 0 and len(troughs_fast) > 0:
if peaks_fast[-1] > troughs_fast[-1]:
trend_fast = "downtrend"
else:
trend_fast = "uptrend"
else:
slope = df.close_smooth_fast.diff().iloc[-5:].mean()
trend_fast = "downtrend" if slope < 0 else "uptrend"
# --- Kombiniertes Signal ---
signal = 0 # 0 = neutral, 1 = long, -1 = short
stop_loss = None
if trend_standard == "uptrend" and trend_fast == "uptrend":
signal = 1
stop_loss = df.close.iloc[-1] - atr_mult * atr
elif trend_standard == "downtrend" and trend_fast == "downtrend":
signal = -1
stop_loss = df.close.iloc[-1] + atr_mult * atr
# Speichern
trend_dict['m5'] = {"standard": trend_standard, "fast": trend_fast, "signal": signal}
return {
"signal": signal,
"price": df.close.iloc[-1],
"stop_loss": stop_loss,
"trends": trend_dict['m15']
}
In [41]:
def generate_signal(df = get_mt5_data(symbol=symbol, timeframe=timeframes_dict['m15']), confirm_window=3):
"""
Berechnet drei Signalarten:
- fast_signal: schnelle, aggressive Variante
- standard_signal: konservative Basisstrategie
- optimized_signal: zusätzliche Filter (ATR, ADX, strengerer RSI)
"""
# Indikatoren
df["ema21"] = df["close"].ewm(span=3).mean()
df["ema50"] = df["close"].ewm(span=9).mean()
df["rsi9"] = ta.rsi(df["close"], length=9)
df["rsi14"] = ta.rsi(df["close"], length=14)
df["trend"] = savgol_filter(df["close"], 25, 3)
# Zusätzliche Filterindikatoren
df["atr"] = ta.atr(df["high"], df["low"], df["close"], length=14)
df["adx"] = ta.adx(df["high"], df["low"], df["close"], length=14)["ADX_14"]
# Spalten für Signale
df["fast_signal"] = 0
df["standard_signal"] = 0
df["optimized_signal"] = 0
for i in range(1, len(df)):
# -----------------------
# FAST SIGNAL (früh/aggressiv)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["rsi9"].iloc[i] > 30
):
df.at[i, "fast_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["rsi9"].iloc[i] < 60
):
df.at[i, "fast_signal"] = -1
# -----------------------
# STANDARD SIGNAL (konservativ, ursprüngliche Logik)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] <= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] < 70
and df["rsi9"].iloc[i] > 40
and df["trend"].iloc[i] > df["trend"].iloc[i - 1]
):
df.at[i, "standard_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] >= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] > 40
and df["rsi9"].iloc[i] < 70
and df["trend"].iloc[i] < df["trend"].iloc[i - 1]
):
df.at[i, "standard_signal"] = -1
# -----------------------
# OPTIMIZED SIGNAL (mit ADX + ATR + strengerem RSI)
# -----------------------
if (
df["ema21"].iloc[i] > df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] <= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] < 65 # strengerer Filter
and df["rsi9"].iloc[i] > 45 # Momentum klarer
and df["adx"].iloc[i] > 20 # Trendstärke vorhanden
and df["atr"].iloc[i] > df["atr"].rolling(50).mean().iloc[i] # Volatilität über Durchschnitt
):
df.at[i, "optimized_signal"] = 1
elif (
df["ema21"].iloc[i] < df["ema50"].iloc[i]
and df["ema21"].iloc[i - 1] >= df["ema50"].iloc[i - 1]
and df["rsi14"].iloc[i] > 35 # strengerer Filter unten
and df["rsi9"].iloc[i] < 55
and df["adx"].iloc[i] > 20
and df["atr"].iloc[i] > df["atr"].rolling(50).mean().iloc[i]
):
df.at[i, "optimized_signal"] = -1
return df
In [42]:
generate_signal()Out [42]:
| time | open | high | low | close | tick_volume | spread | real_volume | ema21 | ema50 | rsi9 | rsi14 | trend | atr | adx | fast_signal | standard_signal | optimized_signal | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2025-08-27 07:45:00 | 3374.93 | 3375.85 | 3373.94 | 3374.93 | 2467 | 20 | 0 | 3374.930000 | 3374.930000 | NaN | NaN | 3376.087631 | NaN | NaN | 0 | 0 | 0 |
| 1 | 2025-08-27 08:00:00 | 3374.91 | 3376.21 | 3374.11 | 3374.74 | 2904 | 20 | 0 | 3374.803333 | 3374.824444 | NaN | NaN | 3375.914101 | NaN | NaN | 0 | 0 | 0 |
| 2 | 2025-08-27 08:15:00 | 3374.73 | 3377.46 | 3374.21 | 3375.77 | 2313 | 20 | 0 | 3375.355714 | 3375.211967 | NaN | NaN | 3375.933879 | NaN | NaN | 0 | 0 | 0 |
| 3 | 2025-08-27 08:30:00 | 3375.78 | 3378.81 | 3375.69 | 3377.69 | 2745 | 20 | 0 | 3376.600667 | 3376.051409 | NaN | NaN | 3376.121647 | NaN | NaN | 0 | 0 | 0 |
| 4 | 2025-08-27 08:45:00 | 3377.68 | 3378.88 | 3375.85 | 3377.23 | 2374 | 20 | 0 | 3376.925484 | 3376.402013 | NaN | NaN | 3376.452085 | NaN | NaN | 0 | 0 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 495 | 2025-09-03 19:15:00 | 3565.40 | 3568.01 | 3564.18 | 3567.83 | 3848 | 20 | 0 | 3566.195399 | 3562.948376 | 71.921940 | 69.332849 | 3568.219300 | 5.677877 | 31.169524 | 1 | 0 | 0 |
| 496 | 2025-09-03 19:30:00 | 3567.83 | 3569.42 | 3566.97 | 3569.01 | 3442 | 20 | 0 | 3567.602699 | 3564.160701 | 73.431485 | 70.341690 | 3569.360376 | 5.447314 | 32.165951 | 1 | 0 | 0 |
| 497 | 2025-09-03 19:45:00 | 3569.01 | 3572.57 | 3568.59 | 3572.43 | 3585 | 20 | 0 | 3570.016350 | 3565.814561 | 77.394221 | 73.103386 | 3570.468641 | 5.342506 | 33.517576 | 1 | 0 | 0 |
| 498 | 2025-09-03 20:00:00 | 3572.45 | 3572.98 | 3570.24 | 3572.22 | 3806 | 20 | 0 | 3571.118175 | 3567.095649 | 76.604942 | 72.656004 | 3571.539564 | 5.156613 | 34.825121 | 1 | 0 | 0 |
| 499 | 2025-09-03 20:15:00 | 3572.26 | 3572.68 | 3571.43 | 3572.06 | 1092 | 20 | 0 | 3571.589087 | 3568.088519 | 75.941120 | 72.292990 | 3572.568615 | 4.877569 | 36.039270 | 1 | 0 | 0 |
500 rows × 18 columns
In [43]:
pos = mt.positions_total()
if pos > 0:
open_positions = mt.positions_get()
trail_factor = 0.5
entry_price = open_positions[0].price_open
tp_current = open_positions[0].tp
current_price = open_positions[0].price_current
profit = open_positions[0].profit
profit = current_price - entry_price
if profit > 0:
new_tp = current_price - entry_price * trail_factor
if profit < 0:
new_tp = current_price - profit * trail_factor
if new_tp > current_price:
#update tp from open pos
new_tp
print(f"Trailing +TP for BUY {symbol}: {new_tp} CP: {current_price}")
if new_tp < current_price:
#update tp from open pos
new_tp
print(f"Trailing -TP for BUY {symbol}: {new_tp} CP: {current_price}")
#open_positions
Trailing +TP for BUY XAUUSD: 3572.2200000000003 CP: 3572.06
In [44]:
def update_trailing_sl_tp(pos, atr, rrr=2.0, atr_mult=1.5, max_retries=2):
"""
Aktualisiert SL und TP für offene Positionen:
- ATR-basiertes Trailing
- Gewinn-stufenweises Nachziehen
- Dynamische Anpassung mit Validierung
- Fallback-Mechanismus bei RETCODE 1016
"""
symbol = pos.symbol
info = mt.symbol_info(symbol)
digits = info.digits
point = info.point
stops_level = info.trade_stops_level * point # Mindestabstand vom Broker
entry_price = pos.price_open
current_tick = mt.symbol_info_tick(symbol)
bid, ask = current_tick.bid, current_tick.ask
current_price = bid if pos.type == 0 else ask
pos_type = pos.type # 0 = BUY, 1 = SELL
# Gewinn in ATR berechnen
if pos_type == 0: # LONG
profit_atr = (current_price - entry_price) / atr
base_sl = current_price - atr_mult * atr
new_sl = max(pos.sl or 0, base_sl)
if profit_atr > 2:
new_sl = max(new_sl, entry_price + 1.5 * atr)
elif profit_atr > 1:
new_sl = max(new_sl, entry_price + 1.0 * atr)
elif profit_atr > 0.5:
new_sl = max(new_sl, entry_price + 0.5 * atr)
new_tp = entry_price + (entry_price - new_sl) * rrr
# Validierung BUY
if new_sl >= bid - stops_level:
new_sl = bid - stops_level
if new_tp <= ask + stops_level:
new_tp = ask + stops_level
else: # SHORT
profit_atr = (entry_price - current_price) / atr
base_sl = current_price + atr_mult * atr
new_sl = min(pos.sl or 999999, base_sl)
if profit_atr > 2:
new_sl = min(new_sl, entry_price - 1.5 * atr)
elif profit_atr > 1:
new_sl = min(new_sl, entry_price - 1.0 * atr)
elif profit_atr > 0.5:
new_sl = min(new_sl, entry_price - 0.5 * atr)
new_tp = entry_price - (new_sl - entry_price) * rrr
# Validierung SELL
if new_sl <= ask + stops_level:
new_sl = ask + stops_level
if new_tp >= bid - stops_level:
new_tp = bid - stops_level
# Runden auf gültige Stellen
new_sl = round(new_sl, digits)
new_tp = round(new_tp, digits)
# --- Nur updaten, wenn sich Werte geändert haben ---
if (pos.sl is None or abs(new_sl - pos.sl) > point) or \
(pos.tp is None or abs(new_tp - pos.tp) > point):
for attempt in range(max_retries):
request = {
"action": mt.TRADE_ACTION_SLTP,
"symbol": symbol,
"sl": new_sl,
"tp": new_tp,
"position": pos.ticket
}
result = mt.order_send(request)
if result.retcode == mt.TRADE_RETCODE_DONE:
print(f"🔄 Updated {symbol} | SL: {new_sl:.5f} | TP: {new_tp:.5f}")
break
elif result.retcode == mt.TRADE_RETCODE_INVALID_STOPS:
# Fallback: Stops korrigieren
print(f"⚠️ RETCODE 1016 (Invalid stops) – Versuch {attempt+1}/{max_retries}")
adjust = 2 * stops_level # mehr Abstand
if pos_type == 0: # BUY
new_sl = bid - adjust
new_tp = ask + adjust
else: # SELL
new_sl = ask + adjust
new_tp = bid - adjust
new_sl = round(new_sl, digits)
new_tp = round(new_tp, digits)
continue # retry
else:
print(f"❌ SL/TP Update Fehler: {result.retcode} ({result.comment})")
break
return {"new_sl": new_sl, "new_tp": new_tp, "profit_atr": profit_atr}
In [45]:
def manual_update_trailing_sl_tp(atr_mult=1.5, slope_factor=1.5):
# --- Hole die letzten M5-Daten ---
df = get_rates('m5').iloc[-200:]
# --- ATR Berechnung ---
df["hl"] = df["high"] - df["low"]
df["hc"] = (df["high"] - df["close"].shift()).abs()
df["lc"] = (df["low"] - df["close"].shift()).abs()
df["tr"] = df[["hl","hc","lc"]].max(axis=1)
df["atr"] = df["tr"].rolling(14).mean()
atr = df["atr"].iloc[-1]
current_price = df["close"].iloc[-1]
# --- Trendrichtung per Linear Regression ---
def linreg_slope(series):
X = np.arange(len(series)).reshape(-1, 1)
y = series.values.reshape(-1, 1)
model = LinearRegression().fit(X, y)
return model.coef_[0][0]
slope_long = linreg_slope(df["close"].iloc[-50:]) # 50 Balken (~4h)
slope_short = linreg_slope(df["close"].iloc[-15:]) # 15 Balken (~1h)
# --- Dynamischer Seitwärtsfilter ---
slope_threshold = slope_factor * atr / df["close"].iloc[-1]
# --- Dynamische RRR-Berechnung ---
atr_norm = atr / current_price # relative Volatilität
slope_strength = abs(slope_short) # Trendstärke aus Regression
rrr_base = 1.2
rrr_from_vol = atr_norm * 1500 # skaliert ATR-Einfluss
rrr_from_slope = slope_strength / slope_threshold # Slope-Einfluss
rrr = rrr_base + rrr_from_vol + rrr_from_slope
rrr = max(1.2, min(rrr, 3.0)) # Begrenzung
open_positions = mt.positions_get(symbol=symbol)
for pos in open_positions:
atr_value = df["atr"].iloc[-1]
update_trailing_sl_tp(pos, atr=atr_value, rrr=rrr, atr_mult=atr_mult)
In [46]:
manual_update_trailing_sl_tp()🔄 Updated XAUUSD | SL: 3569.71000 | TP: 3580.40000
In [54]:
def get_m5_trade_signals(symbol=symbol, atr_mult=1.5, base_rrr=2.0, atr_min=0.0005, slope_factor=1.5, execute=False):
"""
Analyse von M5-Signalen mit ATR/Trend/Seitwärtsfilter
- execute=False -> nur Analyse
- execute=True -> führt Orders aus
"""
global trend_dict, volume_dict
df = get_rates('m5').iloc[-200:]
# --- ATR ---
df["hl"] = df["high"] - df["low"]
df["hc"] = (df["high"] - df["close"].shift()).abs()
df["lc"] = (df["low"] - df["close"].shift()).abs()
df["tr"] = df[["hl","hc","lc"]].max(axis=1)
df["atr"] = df["tr"].rolling(14).mean()
atr = df["atr"].iloc[-1]
if atr < atr_min:
return {"signal": 0, "reason": "ATR too low → sideways"}
# --- Trend ---
def linreg_slope(series):
X = np.arange(len(series)).reshape(-1, 1)
y = series.values.reshape(-1, 1)
model = LinearRegression().fit(X, y)
return model.coef_[0][0]
slope_long = linreg_slope(df["close"].iloc[-50:])
slope_short = linreg_slope(df["close"].iloc[-15:])
slope_threshold = slope_factor * atr / df["close"].iloc[-1]
if abs(slope_long) < slope_threshold and abs(slope_short) < slope_threshold:
return {"signal": 0, "reason": "Trend flat → sideways"}
trend_standard = "uptrend" if slope_long > 0 else "downtrend"
trend_fast = "uptrend" if slope_short > 0 else "downtrend"
current_price = df["close"].iloc[-1]
# --- RRR ---
atr_norm = atr / current_price
slope_strength = abs(slope_short)
rrr_base = 1.2
rrr_from_vol = atr_norm * 1500
rrr_from_slope = slope_strength / slope_threshold
rrr = max(1.2, min(rrr_base + rrr_from_vol + rrr_from_slope, 3.0))
# --- Signal ---
signal, stop_loss, takeprofit = 0, None, None
if trend_standard == "uptrend" and trend_fast == "uptrend":
signal = 1
stop_loss = current_price - atr_mult * atr
takeprofit = current_price + atr_mult * atr * rrr
if execute:
print(f"BUY {symbol} @ {current_price} | TP: {takeprofit} | SL: {stop_loss}")
market_order(symbol, volume_dict[symbol], "buy", stoploss=stop_loss, take_profit=takeprofit)
elif trend_standard == "downtrend" and trend_fast == "downtrend":
signal = -1
stop_loss = current_price + atr_mult * atr
takeprofit = current_price - atr_mult * atr * rrr
if execute:
print(f"SELL {symbol} @ {current_price} | TP: {takeprofit} | SL: {stop_loss}")
market_order(symbol, volume_dict[symbol], "sell", stoploss=stop_loss, take_profit=takeprofit)
# --- speichern ---
trend_dict['m5'] = {"standard": trend_standard, "fast": trend_fast, "signal": signal}
return {
"signal": signal,
"price": current_price,
"stop_loss": stop_loss,
"take_profit": takeprofit,
"Risk Reward": rrr,
"trends": trend_dict['m5'],
"atr": atr,
"slope_long": slope_long,
"slope_short": slope_short
}In [47]:
def execute_m5_trade(symbol=symbol, atr_mult=1.5, base_rrr=2.0):
"""
Führt einen Trade auf M5 nur aus, wenn das Top-Down-Setup ein positives Signal liefert.
Nutzt adaptive ATR, dynamisches RRR und SL/TP.
"""
global trend_dict, volume_dict, pause_trading
# --- Top-Down-Signal prüfen ---
top_down = get_top_down_signal(symbol)
if pause_trading == 1:
print("⚠️ Trading pausiert. Keine Trades ausgeführt.")
return None
if not top_down["setup_ready"] or top_down["entry_signal"] == 0:
print("Kein Top-Down-Setup vorhanden. Kein Trade.")
return None
# --- M5-Signal berechnen (nur Signal, keine automatische Order) ---
m5_signal_info = get_m5_trade_signals(symbol=symbol, atr_mult=atr_mult, base_rrr=base_rrr)
if m5_signal_info["signal"] == 0:
print("M5 Signal neutral. Kein Trade.")
return None
# --- Trade ausführen ---
current_price = m5_signal_info["price"]
stop_loss = m5_signal_info["stop_loss"]
take_profit = m5_signal_info["take_profit"]
if m5_signal_info["signal"] == 1:
# Long
print(f"✅ BUY {symbol} @ {current_price} | TP: {take_profit} | SL: {stop_loss}")
market_order(symbol, volume_dict[symbol], "buy", stoploss=stop_loss, take_profit=take_profit)
elif m5_signal_info["signal"] == -1:
# Short
print(f"✅ SELL {symbol} @ {current_price} | TP: {take_profit} | SL: {stop_loss}")
market_order(symbol, volume_dict[symbol], "sell", stoploss=stop_loss, take_profit=take_profit)
# --- Offene Trades aktualisieren (Trailing SL/TP) ---
open_positions = mt.positions_get(symbol=symbol)
for pos in open_positions:
update_trailing_sl_tp(pos, atr=m5_signal_info["atr"], rrr=m5_signal_info["Risk Reward"], atr_mult=atr_mult)
return {
"top_down": top_down,
"m5_signal": m5_signal_info
}
In [55]:
execute_m5_trade()Kein Top-Down-Setup vorhanden. Kein Trade.
✅ BUY XAUUSD @ 3571.06 | TP: 3578.2085714285713 | SL: 3568.677142857143
✅ BUY XAUUSD @ 3571.81 | TP: 3579.604642857143 | SL: 3569.2117857142857
🔄 Updated XAUUSD | SL: 3570.03000 | TP: 3579.44000
🔄 Updated XAUUSD | SL: 3570.58000 | TP: 3577.77000
🔄 Updated XAUUSD | SL: 3573.16000 | TP: 3573.77000
✅ BUY XAUUSD @ 3573.65 | TP: 3581.7596428571433 | SL: 3570.946785714286
🔄 Updated XAUUSD | SL: 3570.95000 | TP: 3582.31000
🔄 Updated XAUUSD | SL: 3571.07000 | TP: 3581.95000
✅ BUY XAUUSD @ 3573.67 | TP: 3581.6832142857143 | SL: 3570.9989285714287
🔄 Updated XAUUSD | SL: 3571.77000 | TP: 3579.86000
🔄 Updated XAUUSD | SL: 3571.77000 | TP: 3579.85000
✅ BUY XAUUSD @ 3574.21 | TP: 3582.0914285714284 | SL: 3571.5828571428574
🔄 Updated XAUUSD | SL: 3574.66000 | TP: 3575.40000
🔄 Updated XAUUSD | SL: 3574.67000 | TP: 3575.60000
🔄 Updated XAUUSD | SL: 3574.67000 | TP: 3575.39000
🔄 Updated XAUUSD | SL: 3574.67000 | TP: 3575.63000
🔄 Updated XAUUSD | SL: 3574.67000 | TP: 3575.68000
✅ BUY XAUUSD @ 3575.28 | TP: 3582.5957142857146 | SL: 3572.841428571429
✅ BUY XAUUSD @ 3575.47 | TP: 3583.2614285714285 | SL: 3572.872857142857
🔄 Updated XAUUSD | SL: 3572.88000 | TP: 3584.11000
Kein Top-Down-Setup vorhanden. Kein Trade.
✅ BUY XAUUSD @ 3575.29 | TP: 3583.4285714285716 | SL: 3572.577142857143
🔄 Updated XAUUSD | SL: 3573.59000 | TP: 3581.99000
🔄 Updated XAUUSD | SL: 3573.90000 | TP: 3581.07000
🔄 Updated XAUUSD | SL: 3573.90000 | TP: 3581.06000
🔄 Updated XAUUSD | SL: 3576.56000 | TP: 3577.79000
✅ BUY XAUUSD @ 3577.4 | TP: 3584.628928571429 | SL: 3574.990357142857
⚠️ RETCODE 1016 (Invalid stops) – Versuch 1/2
🔄 Updated XAUUSD | SL: 3577.00000 | TP: 3578.00000
✅ BUY XAUUSD @ 3578.23 | TP: 3586.0310714285715 | SL: 3575.6296428571427
🔄 Updated XAUUSD | SL: 3575.63000 | TP: 3586.63000
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
M5 Signal neutral. Kein Trade.
M5 Signal neutral. Kein Trade.
M5 Signal neutral. Kein Trade.
M5 Signal neutral. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
M5 Signal neutral. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
M5 Signal neutral. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
M5 Signal neutral. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
M5 Signal neutral. Kein Trade.
✅ SELL XAUUSD @ 3563.72 | TP: 3551.165 | SL: 3567.9049999999997
Kein Top-Down-Setup vorhanden. Kein Trade.
✅ SELL XAUUSD @ 3563.47 | TP: 3551.0532142857146 | SL: 3567.6089285714284
✅ SELL XAUUSD @ 3563.92 | TP: 3552.6603571428577 | SL: 3567.673214285714
✅ SELL XAUUSD @ 3565.45 | TP: 3553.589285714286 | SL: 3569.403571428571
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
✅ SELL XAUUSD @ 3562.22 | TP: 3553.2489285714287 | SL: 3565.210357142857
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
Kein Top-Down-Setup vorhanden. Kein Trade.
In [51]:
from apscheduler.schedulers.background import BackgroundScheduler
import time
scheduler = BackgroundScheduler()
#scheduler.add_job(main, 'date', run_date='2025-03-07 14:29:50')
#scheduler.add_job(decide_order, 'interval', minutes=1) #intervall
#scheduler.add_job(set_symbol, 'interval', minutes=30)
#scheduler.add_job(set_trend, 'interval', minutes=1)
#scheduler.add_job(decide_order, 'interval', minutes=1)
#scheduler.add_job(decide_order, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*') #cron
#scheduler.add_job(get_buy_sell_signal, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
#scheduler.add_job(get_m5_trade_signals, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
scheduler.add_job(execute_m5_trade, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*/5') #cron
scheduler.add_job(manual_update_trailing_sl_tp, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour='0-23', minute='*') #cron
#scheduler.add_job(export_marketview, 'cron', year="*", month='*', day_of_week='mon, tue, wed; thu, fri', hour='8-22', minute=00)
#scheduler.add_job(pause_trading, 'cron', year="*", month="*", day_of_week="mon, tue, wed, thu, fri", hour=22, minute=00) #cron
scheduler.start()
In [52]:
scheduler.get_jobs()Out [52]:
[<Job (id=6d0f4ea790404395bdf8de6e4b08d249 name=manual_update_trailing_sl_tp)>, <Job (id=aaf1ad647f4d4187acd88c3bfd1a24be name=execute_m5_trade)>]
In [ ]:
scheduler.remove_all_jobs()In [ ]:
scheduler.shutdown()