Files
Place-Order-Trading-Bot/TradingBot_V1.ipynb
T

2061 lines
65 KiB
Plaintext
Raw Normal View History

2025-09-03 23:17:02 +02:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Import Libaries"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#!pip install ta_lib-0.6.5-cp311-cp311-win_amd64.whl"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#%pip install talib"
]
},
{
"cell_type": "code",
"execution_count": 8,
"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": 9,
"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": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 10,
"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": 11,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'trading-demo'"
]
},
"execution_count": 11,
"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": 12,
"metadata": {},
"outputs": [],
"source": [
"pause_trading = 0\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"symbols = ['XAUUSD']\n",
"#symbols = ['BTCUSD']\n",
"\n",
"#'BTCUSD', 'ETHUSD', \n",
" #'XRPUSD', , 'EURNZD', 'EURUSD'"
]
},
{
"cell_type": "code",
"execution_count": 14,
"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": 15,
"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': [ 'm5'],\n",
"\n",
" \n",
" 'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],\n",
"\n",
"\n",
" 'EURNZD': ['m5', 'm2', 'm1'],\n",
"\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 16,
"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": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'XAUUSD': 0.93}\n",
"XAUUSD 0.93 0.1\n"
]
},
{
"data": {
"text/plain": [
"('XAUUSD',\n",
" 0.93,\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': ['m5'],\n",
" 'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],\n",
" 'EURNZD': ['m5', 'm2', 'm1']})"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_symbol()"
]
},
{
"cell_type": "code",
"execution_count": 18,
"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": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'XAUUSD': 0.93}\n",
"XAUUSD 0.93 0.1\n"
]
},
{
"data": {
"text/plain": [
"('XAUUSD',\n",
" 0.93,\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': ['m5'],\n",
" 'EURUSD': ['h1', 'm30', 'm15', 'm5', 'm1'],\n",
" 'EURNZD': ['m5', 'm2', 'm1']})"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_symbol()"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'XAUUSD': 0.93}\n",
"XAUUSD 0.93 0.1\n"
]
}
],
"source": [
"set_symbol()\n"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('XAUUSD', 0.1, 0)"
]
},
"execution_count": 21,
"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": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 22,
"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": 23,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"()\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": 25,
"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": 26,
"metadata": {},
"outputs": [],
"source": [
"debug = False"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"trend_dict = {\n",
" 'm5': '',\n",
" #'m10': '',\n",
" #'m15': '',\n",
" #'m30': '',\n",
" #'h4': '',\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 28,
"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",
"}\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": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'m5': ''}\n"
]
}
],
"source": [
"print(trend_dict)"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [],
"source": [
"def get_rates(periode):\n",
"\n",
" global symbol\n",
"\n",
" ohlc = mt.copy_rates_from_pos(symbol, timeframes_dict[periode], 0, 300)\n",
" df = pd.DataFrame(ohlc)\n",
" df['time']=pd.to_datetime(df['time'], unit='s')\n",
"\n",
" #df = df[[\"time\",\"open\",\"high\",\"low\",\"close\"]]\n",
"\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",
" ## Take the rolling atr so the yaxis doesn't shake too much \n",
" df[\"atr\"] = ta.atr(high=df.high, low=df.low, close=df.close)\n",
" df[\"atr\"] = df.atr.rolling(window=30).mean()\n",
"\n",
"\n",
" df.set_index(\"time\", inplace = True)\n",
"\n",
" return df"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>tick_volume</th>\n",
" <th>spread</th>\n",
" <th>real_volume</th>\n",
" <th>atr</th>\n",
" </tr>\n",
" <tr>\n",
" <th>time</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2025-08-28 22:00:00</th>\n",
" <td>3420.04</td>\n",
" <td>3420.35</td>\n",
" <td>3419.77</td>\n",
" <td>3419.85</td>\n",
" <td>1045</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-28 22:05:00</th>\n",
" <td>3419.82</td>\n",
" <td>3420.01</td>\n",
" <td>3418.74</td>\n",
" <td>3418.90</td>\n",
" <td>1057</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-28 22:10:00</th>\n",
" <td>3418.93</td>\n",
" <td>3419.20</td>\n",
" <td>3418.27</td>\n",
" <td>3418.62</td>\n",
" <td>1003</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-28 22:15:00</th>\n",
" <td>3418.62</td>\n",
" <td>3419.81</td>\n",
" <td>3418.55</td>\n",
" <td>3419.69</td>\n",
" <td>1218</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-28 22:20:00</th>\n",
" <td>3419.67</td>\n",
" <td>3420.01</td>\n",
" <td>3419.15</td>\n",
" <td>3419.17</td>\n",
" <td>956</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>NaN</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-29 23:35:00</th>\n",
" <td>3447.78</td>\n",
" <td>3448.55</td>\n",
" <td>3447.18</td>\n",
" <td>3448.21</td>\n",
" <td>546</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>1.968904</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-29 23:40:00</th>\n",
" <td>3448.21</td>\n",
" <td>3449.35</td>\n",
" <td>3447.78</td>\n",
" <td>3449.17</td>\n",
" <td>421</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>1.961315</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-29 23:45:00</th>\n",
" <td>3449.15</td>\n",
" <td>3449.18</td>\n",
" <td>3447.64</td>\n",
" <td>3447.90</td>\n",
" <td>578</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>1.955341</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-29 23:50:00</th>\n",
" <td>3447.86</td>\n",
" <td>3448.36</td>\n",
" <td>3447.68</td>\n",
" <td>3448.22</td>\n",
" <td>467</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>1.947173</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2025-08-29 23:55:00</th>\n",
" <td>3448.20</td>\n",
" <td>3448.91</td>\n",
" <td>3447.97</td>\n",
" <td>3448.87</td>\n",
" <td>228</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>1.938756</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>300 rows × 8 columns</p>\n",
"</div>"
],
"text/plain": [
" open high low close tick_volume spread \\\n",
"time \n",
"2025-08-28 22:00:00 3420.04 3420.35 3419.77 3419.85 1045 20 \n",
"2025-08-28 22:05:00 3419.82 3420.01 3418.74 3418.90 1057 20 \n",
"2025-08-28 22:10:00 3418.93 3419.20 3418.27 3418.62 1003 20 \n",
"2025-08-28 22:15:00 3418.62 3419.81 3418.55 3419.69 1218 20 \n",
"2025-08-28 22:20:00 3419.67 3420.01 3419.15 3419.17 956 20 \n",
"... ... ... ... ... ... ... \n",
"2025-08-29 23:35:00 3447.78 3448.55 3447.18 3448.21 546 20 \n",
"2025-08-29 23:40:00 3448.21 3449.35 3447.78 3449.17 421 20 \n",
"2025-08-29 23:45:00 3449.15 3449.18 3447.64 3447.90 578 20 \n",
"2025-08-29 23:50:00 3447.86 3448.36 3447.68 3448.22 467 20 \n",
"2025-08-29 23:55:00 3448.20 3448.91 3447.97 3448.87 228 20 \n",
"\n",
" real_volume atr \n",
"time \n",
"2025-08-28 22:00:00 0 NaN \n",
"2025-08-28 22:05:00 0 NaN \n",
"2025-08-28 22:10:00 0 NaN \n",
"2025-08-28 22:15:00 0 NaN \n",
"2025-08-28 22:20:00 0 NaN \n",
"... ... ... \n",
"2025-08-29 23:35:00 0 1.968904 \n",
"2025-08-29 23:40:00 0 1.961315 \n",
"2025-08-29 23:45:00 0 1.955341 \n",
"2025-08-29 23:50:00 0 1.947173 \n",
"2025-08-29 23:55:00 0 1.938756 \n",
"\n",
"[300 rows x 8 columns]"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_rates('m5')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Parameter | Wirkung |\n",
"| ------------ | ------------------------------------------- |\n",
"| `distance` | Wie **nah beieinander** Peaks liegen dürfen |\n",
"| `width` | Wie **breit/flach** ein Peak sein muss |\n",
"| `prominence` | Wie **stark auffällig** ein Peak sein muss |\n"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [],
"source": [
"def get_trend(period):\n",
"\n",
" global trend_dict, periods_dict, symbol, debug\n",
"\n",
" #current_periods = periods_dict[symbol][0]\n",
"\n",
" df2 = get_rates(period).iloc[-200:]\n",
"\n",
" df2[\"close_smooth\"] = savgol_filter(df2.close, 25, 5)\n",
"\n",
" fig, ax = plt.subplots()\n",
" plt.xticks(rotation=-30)\n",
" price, = ax.plot(df2.index, df2.close, c='grey', lw=2, alpha=0.5, zorder=5)\n",
" price_smooth, = ax.plot(df2.index, df2.close_smooth, c='b', lw=2, zorder=5)\n",
"\n",
" atr = df2.atr.iloc[-1] # all the first atrs are NaN\n",
"\n",
" peaks_idx, _ = find_peaks(df2.close_smooth, distance = 1, \n",
" width = 2, prominence=atr)\n",
"\n",
" troughs_idx, _ = find_peaks(-1*df2.close_smooth, distance = 1, \n",
" width = 2, prominence=atr)\n",
"\n",
" \n",
"\n",
" peaks, = ax.plot(df2.index[peaks_idx], df2.close_smooth.iloc[peaks_idx], \\\n",
" c=\"r\", linestyle='None', markersize = 10.0, marker = \"o\", zorder=10)\n",
"\n",
" troughs, = ax.plot(df2.index[troughs_idx], df2.close_smooth.iloc[troughs_idx], \\\n",
" c=\"g\", linestyle='None', markersize = 10.0, marker = \"o\", zorder=10)\n",
"\n",
"\n",
" plt.show()\n",
"\n",
" #print(peaks_idx[-1], troughs_idx[-1])\n",
"\n",
" if peaks_idx[-1] > troughs_idx[-1]:\n",
" print(\"downtrend\")\n",
"\n",
" trend_dict[period] = 'downtrend'\n",
" else:\n",
" print(\"uptrend\")\n",
"\n",
" trend_dict[period] = 'uptrend'\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [],
"source": [
"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"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'downtrend'"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_trend_fast('m5')"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [],
"source": [
"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}\")"
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"m5\n",
"Pause Trading: 1\n"
]
}
],
"source": [
"set_trend()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Set Trend manually"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pause_trading, trend_dict, periods_dict, symbols[0]"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"m5\n",
"m5 uptrend\n",
"Pause Trading: 0\n"
]
}
],
"source": [
"set_trend()"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'uptrend'"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"trend_dict[periods_dict[symbol][0]]"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(['m5'], 0)"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"periods_dict[symbol], pause_trading"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Job-Scheduler\n",
"\n",
"[Doku Appscheduler Cronjob](https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#id0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"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",
"\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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Get Jobs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"scheduler.get_jobs()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Remove all Jobs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"scheduler.remove_all_jobs()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Shutdown AppScheduler"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"scheduler.shutdown()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate signals"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [],
"source": [
"def get_mt5_data(symbol=symbol, timeframe=mt.TIMEFRAME_M5, 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"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [],
"source": [
"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['m5']\n",
" }\n"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [],
"source": [
"def generate_signal(df = get_mt5_data(symbol=symbol, timeframe=timeframes_dict['m5']), 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"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>time</th>\n",
" <th>open</th>\n",
" <th>high</th>\n",
" <th>low</th>\n",
" <th>close</th>\n",
" <th>tick_volume</th>\n",
" <th>spread</th>\n",
" <th>real_volume</th>\n",
" <th>ema21</th>\n",
" <th>ema50</th>\n",
" <th>rsi9</th>\n",
" <th>rsi14</th>\n",
" <th>trend</th>\n",
" <th>atr</th>\n",
" <th>adx</th>\n",
" <th>fast_signal</th>\n",
" <th>standard_signal</th>\n",
" <th>optimized_signal</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2025-08-28 05:25:00</td>\n",
" <td>3386.52</td>\n",
" <td>3386.74</td>\n",
" <td>3385.71</td>\n",
" <td>3386.64</td>\n",
" <td>714</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3386.640000</td>\n",
" <td>3386.640000</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>3385.220875</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025-08-28 05:30:00</td>\n",
" <td>3386.75</td>\n",
" <td>3386.98</td>\n",
" <td>3385.81</td>\n",
" <td>3385.85</td>\n",
" <td>985</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3386.113333</td>\n",
" <td>3386.201111</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>3386.775285</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2025-08-28 05:35:00</td>\n",
" <td>3385.86</td>\n",
" <td>3387.38</td>\n",
" <td>3384.59</td>\n",
" <td>3387.17</td>\n",
" <td>1151</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3386.717143</td>\n",
" <td>3386.598197</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>3388.065491</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2025-08-28 05:40:00</td>\n",
" <td>3387.12</td>\n",
" <td>3388.36</td>\n",
" <td>3386.82</td>\n",
" <td>3388.18</td>\n",
" <td>798</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3387.497333</td>\n",
" <td>3387.134038</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>3389.112151</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2025-08-28 05:45:00</td>\n",
" <td>3388.16</td>\n",
" <td>3390.56</td>\n",
" <td>3387.94</td>\n",
" <td>3390.46</td>\n",
" <td>947</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3389.026452</td>\n",
" <td>3388.123436</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>3389.935928</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>...</th>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" <td>...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>495</th>\n",
" <td>2025-08-29 23:40:00</td>\n",
" <td>3448.21</td>\n",
" <td>3449.35</td>\n",
" <td>3447.78</td>\n",
" <td>3449.17</td>\n",
" <td>421</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3448.743230</td>\n",
" <td>3448.456881</td>\n",
" <td>56.591716</td>\n",
" <td>56.452748</td>\n",
" <td>3448.267585</td>\n",
" <td>2.002968</td>\n",
" <td>15.874639</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>496</th>\n",
" <td>2025-08-29 23:45:00</td>\n",
" <td>3449.15</td>\n",
" <td>3449.18</td>\n",
" <td>3447.64</td>\n",
" <td>3447.90</td>\n",
" <td>578</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3448.321615</td>\n",
" <td>3448.345505</td>\n",
" <td>50.150670</td>\n",
" <td>52.209309</td>\n",
" <td>3447.941698</td>\n",
" <td>1.969899</td>\n",
" <td>15.547755</td>\n",
" <td>-1</td>\n",
" <td>-1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>497</th>\n",
" <td>2025-08-29 23:50:00</td>\n",
" <td>3447.86</td>\n",
" <td>3448.36</td>\n",
" <td>3447.68</td>\n",
" <td>3448.22</td>\n",
" <td>467</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3448.270808</td>\n",
" <td>3448.320404</td>\n",
" <td>51.708684</td>\n",
" <td>53.164605</td>\n",
" <td>3447.570685</td>\n",
" <td>1.877764</td>\n",
" <td>15.244220</td>\n",
" <td>-1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>498</th>\n",
" <td>2025-08-29 23:55:00</td>\n",
" <td>3448.20</td>\n",
" <td>3448.91</td>\n",
" <td>3447.97</td>\n",
" <td>3448.87</td>\n",
" <td>228</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3448.570404</td>\n",
" <td>3448.430323</td>\n",
" <td>54.927801</td>\n",
" <td>55.126747</td>\n",
" <td>3447.156651</td>\n",
" <td>1.810780</td>\n",
" <td>15.232676</td>\n",
" <td>1</td>\n",
" <td>0</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>499</th>\n",
" <td>2025-09-01 01:00:00</td>\n",
" <td>3444.73</td>\n",
" <td>3449.27</td>\n",
" <td>3444.67</td>\n",
" <td>3445.14</td>\n",
" <td>245</td>\n",
" <td>20</td>\n",
" <td>0</td>\n",
" <td>3446.855202</td>\n",
" <td>3447.772258</td>\n",
" <td>38.401814</td>\n",
" <td>43.789528</td>\n",
" <td>3446.701703</td>\n",
" <td>2.010010</td>\n",
" <td>14.843679</td>\n",
" <td>-1</td>\n",
" <td>-1</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>500 rows × 18 columns</p>\n",
"</div>"
],
"text/plain": [
" time open high low close tick_volume \\\n",
"0 2025-08-28 05:25:00 3386.52 3386.74 3385.71 3386.64 714 \n",
"1 2025-08-28 05:30:00 3386.75 3386.98 3385.81 3385.85 985 \n",
"2 2025-08-28 05:35:00 3385.86 3387.38 3384.59 3387.17 1151 \n",
"3 2025-08-28 05:40:00 3387.12 3388.36 3386.82 3388.18 798 \n",
"4 2025-08-28 05:45:00 3388.16 3390.56 3387.94 3390.46 947 \n",
".. ... ... ... ... ... ... \n",
"495 2025-08-29 23:40:00 3448.21 3449.35 3447.78 3449.17 421 \n",
"496 2025-08-29 23:45:00 3449.15 3449.18 3447.64 3447.90 578 \n",
"497 2025-08-29 23:50:00 3447.86 3448.36 3447.68 3448.22 467 \n",
"498 2025-08-29 23:55:00 3448.20 3448.91 3447.97 3448.87 228 \n",
"499 2025-09-01 01:00:00 3444.73 3449.27 3444.67 3445.14 245 \n",
"\n",
" spread real_volume ema21 ema50 rsi9 rsi14 \\\n",
"0 20 0 3386.640000 3386.640000 NaN NaN \n",
"1 20 0 3386.113333 3386.201111 NaN NaN \n",
"2 20 0 3386.717143 3386.598197 NaN NaN \n",
"3 20 0 3387.497333 3387.134038 NaN NaN \n",
"4 20 0 3389.026452 3388.123436 NaN NaN \n",
".. ... ... ... ... ... ... \n",
"495 20 0 3448.743230 3448.456881 56.591716 56.452748 \n",
"496 20 0 3448.321615 3448.345505 50.150670 52.209309 \n",
"497 20 0 3448.270808 3448.320404 51.708684 53.164605 \n",
"498 20 0 3448.570404 3448.430323 54.927801 55.126747 \n",
"499 20 0 3446.855202 3447.772258 38.401814 43.789528 \n",
"\n",
" trend atr adx fast_signal standard_signal \\\n",
"0 3385.220875 NaN NaN 0 0 \n",
"1 3386.775285 NaN NaN 0 0 \n",
"2 3388.065491 NaN NaN 0 0 \n",
"3 3389.112151 NaN NaN 0 0 \n",
"4 3389.935928 NaN NaN 0 0 \n",
".. ... ... ... ... ... \n",
"495 3448.267585 2.002968 15.874639 1 0 \n",
"496 3447.941698 1.969899 15.547755 -1 -1 \n",
"497 3447.570685 1.877764 15.244220 -1 0 \n",
"498 3447.156651 1.810780 15.232676 1 0 \n",
"499 3446.701703 2.010010 14.843679 -1 -1 \n",
"\n",
" optimized_signal \n",
"0 0 \n",
"1 0 \n",
"2 0 \n",
"3 0 \n",
"4 0 \n",
".. ... \n",
"495 0 \n",
"496 0 \n",
"497 0 \n",
"498 0 \n",
"499 0 \n",
"\n",
"[500 rows x 18 columns]"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"generate_signal()"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
"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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Update TP-SL on open positions"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [],
"source": [
"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"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Get M5 Trade Signals"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_m5_trade_signals(symbol=symbol, atr_mult=1.5, base_rrr=2.0, atr_min=0.0005, slope_factor=1.5):\n",
" \"\"\"\n",
" M5 Trade Signal mit dynamischem Seitwärtsfilter + dynamischer RRR-Berechnung:\n",
" - ATR-Minimum prüft ob Markt volatil genug ist\n",
" - Linear Regression ersetzt Savitzky-Golay für Trenddetektion\n",
" - adaptive Filterung nach ATR und Trend-Slope\n",
" - dynamisches RRR (Chance-Risiko-Verhältnis) auf Basis von ATR + Slope\n",
" \"\"\"\n",
"\n",
" global trend_dict, periods_dict, pause_trading, volume_dict\n",
" set_trend()\n",
"\n",
" # if pause_trading == 1:\n",
" # pos = mt.positions_total()\n",
" # if pos > 0:\n",
" # open_positions = mt.positions_get()\n",
" # print(f\"⚠️ Trading pausiert, offene Positionen auf {symbol} werden geschlossen...\")\n",
" # for pos in open_positions:\n",
" # market_order(symbol, volume_dict[symbol], \"sell\")\n",
" # return {\"signal\": 0, \"reason\": \"Trading paused\"}\n",
"\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",
" if atr < atr_min:\n",
" return {\"signal\": 0, \"reason\": \"ATR too low → sideways\"}\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",
" if abs(slope_long) < slope_threshold and abs(slope_short) < slope_threshold:\n",
" return {\"signal\": 0, \"reason\": \"Trend flat → sideways\"}\n",
"\n",
" # --- Trendlogik ---\n",
" trend_standard = \"uptrend\" if slope_long > 0 else \"downtrend\"\n",
" trend_fast = \"uptrend\" if slope_short > 0 else \"downtrend\"\n",
"\n",
" current_price = 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",
" print(f\"[RRR-DEBUG] ATR_norm={atr_norm:.5f} | slope_strength={slope_strength:.5f} | \"\n",
" f\"rrr_vol={rrr_from_vol:.2f} | rrr_slope={rrr_from_slope:.2f} | FINAL_RRR={rrr:.2f}\")\n",
"\n",
" # --- Signale ---\n",
" signal, stop_loss, takeprofit = 0, None, None\n",
" if trend_standard == \"uptrend\" and trend_fast == \"uptrend\":\n",
" signal = 1\n",
" stop_loss = current_price - atr_mult * atr\n",
" takeprofit = current_price + atr_mult * atr * rrr\n",
" print(f\"BUY {symbol} @ {current_price} | TP: {takeprofit} | SL: {stop_loss}\")\n",
" market_order(symbol, volume_dict[symbol], \"buy\", stoploss=stop_loss, take_profit=takeprofit)\n",
"\n",
" elif trend_standard == \"downtrend\" and trend_fast == \"downtrend\":\n",
" signal = -1\n",
" stop_loss = current_price + atr_mult * atr\n",
" takeprofit = current_price - atr_mult * atr * rrr\n",
" print(f\"SELL {symbol} @ {current_price} | TP: {takeprofit} | SL: {stop_loss}\")\n",
" market_order(symbol, volume_dict[symbol], \"sell\", stoploss=stop_loss, take_profit=takeprofit)\n",
"\n",
" # --- SL/TP sowohl mit ATR als auch mit Gewinn-Trailing\n",
" # manage_open_trades()\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",
" # --- speichern ---\n",
" trend_dict['m5'] = {\"standard\": trend_standard, \"fast\": trend_fast, \"signal\": signal}\n",
"\n",
" return {\n",
" \"signal\": signal,\n",
" \"price\": current_price,\n",
" \"stop_loss\": stop_loss,\n",
" \"take_profit\": takeprofit,\n",
" \"Risk Reward\": rrr,\n",
" \"trends\": trend_dict['m5'],\n",
" \"atr\": atr,\n",
" \"slope_long\": slope_long,\n",
" \"slope_short\": slope_short\n",
" }\n"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"m5\n",
"Pause Trading: 1\n"
]
},
{
"data": {
"text/plain": [
"{'signal': 0, 'reason': 'Trading paused'}"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"get_m5_trade_signals()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "base",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}