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