Initial commit
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,486 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loaded DL model from models/saved_models/best_lstm_model.h5\n",
|
||||
"Loaded scaler from models/saved_models/lstm_scaler.pkl\n",
|
||||
"Using hardcoded training_columns. Ensure they match your training pipeline.\n",
|
||||
"Loaded final pipeline for BTCUSD\n",
|
||||
"Checking market status...\n",
|
||||
"Market is closed. No actions performed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"import os\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series\n",
|
||||
"sys.path.append(str(project_root))\n",
|
||||
"os.chdir(str(project_root))\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import MetaTrader5 as mt5\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import ta\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"from sklearn.model_selection import train_test_split\n",
|
||||
"from sklearn.preprocessing import StandardScaler\n",
|
||||
"import time\n",
|
||||
"import logging\n",
|
||||
"import joblib\n",
|
||||
"import tensorflow as tf\n",
|
||||
"from tensorflow.keras.models import load_model\n",
|
||||
"from tensorflow.keras.optimizers import Adam\n",
|
||||
"\n",
|
||||
"# Setup logging\n",
|
||||
"logging.basicConfig(\n",
|
||||
" filename='models/saved_models/trading_app.log',\n",
|
||||
" level=logging.INFO,\n",
|
||||
" format='%(asctime)s %(levelname)s:%(message)s',\n",
|
||||
" datefmt='%Y-%m-%d %H:%M:%S'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def log_and_print(message, is_error=False):\n",
|
||||
" if is_error:\n",
|
||||
" logging.error(message)\n",
|
||||
" else:\n",
|
||||
" logging.info(message)\n",
|
||||
" print(message)\n",
|
||||
"\n",
|
||||
"# Update the login credentials and server information accordingly\n",
|
||||
"name = 524434\n",
|
||||
"key = 'un@uxXssffsf3'\n",
|
||||
"serv = 'ICMarketsSC-Demo'\n",
|
||||
"\n",
|
||||
"# Global variables\n",
|
||||
"SYMBOL = \"BTCUSD\" # e.g., \"EURUSD\", \"BTCUSD\", \"AAPL\", etc.\n",
|
||||
"LOT_SIZE = 0.01\n",
|
||||
"TIMEFRAME = mt5.TIMEFRAME_D1\n",
|
||||
"N_BARS = 50000\n",
|
||||
"MAGIC_NUMBER = 234003\n",
|
||||
"SLEEP_TIME = 86400 # e.g., 86400 seconds = 24 hours; adjust as needed\n",
|
||||
"COMMENT_ML = \"regression return\"\n",
|
||||
"\n",
|
||||
"class TradingApp:\n",
|
||||
" def __init__(self, symbol, lot_size, magic_number, lookback=10):\n",
|
||||
" self.symbol = symbol\n",
|
||||
" self.lot_size = lot_size\n",
|
||||
" self.magic_number = magic_number\n",
|
||||
" self.pipeline = None # The loaded DL model\n",
|
||||
" self.scaler = None # The pre-fitted scaler\n",
|
||||
" self.training_columns = None # The exact columns (and order) used in training\n",
|
||||
" self.last_retrain_time = None\n",
|
||||
" self.lookback = lookback\n",
|
||||
"\n",
|
||||
" def get_data(self, symbol, n, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Fetch 'n' bars of historical data for the given symbol and timeframe.\n",
|
||||
" \"\"\"\n",
|
||||
" rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)\n",
|
||||
" rates_frame = pd.DataFrame(rates)\n",
|
||||
" rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')\n",
|
||||
" rates_frame.set_index('time', inplace=True)\n",
|
||||
" return rates_frame\n",
|
||||
"\n",
|
||||
" def add_all_ta_features(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" Add technical analysis features to the DataFrame using the 'ta' library.\n",
|
||||
" \"\"\"\n",
|
||||
" df = ta.add_all_ta_features(\n",
|
||||
" df,\n",
|
||||
" open=\"open\",\n",
|
||||
" high=\"high\",\n",
|
||||
" low=\"low\",\n",
|
||||
" close=\"close\",\n",
|
||||
" volume=\"tick_volume\",\n",
|
||||
" fillna=True\n",
|
||||
" )\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
" def load_pipeline(self, pipeline_path, scaler_path, columns_path=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Load the fine-tuned DL model and the pre-fitted scaler from disk.\n",
|
||||
" Also load or define the exact columns used during training.\n",
|
||||
" \"\"\"\n",
|
||||
" # Load the LSTM model\n",
|
||||
" self.pipeline = load_model(pipeline_path)\n",
|
||||
" self.pipeline.compile(optimizer=Adam(learning_rate=0.001), loss=\"mean_squared_error\")\n",
|
||||
" log_and_print(f\"Loaded DL model from {pipeline_path}\")\n",
|
||||
"\n",
|
||||
" # Load the pre-fitted scaler (MinMaxScaler or StandardScaler)\n",
|
||||
" self.scaler = joblib.load(scaler_path)\n",
|
||||
" log_and_print(f\"Loaded scaler from {scaler_path}\")\n",
|
||||
"\n",
|
||||
" # Option 1: If you stored columns in a file (e.g., .pkl)\n",
|
||||
" if columns_path is not None:\n",
|
||||
" self.training_columns = joblib.load(columns_path)\n",
|
||||
" log_and_print(f\"Loaded training columns from {columns_path}\")\n",
|
||||
" else:\n",
|
||||
" # Option 2: Define them here manually (must match your training code)\n",
|
||||
" self.training_columns = [\n",
|
||||
" # Example: 'open', 'high', 'low', 'close', 'SMA_10', 'SMA_20', ...\n",
|
||||
" # Fill in the exact column names in correct order from your training pipeline\n",
|
||||
" 'open', 'high', 'low', 'close', 'tick_volume',\n",
|
||||
" 'volume_adi', 'volume_obv', 'trend_sma_fast', 'trend_sma_slow', \n",
|
||||
" # ... etc ...\n",
|
||||
" ]\n",
|
||||
" log_and_print(\"Using hardcoded training_columns. Ensure they match your training pipeline.\")\n",
|
||||
"\n",
|
||||
" def ml_signal_generation(self, symbol, n_bars, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate buy/sell signals using the loaded DL model.\n",
|
||||
" Steps:\n",
|
||||
" 1) Fetch new data.\n",
|
||||
" 2) Add TA features and fill missing values.\n",
|
||||
" 3) Subset and transform the features using the pre-fitted scaler.\n",
|
||||
" 4) Create a sequence from the last 'lookback' bars.\n",
|
||||
" 5) Predict the next return.\n",
|
||||
" 6) Generate signals based on the prediction.\n",
|
||||
" \"\"\"\n",
|
||||
" if self.pipeline is None or self.scaler is None or self.training_columns is None:\n",
|
||||
" log_and_print(\"Model, scaler, or training_columns not loaded. Call load_pipeline(...) first.\", is_error=True)\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 1) Fetch new data\n",
|
||||
" df = self.get_data(symbol, n_bars, timeframe)\n",
|
||||
" # 2) Add TA features\n",
|
||||
" df = self.add_all_ta_features(df)\n",
|
||||
" df.fillna(method='ffill', inplace=True)\n",
|
||||
"\n",
|
||||
" # 3) Subset columns to match training, then transform with the pre-fitted scaler\n",
|
||||
" # Ensure all required columns exist\n",
|
||||
" missing_cols = [col for col in self.training_columns if col not in df.columns]\n",
|
||||
" if missing_cols:\n",
|
||||
" log_and_print(f\"Missing columns in live data: {missing_cols}\", is_error=True)\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # Reindex to ensure correct order\n",
|
||||
" df_sub = df.reindex(columns=self.training_columns)\n",
|
||||
" X_new = self.scaler.transform(df_sub.values)\n",
|
||||
"\n",
|
||||
" if len(X_new) < self.lookback:\n",
|
||||
" log_and_print(\"Not enough data to form a sequence for prediction.\", is_error=True)\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 4) Create sequence for LSTM: use the last 'lookback' rows\n",
|
||||
" X_seq = np.array([X_new[-self.lookback:]])\n",
|
||||
" # 5) Predict with the DL model\n",
|
||||
" predictions = self.pipeline.predict(X_seq)\n",
|
||||
" latest_pred = predictions[0, 0]\n",
|
||||
"\n",
|
||||
" # 6) Generate signals\n",
|
||||
" buy_signal = latest_pred > 0\n",
|
||||
" sell_signal = latest_pred < 0\n",
|
||||
"\n",
|
||||
" return buy_signal, sell_signal, not buy_signal, not sell_signal\n",
|
||||
"\n",
|
||||
" def orders(self, symbol, lot, is_buy=True, id_position=None, sl=None, tp=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Place an order (BUY or SELL) for the specified symbol and lot size.\n",
|
||||
" \"\"\"\n",
|
||||
" symbol_info = mt5.symbol_info(symbol)\n",
|
||||
" if symbol_info is None:\n",
|
||||
" log_and_print(f\"Symbol {symbol} not found, can't place order.\", is_error=True)\n",
|
||||
" return \"Symbol not found\"\n",
|
||||
"\n",
|
||||
" if not symbol_info.visible:\n",
|
||||
" if not mt5.symbol_select(symbol, True):\n",
|
||||
" log_and_print(f\"Failed to select symbol {symbol}\", is_error=True)\n",
|
||||
" return \"Symbol not visible or could not be selected.\"\n",
|
||||
"\n",
|
||||
" tick_info = mt5.symbol_info_tick(symbol)\n",
|
||||
" if tick_info is None:\n",
|
||||
" log_and_print(f\"Could not get tick info for {symbol}.\", is_error=True)\n",
|
||||
" return \"Tick info unavailable\"\n",
|
||||
"\n",
|
||||
" if tick_info.bid <= 0 or tick_info.ask <= 0:\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Zero or invalid bid/ask for {symbol}: bid={tick_info.bid}, ask={tick_info.ask}\",\n",
|
||||
" is_error=True\n",
|
||||
" )\n",
|
||||
" return \"Invalid prices\"\n",
|
||||
"\n",
|
||||
" # Adjust lot size to broker constraints\n",
|
||||
" lot = max(lot, symbol_info.volume_min)\n",
|
||||
" step = symbol_info.volume_step\n",
|
||||
" if step > 0:\n",
|
||||
" remainder = lot % step\n",
|
||||
" if remainder != 0:\n",
|
||||
" lot = lot - remainder + step\n",
|
||||
" if lot > symbol_info.volume_max:\n",
|
||||
" lot = symbol_info.volume_max\n",
|
||||
"\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Adjusted lot size to {lot} (min={symbol_info.volume_min}, \"\n",
|
||||
" f\"step={symbol_info.volume_step}, max={symbol_info.volume_max})\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" filling_mode = 1 # ORDER_FILLING_IOC\n",
|
||||
" order_type = mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL\n",
|
||||
" deviation = 20\n",
|
||||
"\n",
|
||||
" request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": lot,\n",
|
||||
" \"type\": order_type,\n",
|
||||
" \"deviation\": deviation,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": filling_mode,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if sl is not None:\n",
|
||||
" request[\"sl\"] = sl\n",
|
||||
" if tp is not None:\n",
|
||||
" request[\"tp\"] = tp\n",
|
||||
" if id_position is not None:\n",
|
||||
" request[\"position\"] = id_position\n",
|
||||
"\n",
|
||||
" log_and_print(f\"Sending order request: {request}\")\n",
|
||||
" result = mt5.order_send(request)\n",
|
||||
"\n",
|
||||
" order_type_str = \"BUY\" if is_buy else \"SELL\"\n",
|
||||
" if result is None or result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Order failed for {symbol}\"\n",
|
||||
" if result:\n",
|
||||
" error_message += f\", retcode={result.retcode}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\\n\"\n",
|
||||
" f\"Request: {request}\\n\"\n",
|
||||
" f\"Result: {result}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(f\"Order failed details: {additional_info}\", is_error=True)\n",
|
||||
" else:\n",
|
||||
" success_message = f\"Order successful for {symbol}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(success_message)\n",
|
||||
"\n",
|
||||
" def get_positions_by_magic(self, symbol, magic_number):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve open positions for the specified symbol and magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" all_positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not all_positions:\n",
|
||||
" log_and_print(\"No positions found.\", is_error=False)\n",
|
||||
" return []\n",
|
||||
" return [pos for pos in all_positions if pos.magic == magic_number]\n",
|
||||
"\n",
|
||||
" def run_strategy(self, symbol, lot, buy_signal, sell_signal):\n",
|
||||
" \"\"\"\n",
|
||||
" Based on buy/sell signals, decide whether to open or close positions.\n",
|
||||
" \"\"\"\n",
|
||||
" log_and_print(\"------------------------------------------------------------------\")\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, \"\n",
|
||||
" f\"SYMBOL: {symbol}, BUY SIGNAL: {buy_signal}, SELL SIGNAL: {sell_signal}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" positions = self.get_positions_by_magic(symbol, self.magic_number)\n",
|
||||
" has_buy = any(pos.type == mt5.POSITION_TYPE_BUY for pos in positions)\n",
|
||||
" has_sell = any(pos.type == mt5.POSITION_TYPE_SELL for pos in positions)\n",
|
||||
"\n",
|
||||
" if buy_signal and not has_buy:\n",
|
||||
" if has_sell:\n",
|
||||
" log_and_print(\"Existing sell positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=True):\n",
|
||||
" log_and_print(\"Sell positions closed. Placing new buy order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close sell positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" elif sell_signal and not has_sell:\n",
|
||||
" if has_buy:\n",
|
||||
" log_and_print(\"Existing buy positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=False):\n",
|
||||
" log_and_print(\"Buy positions closed. Placing new sell order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close buy positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Appropriate position already exists or no signal to act on.\")\n",
|
||||
"\n",
|
||||
" def close_position(self, symbol, is_buy):\n",
|
||||
" \"\"\"\n",
|
||||
" Closes positions of the opposite type (BUY/SELL) for this app's magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not positions:\n",
|
||||
" log_and_print(f\"No positions to close for symbol: {symbol}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" initial_balance = mt5.account_info().balance\n",
|
||||
" closed_any = False\n",
|
||||
"\n",
|
||||
" for position in positions:\n",
|
||||
" if position.magic == self.magic_number and (\n",
|
||||
" (is_buy and position.type == mt5.POSITION_TYPE_SELL) or\n",
|
||||
" (not is_buy and position.type == mt5.POSITION_TYPE_BUY)\n",
|
||||
" ):\n",
|
||||
" close_request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": position.volume,\n",
|
||||
" \"type\": mt5.ORDER_TYPE_BUY if position.type == mt5.POSITION_TYPE_SELL else mt5.ORDER_TYPE_SELL,\n",
|
||||
" \"position\": position.ticket,\n",
|
||||
" \"deviation\": 20,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": mt5.ORDER_FILLING_RETURN,\n",
|
||||
" }\n",
|
||||
" result = mt5.order_send(close_request)\n",
|
||||
" if result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = (\n",
|
||||
" f\"Failed to close position {position.ticket} for {symbol}: {result.retcode}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(f\"Successfully closed position {position.ticket} for {symbol}\")\n",
|
||||
" closed_any = True\n",
|
||||
"\n",
|
||||
" if closed_any:\n",
|
||||
" final_balance = mt5.account_info().balance\n",
|
||||
" profit = final_balance - initial_balance\n",
|
||||
" success_message = f\"Closed positions successfully, Profit: {profit}\"\n",
|
||||
" log_and_print(success_message)\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" def check_and_execute_trades(self):\n",
|
||||
" \"\"\"\n",
|
||||
" Convenience method to perform the entire flow: \n",
|
||||
" generate signals, run strategy, and deselect symbol.\n",
|
||||
" \"\"\"\n",
|
||||
" mt5.symbol_select(self.symbol, True)\n",
|
||||
" buy, sell, _, _ = self.ml_signal_generation(self.symbol, N_BARS, TIMEFRAME)\n",
|
||||
" self.run_strategy(self.symbol, self.lot_size, buy, sell)\n",
|
||||
" mt5.symbol_select(self.symbol, False)\n",
|
||||
" log_and_print(\"Waiting for new signals...\")\n",
|
||||
"\n",
|
||||
"def is_market_open():\n",
|
||||
" \"\"\"\n",
|
||||
" Check if the current time is within typical Forex trading session hours (CET/CEST).\n",
|
||||
" - Closes: Friday 10:00 PM CET\n",
|
||||
" - Opens: Sunday 11:00 PM CET\n",
|
||||
" - Closed all day Saturday\n",
|
||||
" \"\"\"\n",
|
||||
" current_time_utc = datetime.utcnow()\n",
|
||||
" # Adjust for DST (CET=UTC+1, CEST=UTC+2)\n",
|
||||
" current_time_cet = current_time_utc + timedelta(hours=2) if time.localtime().tm_isdst else current_time_utc + timedelta(hours=1)\n",
|
||||
"\n",
|
||||
" # Friday after 10 PM CET\n",
|
||||
" if current_time_cet.weekday() == 4 and current_time_cet.hour >= 22:\n",
|
||||
" return False\n",
|
||||
" # Sunday before 11 PM CET\n",
|
||||
" elif current_time_cet.weekday() == 6 and current_time_cet.hour < 23:\n",
|
||||
" return False\n",
|
||||
" # All day Saturday\n",
|
||||
" elif current_time_cet.weekday() == 5:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" try:\n",
|
||||
" if not mt5.initialize(login=name, server=serv, password=key):\n",
|
||||
" log_and_print(\"Failed to initialize MetaTrader 5\", is_error=True)\n",
|
||||
" exit()\n",
|
||||
"\n",
|
||||
" app = TradingApp(symbol=SYMBOL, lot_size=LOT_SIZE, magic_number=MAGIC_NUMBER)\n",
|
||||
"\n",
|
||||
" # Load the final LSTM model and the matching scaler + columns\n",
|
||||
" pipeline_path = \"models/saved_models/best_lstm_model.h5\"\n",
|
||||
" scaler_path = \"models/saved_models/lstm_scaler.pkl\"\n",
|
||||
" \n",
|
||||
"\n",
|
||||
" app.load_pipeline(\n",
|
||||
" pipeline_path=pipeline_path,\n",
|
||||
" scaler_path=scaler_path\n",
|
||||
" \n",
|
||||
" )\n",
|
||||
" log_and_print(f\"Loaded final pipeline for {app.symbol}\")\n",
|
||||
"\n",
|
||||
" while True:\n",
|
||||
" log_and_print(\"Checking market status...\")\n",
|
||||
" if is_market_open():\n",
|
||||
" log_and_print(\"Market is open. Executing trades...\")\n",
|
||||
"\n",
|
||||
" # Generate signals using the loaded DL model\n",
|
||||
" buy_signal, sell_signal, _, _ = app.ml_signal_generation(\n",
|
||||
" symbol=app.symbol,\n",
|
||||
" n_bars=N_BARS,\n",
|
||||
" timeframe=TIMEFRAME\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Run strategy\n",
|
||||
" app.run_strategy(app.symbol, app.lot_size, buy_signal, sell_signal)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Market is closed. No actions performed.\")\n",
|
||||
"\n",
|
||||
" # Sleep for the configured interval (e.g., 24 hours)\n",
|
||||
" time.sleep(SLEEP_TIME)\n",
|
||||
"\n",
|
||||
" except KeyboardInterrupt:\n",
|
||||
" log_and_print(\"Shutdown signal received.\")\n",
|
||||
" except Exception as e:\n",
|
||||
" error_message = f\"An error occurred: {e}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" finally:\n",
|
||||
" mt5.shutdown()\n",
|
||||
" log_and_print(\"MetaTrader 5 shutdown completed.\")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "dl",
|
||||
"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.12.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,460 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loaded final pipeline for EURUSD\n",
|
||||
"Checking market status...\n",
|
||||
"Market is closed. No actions performed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"import os\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series\n",
|
||||
"sys.path.append(str(project_root))\n",
|
||||
"os.chdir(str(project_root))\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import warnings\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import MetaTrader5 as mt5\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import ta\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"from sklearn.model_selection import train_test_split\n",
|
||||
"from sklearn.preprocessing import StandardScaler\n",
|
||||
"from sklearn.ensemble import RandomForestRegressor\n",
|
||||
"from sklearn.feature_selection import SelectFromModel\n",
|
||||
"import time\n",
|
||||
"import logging\n",
|
||||
"import joblib\n",
|
||||
"\n",
|
||||
"# Setup logging\n",
|
||||
"logging.basicConfig(\n",
|
||||
" filename='models/saved_models/trading_app.log',\n",
|
||||
" level=logging.INFO,\n",
|
||||
" format='%(asctime)s %(levelname)s:%(message)s',\n",
|
||||
" datefmt='%Y-%m-%d %H:%M:%S'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def log_and_print(message, is_error=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Logs and prints a message. \n",
|
||||
" If is_error=True, logs at the ERROR level; otherwise logs at INFO level.\n",
|
||||
" \"\"\"\n",
|
||||
" if is_error:\n",
|
||||
" logging.error(message)\n",
|
||||
" else:\n",
|
||||
" logging.info(message)\n",
|
||||
" print(message)\n",
|
||||
"\n",
|
||||
"# Update the login credentials and server information accordingly\n",
|
||||
"name = 7889999\n",
|
||||
"key = 'hdgdggFxEG38'\n",
|
||||
"serv = 'ICMarketsSC-Demo'\n",
|
||||
"\n",
|
||||
"# Global variables\n",
|
||||
"SYMBOL = \"EURUSD\"\n",
|
||||
"LOT_SIZE = 0.01\n",
|
||||
"TIMEFRAME = mt5.TIMEFRAME_D1\n",
|
||||
"N_BARS = 50000\n",
|
||||
"MAGIC_NUMBER = 234003\n",
|
||||
"SLEEP_TIME = 86400 # 4 hours in seconds\n",
|
||||
"COMMENT_ML = \"regression return\"\n",
|
||||
"\n",
|
||||
"def select_features_rf_reg(X, y, estimator, max_features=20):\n",
|
||||
" \"\"\"\n",
|
||||
" Use a RandomForest (or similar) to select top 'max_features' features.\n",
|
||||
" \"\"\"\n",
|
||||
" selector = SelectFromModel(estimator=estimator, threshold=-np.inf, max_features=max_features).fit(X, y)\n",
|
||||
" X_transformed = selector.transform(X)\n",
|
||||
" selected_features_mask = selector.get_support()\n",
|
||||
" return X_transformed, selected_features_mask\n",
|
||||
"\n",
|
||||
"class TradingApp:\n",
|
||||
" def __init__(self, symbol, lot_size, magic_number):\n",
|
||||
" self.symbol = symbol\n",
|
||||
" self.lot_size = lot_size\n",
|
||||
" self.magic_number = magic_number\n",
|
||||
" self.pipeline = None # We'll store the loaded pipeline here\n",
|
||||
" self.last_retrain_time = None\n",
|
||||
"\n",
|
||||
" def get_data(self, symbol, n, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Fetch 'n' bars of historical data for the given symbol and timeframe.\n",
|
||||
" \"\"\"\n",
|
||||
" rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)\n",
|
||||
" rates_frame = pd.DataFrame(rates)\n",
|
||||
" rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')\n",
|
||||
" rates_frame.set_index('time', inplace=True)\n",
|
||||
" return rates_frame\n",
|
||||
"\n",
|
||||
" def add_all_ta_features(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" Add technical analysis features to the DataFrame using the 'ta' library.\n",
|
||||
" \"\"\"\n",
|
||||
" df = ta.add_all_ta_features(\n",
|
||||
" df,\n",
|
||||
" open=\"open\",\n",
|
||||
" high=\"high\",\n",
|
||||
" low=\"low\",\n",
|
||||
" close=\"close\",\n",
|
||||
" volume=\"tick_volume\",\n",
|
||||
" fillna=True\n",
|
||||
" )\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
" def load_pipeline(self, pipeline_path):\n",
|
||||
" \"\"\"\n",
|
||||
" Load a pre-trained pipeline (scaler + model + possibly feature selection)\n",
|
||||
" from disk, e.g. 'best_rf_pipeline.pkl'.\n",
|
||||
" \"\"\"\n",
|
||||
" self.pipeline = joblib.load(pipeline_path)\n",
|
||||
" logging.info(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
"\n",
|
||||
" def ml_signal_generation(self, symbol, n_bars, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate buy/sell signals using the loaded pipeline.\n",
|
||||
" Make sure the pipeline expects the same features as we create below.\n",
|
||||
" \"\"\"\n",
|
||||
" if self.pipeline is None:\n",
|
||||
" logging.error(\"No pipeline loaded. Call load_pipeline(...) first.\")\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 1) Fetch new data\n",
|
||||
" df = self.get_data(symbol, n_bars, timeframe)\n",
|
||||
" # 2) Add TA features (if your pipeline doesn't handle feature eng, do it here)\n",
|
||||
" df = self.add_all_ta_features(df)\n",
|
||||
" df.fillna(method='ffill', inplace=True)\n",
|
||||
"\n",
|
||||
" # 3) Prepare the features (the pipeline will do scaling/selection if included)\n",
|
||||
" X_new = df # If your pipeline expects specific columns, subset accordingly.\n",
|
||||
"\n",
|
||||
" # 4) Predict with the pipeline\n",
|
||||
" predictions = self.pipeline.predict(X_new)\n",
|
||||
" latest_pred = predictions[-1] # Get the most recent bar's prediction\n",
|
||||
"\n",
|
||||
" buy_signal = latest_pred > 0\n",
|
||||
" sell_signal = latest_pred < 0\n",
|
||||
"\n",
|
||||
" return buy_signal, sell_signal, not buy_signal, not sell_signal\n",
|
||||
"\n",
|
||||
" def calculate_future_returns(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" (Optional) Example function to calculate future returns for labeling.\n",
|
||||
" \"\"\"\n",
|
||||
" df[\"future_returns\"] = df[\"close\"].pct_change().shift(-1)\n",
|
||||
" return df.dropna()\n",
|
||||
"\n",
|
||||
" def orders(self, symbol, lot, is_buy=True, id_position=None, sl=None, tp=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Place an order (BUY or SELL) for the specified symbol and lot size.\n",
|
||||
" \"\"\"\n",
|
||||
" symbol_info = mt5.symbol_info(symbol)\n",
|
||||
" if symbol_info is None:\n",
|
||||
" log_and_print(f\"Symbol {symbol} not found, can't place order.\", is_error=True)\n",
|
||||
" return \"Symbol not found\"\n",
|
||||
"\n",
|
||||
" # Make sure symbol is selected/visible\n",
|
||||
" if not symbol_info.visible:\n",
|
||||
" if not mt5.symbol_select(symbol, True):\n",
|
||||
" log_and_print(f\"Failed to select symbol {symbol}\", is_error=True)\n",
|
||||
" return \"Symbol not visible or could not be selected.\"\n",
|
||||
"\n",
|
||||
" tick_info = mt5.symbol_info_tick(symbol)\n",
|
||||
" if tick_info is None:\n",
|
||||
" log_and_print(f\"Could not get tick info for {symbol}.\", is_error=True)\n",
|
||||
" return \"Tick info unavailable\"\n",
|
||||
"\n",
|
||||
" # Check for valid bid/ask\n",
|
||||
" if tick_info.bid <= 0 or tick_info.ask <= 0:\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Zero or invalid bid/ask for {symbol}: bid={tick_info.bid}, ask={tick_info.ask}\",\n",
|
||||
" is_error=True\n",
|
||||
" )\n",
|
||||
" return \"Invalid prices\"\n",
|
||||
"\n",
|
||||
" # ----------- LOT SIZE VALIDATION -----------\n",
|
||||
" lot = max(lot, symbol_info.volume_min)\n",
|
||||
" step = symbol_info.volume_step\n",
|
||||
" if step > 0:\n",
|
||||
" remainder = lot % step\n",
|
||||
" if remainder != 0:\n",
|
||||
" lot = lot - remainder + step\n",
|
||||
" if lot > symbol_info.volume_max:\n",
|
||||
" lot = symbol_info.volume_max\n",
|
||||
"\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Adjusted lot size to {lot} (min={symbol_info.volume_min}, \"\n",
|
||||
" f\"step={symbol_info.volume_step}, max={symbol_info.volume_max})\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # ----------- FORCE ORDER_FILLING_IOC -----------\n",
|
||||
" filling_mode = 1 # ORDER_FILLING_IOC\n",
|
||||
"\n",
|
||||
" order_type = mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL\n",
|
||||
" deviation = 20\n",
|
||||
"\n",
|
||||
" request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": lot,\n",
|
||||
" \"type\": order_type,\n",
|
||||
" \"deviation\": deviation,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": filling_mode,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if sl is not None:\n",
|
||||
" request[\"sl\"] = sl\n",
|
||||
" if tp is not None:\n",
|
||||
" request[\"tp\"] = tp\n",
|
||||
" if id_position is not None:\n",
|
||||
" request[\"position\"] = id_position\n",
|
||||
"\n",
|
||||
" log_and_print(f\"Sending order request: {request}\")\n",
|
||||
" result = mt5.order_send(request)\n",
|
||||
"\n",
|
||||
" order_type_str = \"BUY\" if is_buy else \"SELL\"\n",
|
||||
" if result is None or result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Order failed for {symbol}\"\n",
|
||||
" if result:\n",
|
||||
" error_message += f\", retcode={result.retcode}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\\n\"\n",
|
||||
" f\"Request: {request}\\n\"\n",
|
||||
" f\"Result: {result}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(f\"Order failed details: {additional_info}\", is_error=True)\n",
|
||||
" else:\n",
|
||||
" success_message = f\"Order successful for {symbol}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(success_message)\n",
|
||||
"\n",
|
||||
" def get_positions_by_magic(self, symbol, magic_number):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve open positions for the specified symbol and magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" all_positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not all_positions:\n",
|
||||
" log_and_print(\"No positions found.\", is_error=False)\n",
|
||||
" return []\n",
|
||||
" return [pos for pos in all_positions if pos.magic == magic_number]\n",
|
||||
"\n",
|
||||
" def run_strategy(self, symbol, lot, buy_signal, sell_signal):\n",
|
||||
" \"\"\"\n",
|
||||
" Based on buy/sell signals, decide whether to open or close positions.\n",
|
||||
" \"\"\"\n",
|
||||
" log_and_print(\"------------------------------------------------------------------\")\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, \"\n",
|
||||
" f\"SYMBOL: {symbol}, BUY SIGNAL: {buy_signal}, SELL SIGNAL: {sell_signal}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Retrieve positions based on the magic number to manage trades specific to this instance\n",
|
||||
" positions = self.get_positions_by_magic(symbol, self.magic_number)\n",
|
||||
" has_buy = any(pos.type == mt5.POSITION_TYPE_BUY for pos in positions)\n",
|
||||
" has_sell = any(pos.type == mt5.POSITION_TYPE_SELL for pos in positions)\n",
|
||||
"\n",
|
||||
" # Decision making based on current signals and existing positions\n",
|
||||
" if buy_signal and not has_buy:\n",
|
||||
" if has_sell:\n",
|
||||
" log_and_print(\"Existing sell positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=True):\n",
|
||||
" log_and_print(\"Sell positions closed. Placing new buy order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close sell positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" elif sell_signal and not has_sell:\n",
|
||||
" if has_buy:\n",
|
||||
" log_and_print(\"Existing buy positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=False):\n",
|
||||
" log_and_print(\"Buy positions closed. Placing new sell order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close buy positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Appropriate position already exists or no signal to act on.\")\n",
|
||||
"\n",
|
||||
" def close_position(self, symbol, is_buy):\n",
|
||||
" \"\"\"\n",
|
||||
" Closes positions of the opposite type (BUY/SELL) for this app's magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not positions:\n",
|
||||
" log_and_print(f\"No positions to close for symbol: {symbol}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" initial_balance = mt5.account_info().balance\n",
|
||||
" closed_any = False\n",
|
||||
"\n",
|
||||
" for position in positions:\n",
|
||||
" # Close positions of the opposite type with the same magic number\n",
|
||||
" if position.magic == self.magic_number and (\n",
|
||||
" (is_buy and position.type == mt5.POSITION_TYPE_SELL) or\n",
|
||||
" (not is_buy and position.type == mt5.POSITION_TYPE_BUY)\n",
|
||||
" ):\n",
|
||||
" close_request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": position.volume,\n",
|
||||
" \"type\": mt5.ORDER_TYPE_BUY if position.type == mt5.POSITION_TYPE_SELL else mt5.ORDER_TYPE_SELL,\n",
|
||||
" \"position\": position.ticket,\n",
|
||||
" \"deviation\": 20,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": mt5.ORDER_FILLING_RETURN,\n",
|
||||
" }\n",
|
||||
" result = mt5.order_send(close_request)\n",
|
||||
" if result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = (\n",
|
||||
" f\"Failed to close position {position.ticket} for {symbol}: {result.retcode}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(f\"Successfully closed position {position.ticket} for {symbol}\")\n",
|
||||
" closed_any = True\n",
|
||||
"\n",
|
||||
" if closed_any:\n",
|
||||
" final_balance = mt5.account_info().balance\n",
|
||||
" profit = final_balance - initial_balance\n",
|
||||
" success_message = f\"Closed positions successfully, Profit: {profit}\"\n",
|
||||
" log_and_print(success_message)\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" def check_and_execute_trades(self):\n",
|
||||
" \"\"\"\n",
|
||||
" Convenience method to perform the entire flow: \n",
|
||||
" generate signals, run strategy, and deselect symbol.\n",
|
||||
" \"\"\"\n",
|
||||
" mt5.symbol_select(self.symbol, True)\n",
|
||||
" buy, sell, _, _ = self.ml_signal_generation(self.symbol, N_BARS, TIMEFRAME)\n",
|
||||
" self.run_strategy(self.symbol, self.lot_size, buy, sell)\n",
|
||||
" mt5.symbol_select(self.symbol, False)\n",
|
||||
" log_and_print(\"Waiting for new signals...\")\n",
|
||||
"\n",
|
||||
"def is_market_open():\n",
|
||||
" \"\"\"\n",
|
||||
" Check if the current time is within typical Forex trading session hours (CET/CEST).\n",
|
||||
" - Closes: Friday 10:00 PM CET\n",
|
||||
" - Opens: Sunday 11:00 PM CET\n",
|
||||
" - Closed all day Saturday\n",
|
||||
" \"\"\"\n",
|
||||
" current_time_utc = datetime.utcnow()\n",
|
||||
" # Adjust for CET (UTC+1) or CEST (UTC+2)\n",
|
||||
" current_time_cet = current_time_utc + timedelta(hours=2) if time.localtime().tm_isdst else current_time_utc + timedelta(hours=1)\n",
|
||||
"\n",
|
||||
" # Friday after 10 PM CET\n",
|
||||
" if current_time_cet.weekday() == 4 and current_time_cet.hour >= 22:\n",
|
||||
" return False\n",
|
||||
" # Sunday before 11 PM CET\n",
|
||||
" elif current_time_cet.weekday() == 6 and current_time_cet.hour < 23:\n",
|
||||
" return False\n",
|
||||
" # All day Saturday\n",
|
||||
" elif current_time_cet.weekday() == 5:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" try:\n",
|
||||
" if not mt5.initialize(login=name, server=serv, password=key):\n",
|
||||
" log_and_print(\"Failed to initialize MetaTrader 5\", is_error=True)\n",
|
||||
" exit()\n",
|
||||
"\n",
|
||||
" app = TradingApp(symbol=SYMBOL, lot_size=LOT_SIZE, magic_number=MAGIC_NUMBER)\n",
|
||||
"\n",
|
||||
" # Load a previously trained pipeline (scaler + model, etc.)\n",
|
||||
" pipeline_path = \"models/saved_models/best_rf_pipeline.pkl\"\n",
|
||||
" app.load_pipeline(pipeline_path)\n",
|
||||
" log_and_print(f\"Loaded final pipeline for {app.symbol}\")\n",
|
||||
"\n",
|
||||
" while True:\n",
|
||||
" log_and_print(\"Checking market status...\")\n",
|
||||
" if is_market_open():\n",
|
||||
" log_and_print(\"Market is open. Executing trades...\")\n",
|
||||
"\n",
|
||||
" # Generate signals using the loaded pipeline\n",
|
||||
" buy_signal, sell_signal, _, _ = app.ml_signal_generation(\n",
|
||||
" symbol=app.symbol,\n",
|
||||
" n_bars=N_BARS,\n",
|
||||
" timeframe=TIMEFRAME\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Run strategy\n",
|
||||
" app.run_strategy(app.symbol, app.lot_size, buy_signal, sell_signal)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Market is closed. No actions performed.\")\n",
|
||||
"\n",
|
||||
" # Sleep for the configured interval (e.g., 4 hours)\n",
|
||||
" time.sleep(SLEEP_TIME)\n",
|
||||
"\n",
|
||||
" except KeyboardInterrupt:\n",
|
||||
" log_and_print(\"Shutdown signal received.\")\n",
|
||||
" except Exception as e:\n",
|
||||
" error_message = f\"An error occurred: {e}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" finally:\n",
|
||||
" mt5.shutdown()\n",
|
||||
" log_and_print(\"MetaTrader 5 shutdown completed.\")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "ml",
|
||||
"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.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Failed to initialize MetaTrader 5\n",
|
||||
"Loaded pipeline from models/saved_models/best_rf_db_pipeline.pkl\n",
|
||||
"Checking market status...\n",
|
||||
"Market is closed. No actions performed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# LIVE TRADING CODE FOR DOUBLE-BARRIER CLASSIFICATION\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"import os\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series\n",
|
||||
"sys.path.append(str(project_root))\n",
|
||||
"os.chdir(str(project_root))\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import MetaTrader5 as mt5\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import ta\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"import time\n",
|
||||
"import logging\n",
|
||||
"import joblib\n",
|
||||
"\n",
|
||||
"# Setup logging\n",
|
||||
"logging.basicConfig(\n",
|
||||
" filename='models/saved_models/trading_app_db.log',\n",
|
||||
" level=logging.INFO,\n",
|
||||
" format='%(asctime)s %(levelname)s:%(message)s',\n",
|
||||
" datefmt='%Y-%m-%d %H:%M:%S'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def log_and_print(message, is_error=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Logs and prints a message.\n",
|
||||
" If is_error=True, logs at the ERROR level; otherwise logs at INFO level.\n",
|
||||
" \"\"\"\n",
|
||||
" if is_error:\n",
|
||||
" logging.error(message)\n",
|
||||
" else:\n",
|
||||
" logging.info(message)\n",
|
||||
" print(message)\n",
|
||||
"\n",
|
||||
"# Update the login credentials and server information accordingly\n",
|
||||
"name = 66677507\n",
|
||||
"key = 'ST746$nG38'\n",
|
||||
"serv = 'ICMarketsSC-Demo'\n",
|
||||
"\n",
|
||||
"# Global variables\n",
|
||||
"SYMBOL = \"EURUSD\"\n",
|
||||
"LOT_SIZE = 0.01\n",
|
||||
"TIMEFRAME = mt5.TIMEFRAME_D1\n",
|
||||
"N_BARS = 50000\n",
|
||||
"MAGIC_NUMBER = 234003\n",
|
||||
"SLEEP_TIME = 86400 # e.g. 24 hours\n",
|
||||
"COMMENT_ML = \"DoubleBarrier-ML\"\n",
|
||||
"\n",
|
||||
"class TradingApp:\n",
|
||||
" def __init__(self, symbol, lot_size, magic_number):\n",
|
||||
" self.symbol = symbol\n",
|
||||
" self.lot_size = lot_size\n",
|
||||
" self.magic_number = magic_number\n",
|
||||
" self.pipeline = None # We'll store the loaded classification pipeline here\n",
|
||||
" self.last_retrain_time = None\n",
|
||||
"\n",
|
||||
" def get_data(self, symbol, n, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Fetch the last 'n' bars from MetaTrader 5 for the given timeframe.\n",
|
||||
" \"\"\"\n",
|
||||
" rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)\n",
|
||||
" rates_frame = pd.DataFrame(rates)\n",
|
||||
" rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')\n",
|
||||
" rates_frame.set_index('time', inplace=True)\n",
|
||||
" return rates_frame\n",
|
||||
"\n",
|
||||
" def add_all_ta_features(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" Add technical analysis features to the DataFrame using 'ta' library.\n",
|
||||
" \"\"\"\n",
|
||||
" df = ta.add_all_ta_features(\n",
|
||||
" df, open=\"open\", high=\"high\", low=\"low\", close=\"close\", volume=\"tick_volume\", fillna=True\n",
|
||||
" )\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
" def load_pipeline(self, pipeline_path):\n",
|
||||
" \"\"\"\n",
|
||||
" Loads a pre-trained classification pipeline (e.g., final_production_pipeline.pkl)\n",
|
||||
" that was trained on SHIFTED double-barrier labels in {0,1,2}.\n",
|
||||
" \"\"\"\n",
|
||||
" self.pipeline = joblib.load(pipeline_path)\n",
|
||||
" logging.info(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
" log_and_print(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
"\n",
|
||||
" def ml_signal_generation(self, symbol, n_bars, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate buy/sell signals using the loaded classification pipeline.\n",
|
||||
" The pipeline outputs SHIFTED labels in {0,1,2} => SHIFT them back to {-1,0,+1}.\n",
|
||||
" We'll interpret +1 => buy, -1 => sell, 0 => no trade.\n",
|
||||
"\n",
|
||||
" Double-Barrier labeling was used offline to train this pipeline,\n",
|
||||
" so we just replicate the same feature engineering steps and let the model predict.\n",
|
||||
" \"\"\"\n",
|
||||
" if self.pipeline is None:\n",
|
||||
" logging.error(\"No pipeline loaded. Call load_pipeline(...) first.\")\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 1) Fetch new data\n",
|
||||
" df = self.get_data(symbol, n_bars, timeframe)\n",
|
||||
" # 2) Add TA features\n",
|
||||
" df = self.add_all_ta_features(df)\n",
|
||||
" df.fillna(method='ffill', inplace=True)\n",
|
||||
"\n",
|
||||
" # 3) Prepare the features\n",
|
||||
" X_new = df\n",
|
||||
"\n",
|
||||
" # 4) Predict SHIFTED classes\n",
|
||||
" preds_shifted = self.pipeline.predict(X_new)\n",
|
||||
" # SHIFT them back: 0->-1, 1->0, 2->+1\n",
|
||||
" preds = preds_shifted - 1\n",
|
||||
"\n",
|
||||
" # Get the latest predicted class\n",
|
||||
" latest_pred = preds[-1]\n",
|
||||
" # If latest_pred == +1 => buy signal\n",
|
||||
" # If latest_pred == -1 => sell signal\n",
|
||||
" # If 0 => no trade\n",
|
||||
" buy_signal = (latest_pred == 1)\n",
|
||||
" sell_signal = (latest_pred == -1)\n",
|
||||
"\n",
|
||||
" return buy_signal, sell_signal, not buy_signal, not sell_signal\n",
|
||||
"\n",
|
||||
" def orders(self, symbol, lot, is_buy=True, id_position=None, sl=None, tp=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Send an order (buy/sell) to MetaTrader 5.\n",
|
||||
" \"\"\"\n",
|
||||
" symbol_info = mt5.symbol_info(symbol)\n",
|
||||
" if symbol_info is None:\n",
|
||||
" log_and_print(f\"Symbol {symbol} not found, can't place order.\", is_error=True)\n",
|
||||
" return \"Symbol not found\"\n",
|
||||
"\n",
|
||||
" # Make sure symbol is visible\n",
|
||||
" if not symbol_info.visible:\n",
|
||||
" if not mt5.symbol_select(symbol, True):\n",
|
||||
" log_and_print(f\"Failed to select symbol {symbol}\", is_error=True)\n",
|
||||
" return \"Symbol not visible or could not be selected.\"\n",
|
||||
"\n",
|
||||
" tick_info = mt5.symbol_info_tick(symbol)\n",
|
||||
" if tick_info is None:\n",
|
||||
" log_and_print(f\"Could not get tick info for {symbol}.\", is_error=True)\n",
|
||||
" return \"Tick info unavailable\"\n",
|
||||
"\n",
|
||||
" # Check for valid bid/ask\n",
|
||||
" if tick_info.bid <= 0 or tick_info.ask <= 0:\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Zero or invalid bid/ask for {symbol}: bid={tick_info.bid}, ask={tick_info.ask}\",\n",
|
||||
" is_error=True\n",
|
||||
" )\n",
|
||||
" return \"Invalid prices\"\n",
|
||||
"\n",
|
||||
" # LOT SIZE VALIDATION\n",
|
||||
" lot = max(lot, symbol_info.volume_min)\n",
|
||||
" step = symbol_info.volume_step\n",
|
||||
" if step > 0:\n",
|
||||
" remainder = lot % step\n",
|
||||
" if remainder != 0:\n",
|
||||
" lot = lot - remainder + step\n",
|
||||
" if lot > symbol_info.volume_max:\n",
|
||||
" lot = symbol_info.volume_max\n",
|
||||
"\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Adjusted lot size to {lot} (min={symbol_info.volume_min}, \"\n",
|
||||
" f\"step={symbol_info.volume_step}, max={symbol_info.volume_max})\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Force ORDER_FILLING_IOC\n",
|
||||
" filling_mode = mt5.ORDER_FILLING_IOC\n",
|
||||
"\n",
|
||||
" order_type = mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL\n",
|
||||
" deviation = 20\n",
|
||||
"\n",
|
||||
" request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": lot,\n",
|
||||
" \"type\": order_type,\n",
|
||||
" \"deviation\": deviation,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": filling_mode,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if sl is not None:\n",
|
||||
" request[\"sl\"] = sl\n",
|
||||
" if tp is not None:\n",
|
||||
" request[\"tp\"] = tp\n",
|
||||
" if id_position is not None:\n",
|
||||
" request[\"position\"] = id_position\n",
|
||||
"\n",
|
||||
" log_and_print(f\"Sending order request: {request}\")\n",
|
||||
" result = mt5.order_send(request)\n",
|
||||
"\n",
|
||||
" order_type_str = \"BUY\" if is_buy else \"SELL\"\n",
|
||||
" if result is None or result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Order failed for {symbol}\"\n",
|
||||
" if result:\n",
|
||||
" error_message += f\", retcode={result.retcode}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\\n\"\n",
|
||||
" f\"Request: {request}\\n\"\n",
|
||||
" f\"Result: {result}\"\n",
|
||||
" )\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(f\"Order failed details: {additional_info}\", is_error=True)\n",
|
||||
" else:\n",
|
||||
" success_message = f\"Order successful for {symbol}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\"\n",
|
||||
" )\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(success_message)\n",
|
||||
"\n",
|
||||
" def get_positions_by_magic(self, symbol, magic_number):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve positions for a specific symbol and magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" all_positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not all_positions:\n",
|
||||
" log_and_print(\"No positions found.\", is_error=False)\n",
|
||||
" return []\n",
|
||||
" return [pos for pos in all_positions if pos.magic == magic_number]\n",
|
||||
"\n",
|
||||
" def run_strategy(self, symbol, lot, buy_signal, sell_signal):\n",
|
||||
" \"\"\"\n",
|
||||
" Decide whether to open a buy or sell order based on signals,\n",
|
||||
" close opposite positions if needed, etc.\n",
|
||||
" \"\"\"\n",
|
||||
" log_and_print(\"------------------------------------------------------------------\")\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, \"\n",
|
||||
" f\"SYMBOL: {symbol}, BUY SIGNAL: {buy_signal}, SELL SIGNAL: {sell_signal}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" positions = self.get_positions_by_magic(symbol, self.magic_number)\n",
|
||||
" has_buy = any(pos.type == mt5.POSITION_TYPE_BUY for pos in positions)\n",
|
||||
" has_sell = any(pos.type == mt5.POSITION_TYPE_SELL for pos in positions)\n",
|
||||
"\n",
|
||||
" if buy_signal and not has_buy:\n",
|
||||
" if has_sell:\n",
|
||||
" log_and_print(\"Existing sell positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=True):\n",
|
||||
" log_and_print(\"Sell positions closed. Placing new buy order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close sell positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" elif sell_signal and not has_sell:\n",
|
||||
" if has_buy:\n",
|
||||
" log_and_print(\"Existing buy positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=False):\n",
|
||||
" log_and_print(\"Buy positions closed. Placing new sell order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close buy positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Appropriate position already exists or no signal to act on.\")\n",
|
||||
"\n",
|
||||
" def close_position(self, symbol, is_buy):\n",
|
||||
" \"\"\"\n",
|
||||
" Close all positions of the opposite type for the given symbol & magic.\n",
|
||||
" \"\"\"\n",
|
||||
" positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not positions:\n",
|
||||
" log_and_print(f\"No positions to close for symbol: {symbol}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" initial_balance = mt5.account_info().balance\n",
|
||||
" closed_any = False\n",
|
||||
"\n",
|
||||
" for position in positions:\n",
|
||||
" if position.magic == self.magic_number:\n",
|
||||
" # if is_buy==True => we want to close SELL positions\n",
|
||||
" # if is_buy==False => we want to close BUY positions\n",
|
||||
" if ((is_buy and position.type == mt5.POSITION_TYPE_SELL) or\n",
|
||||
" (not is_buy and position.type == mt5.POSITION_TYPE_BUY)):\n",
|
||||
" close_request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": position.volume,\n",
|
||||
" \"type\": mt5.ORDER_TYPE_BUY if position.type == mt5.POSITION_TYPE_SELL else mt5.ORDER_TYPE_SELL,\n",
|
||||
" \"position\": position.ticket,\n",
|
||||
" \"deviation\": 20,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": mt5.ORDER_FILLING_RETURN,\n",
|
||||
" }\n",
|
||||
" result = mt5.order_send(close_request)\n",
|
||||
" if result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = (\n",
|
||||
" f\"Failed to close position {position.ticket} for {symbol}: {result.retcode}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" else:\n",
|
||||
" log_and_print(f\"Successfully closed position {position.ticket} for {symbol}\")\n",
|
||||
" closed_any = True\n",
|
||||
"\n",
|
||||
" if closed_any:\n",
|
||||
" final_balance = mt5.account_info().balance\n",
|
||||
" profit = final_balance - initial_balance\n",
|
||||
" success_message = f\"Closed positions successfully, Profit: {profit}\"\n",
|
||||
" log_and_print(success_message)\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" def check_and_execute_trades(self):\n",
|
||||
" \"\"\"\n",
|
||||
" Called in the main loop: generate signals, run strategy, etc.\n",
|
||||
" \"\"\"\n",
|
||||
" mt5.symbol_select(self.symbol, True)\n",
|
||||
" buy, sell, _, _ = self.ml_signal_generation(self.symbol, N_BARS, TIMEFRAME)\n",
|
||||
" self.run_strategy(self.symbol, self.lot_size, buy, sell)\n",
|
||||
" mt5.symbol_select(self.symbol, False)\n",
|
||||
" log_and_print(\"Waiting for new signals...\")\n",
|
||||
"\n",
|
||||
"def is_market_open():\n",
|
||||
" \"\"\"\n",
|
||||
" Check if the current time is within the typical Forex trading session, adjusted for CET/CEST.\n",
|
||||
" Market closes at Friday 10:00 PM CET and opens at Sunday 11:00 PM CET.\n",
|
||||
" \"\"\"\n",
|
||||
" current_time_utc = datetime.utcnow()\n",
|
||||
" current_time_cet = (\n",
|
||||
" current_time_utc + timedelta(hours=2) \n",
|
||||
" if time.localtime().tm_isdst \n",
|
||||
" else current_time_utc + timedelta(hours=1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Market closes Friday after 10 PM CET\n",
|
||||
" if current_time_cet.weekday() == 4 and current_time_cet.hour >= 22:\n",
|
||||
" return False\n",
|
||||
" # Market opens Sunday after 11 PM CET\n",
|
||||
" elif current_time_cet.weekday() == 6 and current_time_cet.hour < 23:\n",
|
||||
" return False\n",
|
||||
" # Closed all day Saturday\n",
|
||||
" elif current_time_cet.weekday() == 5:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" try:\n",
|
||||
" if not mt5.initialize(login=name, server=serv, password=key):\n",
|
||||
" log_and_print(\"Failed to initialize MetaTrader 5\", is_error=True)\n",
|
||||
" exit()\n",
|
||||
"\n",
|
||||
" app = TradingApp(symbol=SYMBOL, lot_size=LOT_SIZE, magic_number=MAGIC_NUMBER)\n",
|
||||
"\n",
|
||||
" # 1) Load the classification pipeline\n",
|
||||
" # Make sure this pipeline is a classification model expecting SHIFTED double-barrier labels in {0,1,2}\n",
|
||||
" pipeline_path = \"models/saved_models/best_rf_db_pipeline.pkl\"\n",
|
||||
" app.load_pipeline(pipeline_path)\n",
|
||||
"\n",
|
||||
" while True:\n",
|
||||
" log_and_print(\"Checking market status...\")\n",
|
||||
" if is_market_open():\n",
|
||||
" log_and_print(\"Market is open. Executing trades...\")\n",
|
||||
"\n",
|
||||
" # 2) Generate signals using the loaded pipeline\n",
|
||||
" # This pipeline is classification-based => SHIFTED labels {0,1,2}\n",
|
||||
" # ml_signal_generation() SHIFTs them back to [-1,0,+1]\n",
|
||||
" buy_signal, sell_signal, _, _ = app.ml_signal_generation(\n",
|
||||
" symbol=app.symbol,\n",
|
||||
" n_bars=N_BARS,\n",
|
||||
" timeframe=TIMEFRAME\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # 3) Run strategy\n",
|
||||
" app.run_strategy(app.symbol, app.lot_size, buy_signal, sell_signal)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Market is closed. No actions performed.\")\n",
|
||||
"\n",
|
||||
" time.sleep(SLEEP_TIME)\n",
|
||||
"\n",
|
||||
" except KeyboardInterrupt:\n",
|
||||
" log_and_print(\"Shutdown signal received.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" except Exception as e:\n",
|
||||
" error_message = f\"An error occurred: {e}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" finally:\n",
|
||||
" mt5.shutdown()\n",
|
||||
" log_and_print(\"MetaTrader 5 shutdown completed.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "ml",
|
||||
"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.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,451 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loaded pipeline from models/saved_models/best_rf_rd_pipeline.pkl\n",
|
||||
"Checking market status...\n",
|
||||
"Market is closed. No actions performed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# LIVE TRADING CODE FOR REGIME DETECTION CLASSIFICATION\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"import os\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series\n",
|
||||
"sys.path.append(str(project_root))\n",
|
||||
"os.chdir(str(project_root))\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import warnings\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import MetaTrader5 as mt5\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import ta\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"import time\n",
|
||||
"import logging\n",
|
||||
"import joblib\n",
|
||||
"\n",
|
||||
"# Setup logging\n",
|
||||
"logging.basicConfig(\n",
|
||||
" filename='models/saved_models/trading_app.log',\n",
|
||||
" level=logging.INFO,\n",
|
||||
" format='%(asctime)s %(levelname)s:%(message)s',\n",
|
||||
" datefmt='%Y-%m-%d %H:%M:%S'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def log_and_print(message, is_error=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Logs and prints a message.\n",
|
||||
" If is_error=True, logs at the ERROR level; otherwise logs at INFO level.\n",
|
||||
" \"\"\"\n",
|
||||
" if is_error:\n",
|
||||
" logging.error(message)\n",
|
||||
" else:\n",
|
||||
" logging.info(message)\n",
|
||||
" print(message)\n",
|
||||
"\n",
|
||||
"# Update the login credentials and server information accordingly\n",
|
||||
"name = 66677507\n",
|
||||
"key = 'ST746$nG38'\n",
|
||||
"serv = 'ICMarketsSC-Demo'\n",
|
||||
"\n",
|
||||
"# Global variables\n",
|
||||
"SYMBOL = \"EURUSD\"\n",
|
||||
"LOT_SIZE = 0.01\n",
|
||||
"TIMEFRAME = mt5.TIMEFRAME_D1\n",
|
||||
"N_BARS = 50000\n",
|
||||
"MAGIC_NUMBER = 234003\n",
|
||||
"SLEEP_TIME = 86400 # 24 hours in seconds\n",
|
||||
"COMMENT_ML = \"Regime-Detection\"\n",
|
||||
"\n",
|
||||
"class TradingApp:\n",
|
||||
" def __init__(self, symbol, lot_size, magic_number):\n",
|
||||
" self.symbol = symbol\n",
|
||||
" self.lot_size = lot_size\n",
|
||||
" self.magic_number = magic_number\n",
|
||||
" self.pipeline = None # We'll store the loaded classification pipeline here\n",
|
||||
" self.last_retrain_time = None\n",
|
||||
"\n",
|
||||
" def get_data(self, symbol, n, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Fetch the last 'n' bars from MetaTrader 5 for the given timeframe.\n",
|
||||
" \"\"\"\n",
|
||||
" rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)\n",
|
||||
" rates_frame = pd.DataFrame(rates)\n",
|
||||
" rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')\n",
|
||||
" rates_frame.set_index('time', inplace=True)\n",
|
||||
" return rates_frame\n",
|
||||
"\n",
|
||||
" def add_all_ta_features(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" Add technical analysis features to the DataFrame using the 'ta' library.\n",
|
||||
" \"\"\"\n",
|
||||
" df = ta.add_all_ta_features(\n",
|
||||
" df, open=\"open\", high=\"high\", low=\"low\", close=\"close\", volume=\"tick_volume\", fillna=True\n",
|
||||
" )\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
" def load_pipeline(self, pipeline_path):\n",
|
||||
" \"\"\"\n",
|
||||
" Loads a pre-trained classification pipeline (e.g., final_production_pipeline.pkl).\n",
|
||||
" This pipeline is expected to produce SHIFTED labels [0,1,2].\n",
|
||||
" \"\"\"\n",
|
||||
" self.pipeline = joblib.load(pipeline_path)\n",
|
||||
" logging.info(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
" log_and_print(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
"\n",
|
||||
" def ml_signal_generation(self, symbol, n_bars, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate buy/sell signals using the loaded classification pipeline.\n",
|
||||
" The pipeline outputs SHIFTED labels in {0,1,2} => SHIFT them back to {-1,0,+1}.\n",
|
||||
" We'll interpret +1 => buy, -1 => sell, 0 => no trade.\n",
|
||||
" \"\"\"\n",
|
||||
" if self.pipeline is None:\n",
|
||||
" logging.error(\"No pipeline loaded. Call load_pipeline(...) first.\")\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 1) Fetch new data\n",
|
||||
" df = self.get_data(symbol, n_bars, timeframe)\n",
|
||||
" # 2) Add TA features\n",
|
||||
" df = self.add_all_ta_features(df)\n",
|
||||
" df.fillna(method='ffill', inplace=True)\n",
|
||||
"\n",
|
||||
" # 3) Prepare the features (the pipeline must handle columns in correct order)\n",
|
||||
" X_new = df\n",
|
||||
"\n",
|
||||
" # 4) Predict SHIFTED classes\n",
|
||||
" preds_shifted = self.pipeline.predict(X_new)\n",
|
||||
" # SHIFT them back: 0->-1, 1->0, 2->+1\n",
|
||||
" preds = preds_shifted - 1\n",
|
||||
"\n",
|
||||
" # Get the latest predicted class\n",
|
||||
" latest_pred = preds[-1]\n",
|
||||
" # If latest_pred == +1 => buy signal\n",
|
||||
" # If latest_pred == -1 => sell signal\n",
|
||||
" # If 0 => no trade\n",
|
||||
" buy_signal = (latest_pred == 1)\n",
|
||||
" sell_signal = (latest_pred == -1)\n",
|
||||
"\n",
|
||||
" return buy_signal, sell_signal, not buy_signal, not sell_signal\n",
|
||||
"\n",
|
||||
" def orders(self, symbol, lot, is_buy=True, id_position=None, sl=None, tp=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Send an order (buy/sell) to MetaTrader 5.\n",
|
||||
" \"\"\"\n",
|
||||
" symbol_info = mt5.symbol_info(symbol)\n",
|
||||
" if symbol_info is None:\n",
|
||||
" log_and_print(f\"Symbol {symbol} not found, can't place order.\", is_error=True)\n",
|
||||
" return \"Symbol not found\"\n",
|
||||
"\n",
|
||||
" # Make sure symbol is visible\n",
|
||||
" if not symbol_info.visible:\n",
|
||||
" if not mt5.symbol_select(symbol, True):\n",
|
||||
" log_and_print(f\"Failed to select symbol {symbol}\", is_error=True)\n",
|
||||
" return \"Symbol not visible or could not be selected.\"\n",
|
||||
"\n",
|
||||
" tick_info = mt5.symbol_info_tick(symbol)\n",
|
||||
" if tick_info is None:\n",
|
||||
" log_and_print(f\"Could not get tick info for {symbol}.\", is_error=True)\n",
|
||||
" return \"Tick info unavailable\"\n",
|
||||
"\n",
|
||||
" # Check for valid bid/ask\n",
|
||||
" if tick_info.bid <= 0 or tick_info.ask <= 0:\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Zero or invalid bid/ask for {symbol}: bid={tick_info.bid}, ask={tick_info.ask}\",\n",
|
||||
" is_error=True\n",
|
||||
" )\n",
|
||||
" return \"Invalid prices\"\n",
|
||||
"\n",
|
||||
" # LOT SIZE VALIDATION\n",
|
||||
" lot = max(lot, symbol_info.volume_min)\n",
|
||||
" step = symbol_info.volume_step\n",
|
||||
" if step > 0:\n",
|
||||
" remainder = lot % step\n",
|
||||
" if remainder != 0:\n",
|
||||
" lot = lot - remainder + step\n",
|
||||
" if lot > symbol_info.volume_max:\n",
|
||||
" lot = symbol_info.volume_max\n",
|
||||
"\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Adjusted lot size to {lot} (min={symbol_info.volume_min}, \"\n",
|
||||
" f\"step={symbol_info.volume_step}, max={symbol_info.volume_max})\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Force ORDER_FILLING_IOC\n",
|
||||
" filling_mode = mt5.ORDER_FILLING_IOC\n",
|
||||
"\n",
|
||||
" order_type = mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL\n",
|
||||
" deviation = 20\n",
|
||||
" order_price = tick_info.ask if is_buy else tick_info.bid\n",
|
||||
"\n",
|
||||
" request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": lot,\n",
|
||||
" \"type\": order_type,\n",
|
||||
" \"deviation\": deviation,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": filling_mode,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if sl is not None:\n",
|
||||
" request[\"sl\"] = sl\n",
|
||||
" if tp is not None:\n",
|
||||
" request[\"tp\"] = tp\n",
|
||||
" if id_position is not None:\n",
|
||||
" request[\"position\"] = id_position\n",
|
||||
"\n",
|
||||
" log_and_print(f\"Sending order request: {request}\")\n",
|
||||
" result = mt5.order_send(request)\n",
|
||||
"\n",
|
||||
" order_type_str = \"BUY\" if is_buy else \"SELL\"\n",
|
||||
" if result is None or result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Order failed for {symbol}\"\n",
|
||||
" if result:\n",
|
||||
" error_message += f\", retcode={result.retcode}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\\n\"\n",
|
||||
" f\"Request: {request}\\n\"\n",
|
||||
" f\"Result: {result}\"\n",
|
||||
" )\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(f\"Order failed details: {additional_info}\", is_error=True)\n",
|
||||
" else:\n",
|
||||
" success_message = f\"Order successful for {symbol}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\"\n",
|
||||
" )\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(success_message)\n",
|
||||
"\n",
|
||||
" def get_positions_by_magic(self, symbol, magic_number):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve positions for a specific symbol and magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" all_positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not all_positions:\n",
|
||||
" log_and_print(\"No positions found.\", is_error=False)\n",
|
||||
" return []\n",
|
||||
" return [pos for pos in all_positions if pos.magic == magic_number]\n",
|
||||
"\n",
|
||||
" def run_strategy(self, symbol, lot, buy_signal, sell_signal):\n",
|
||||
" \"\"\"\n",
|
||||
" Decide whether to open a buy or sell order based on signals,\n",
|
||||
" close opposite positions if needed, etc.\n",
|
||||
" \"\"\"\n",
|
||||
" log_and_print(\"------------------------------------------------------------------\")\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, \"\n",
|
||||
" f\"SYMBOL: {symbol}, BUY SIGNAL: {buy_signal}, SELL SIGNAL: {sell_signal}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" positions = self.get_positions_by_magic(symbol, self.magic_number)\n",
|
||||
" has_buy = any(pos.type == mt5.POSITION_TYPE_BUY for pos in positions)\n",
|
||||
" has_sell = any(pos.type == mt5.POSITION_TYPE_SELL for pos in positions)\n",
|
||||
"\n",
|
||||
" if buy_signal and not has_buy:\n",
|
||||
" if has_sell:\n",
|
||||
" log_and_print(\"Existing sell positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=True):\n",
|
||||
" log_and_print(\"Sell positions closed. Placing new buy order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close sell positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" elif sell_signal and not has_sell:\n",
|
||||
" if has_buy:\n",
|
||||
" log_and_print(\"Existing buy positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=False):\n",
|
||||
" log_and_print(\"Buy positions closed. Placing new sell order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close buy positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Appropriate position already exists or no signal to act on.\")\n",
|
||||
"\n",
|
||||
" def close_position(self, symbol, is_buy):\n",
|
||||
" \"\"\"\n",
|
||||
" Close all positions of the opposite type for the given symbol & magic.\n",
|
||||
" \"\"\"\n",
|
||||
" positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not positions:\n",
|
||||
" log_and_print(f\"No positions to close for symbol: {symbol}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" initial_balance = mt5.account_info().balance\n",
|
||||
" closed_any = False\n",
|
||||
"\n",
|
||||
" for position in positions:\n",
|
||||
" if position.magic == self.magic_number:\n",
|
||||
" # if is_buy==True => we want to close SELL positions\n",
|
||||
" # if is_buy==False => we want to close BUY positions\n",
|
||||
" if ((is_buy and position.type == mt5.POSITION_TYPE_SELL) or\n",
|
||||
" (not is_buy and position.type == mt5.POSITION_TYPE_BUY)):\n",
|
||||
" close_request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": position.volume,\n",
|
||||
" \"type\": mt5.ORDER_TYPE_BUY if position.type == mt5.POSITION_TYPE_SELL else mt5.ORDER_TYPE_SELL,\n",
|
||||
" \"position\": position.ticket,\n",
|
||||
" \"deviation\": 20,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": mt5.ORDER_FILLING_RETURN,\n",
|
||||
" }\n",
|
||||
" result = mt5.order_send(close_request)\n",
|
||||
" if result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = (\n",
|
||||
" f\"Failed to close position {position.ticket} for {symbol}: {result.retcode}\"\n",
|
||||
" )\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you need notifications, you could log or handle them differently here.\n",
|
||||
" else:\n",
|
||||
" log_and_print(f\"Successfully closed position {position.ticket} for {symbol}\")\n",
|
||||
" closed_any = True\n",
|
||||
"\n",
|
||||
" if closed_any:\n",
|
||||
" final_balance = mt5.account_info().balance\n",
|
||||
" profit = final_balance - initial_balance\n",
|
||||
" success_message = f\"Closed positions successfully, Profit: {profit}\"\n",
|
||||
" log_and_print(success_message)\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" def check_and_execute_trades(self):\n",
|
||||
" \"\"\"\n",
|
||||
" Called in the main loop: generate signals, run strategy, etc.\n",
|
||||
" \"\"\"\n",
|
||||
" mt5.symbol_select(self.symbol, True)\n",
|
||||
" buy, sell, _, _ = self.ml_signal_generation(self.symbol, N_BARS, TIMEFRAME)\n",
|
||||
" self.run_strategy(self.symbol, self.lot_size, buy, sell)\n",
|
||||
" mt5.symbol_select(self.symbol, False)\n",
|
||||
" log_and_print(\"Waiting for new signals...\")\n",
|
||||
"\n",
|
||||
"def is_market_open():\n",
|
||||
" \"\"\"\n",
|
||||
" Check if the current time is within the typical Forex trading session, adjusted for CET/CEST.\n",
|
||||
" Market closes at Friday 10:00 PM CET and opens at Sunday 11:00 PM CET.\n",
|
||||
" \"\"\"\n",
|
||||
" current_time_utc = datetime.utcnow()\n",
|
||||
" current_time_cet = (\n",
|
||||
" current_time_utc + timedelta(hours=2) \n",
|
||||
" if time.localtime().tm_isdst \n",
|
||||
" else current_time_utc + timedelta(hours=1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Market closes Friday after 10 PM CET\n",
|
||||
" if current_time_cet.weekday() == 4 and current_time_cet.hour >= 22:\n",
|
||||
" return False\n",
|
||||
" # Market opens Sunday after 11 PM CET\n",
|
||||
" elif current_time_cet.weekday() == 6 and current_time_cet.hour < 23:\n",
|
||||
" return False\n",
|
||||
" # Closed all day Saturday\n",
|
||||
" elif current_time_cet.weekday() == 5:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" try:\n",
|
||||
" if not mt5.initialize(login=name, server=serv, password=key):\n",
|
||||
" log_and_print(\"Failed to initialize MetaTrader 5\", is_error=True)\n",
|
||||
" exit()\n",
|
||||
"\n",
|
||||
" app = TradingApp(symbol=SYMBOL, lot_size=LOT_SIZE, magic_number=MAGIC_NUMBER)\n",
|
||||
"\n",
|
||||
" # 1) Load the classification pipeline\n",
|
||||
" # Make sure this pipeline is a classification model expecting SHIFTED labels [0,1,2]\n",
|
||||
" pipeline_path = \"models/saved_models/best_rf_rd_pipeline.pkl\"\n",
|
||||
" app.load_pipeline(pipeline_path)\n",
|
||||
"\n",
|
||||
" while True:\n",
|
||||
" log_and_print(\"Checking market status...\")\n",
|
||||
" if is_market_open():\n",
|
||||
" log_and_print(\"Market is open. Executing trades...\")\n",
|
||||
"\n",
|
||||
" # 2) Generate signals using the loaded pipeline\n",
|
||||
" # This pipeline is classification-based => SHIFTED labels [0,1,2]\n",
|
||||
" # ml_signal_generation() SHIFTs them back to [-1,0,+1] for signals\n",
|
||||
" buy_signal, sell_signal, _, _ = app.ml_signal_generation(\n",
|
||||
" symbol=app.symbol,\n",
|
||||
" n_bars=N_BARS,\n",
|
||||
" timeframe=TIMEFRAME\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # 3) Run strategy\n",
|
||||
" app.run_strategy(app.symbol, app.lot_size, buy_signal, sell_signal)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Market is closed. No actions performed.\")\n",
|
||||
"\n",
|
||||
" time.sleep(SLEEP_TIME)\n",
|
||||
"\n",
|
||||
" except KeyboardInterrupt:\n",
|
||||
" log_and_print(\"Shutdown signal received.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" except Exception as e:\n",
|
||||
" error_message = f\"An error occurred: {e}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" finally:\n",
|
||||
" mt5.shutdown()\n",
|
||||
" log_and_print(\"MetaTrader 5 shutdown completed.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "ml",
|
||||
"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.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Loaded pipeline from models/saved_models/best_rf_mb_pipeline.pkl\n",
|
||||
"Checking market status...\n",
|
||||
"Market is closed. No actions performed.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# LIVE TRADING CODE FOR MULTI-BAR CLASSIFICATION\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"import os\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"# 1) SET PROJECT ROOT AND UPDATE PATH/WORKING DIRECTORY\n",
|
||||
"# ---------------------------------------------------------------------------\n",
|
||||
"project_root = Path.cwd().parent.parent # Adjust if your notebook is in notebooks/time_series\n",
|
||||
"sys.path.append(str(project_root))\n",
|
||||
"os.chdir(str(project_root))\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"\n",
|
||||
"import warnings\n",
|
||||
"warnings.filterwarnings(\"ignore\")\n",
|
||||
"import MetaTrader5 as mt5\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import ta\n",
|
||||
"from datetime import datetime, timedelta\n",
|
||||
"import time\n",
|
||||
"import logging\n",
|
||||
"import joblib\n",
|
||||
"\n",
|
||||
"# Setup logging\n",
|
||||
"logging.basicConfig(\n",
|
||||
" filename='models/saved_models/trading_app1.log',\n",
|
||||
" level=logging.INFO,\n",
|
||||
" format='%(asctime)s %(levelname)s:%(message)s',\n",
|
||||
" datefmt='%Y-%m-%d %H:%M:%S'\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"def log_and_print(message, is_error=False):\n",
|
||||
" \"\"\"\n",
|
||||
" Logs and prints a message.\n",
|
||||
" If is_error=True, logs at the ERROR level; otherwise logs at INFO level.\n",
|
||||
" \"\"\"\n",
|
||||
" if is_error:\n",
|
||||
" logging.error(message)\n",
|
||||
" else:\n",
|
||||
" logging.info(message)\n",
|
||||
" print(message)\n",
|
||||
"\n",
|
||||
"# Update the login credentials and server information accordingly\n",
|
||||
"name = 66677507\n",
|
||||
"key = 'ST746$nG38'\n",
|
||||
"serv = 'ICMarketsSC-Demo'\n",
|
||||
"\n",
|
||||
"# Global variables\n",
|
||||
"SYMBOL = \"EURUSD\"\n",
|
||||
"LOT_SIZE = 0.01\n",
|
||||
"TIMEFRAME = mt5.TIMEFRAME_D1\n",
|
||||
"N_BARS = 50000\n",
|
||||
"MAGIC_NUMBER = 234003\n",
|
||||
"SLEEP_TIME = 86400 # 24 hours in seconds\n",
|
||||
"COMMENT_ML = \"RFFV-D\"\n",
|
||||
"\n",
|
||||
"# If you still need feature selection, you can keep this helper function:\n",
|
||||
"def select_features_rf_reg(X, y, estimator, max_features=20):\n",
|
||||
" \"\"\"\n",
|
||||
" Example helper function for feature selection using RandomForest.\n",
|
||||
" \"\"\"\n",
|
||||
" from sklearn.feature_selection import SelectFromModel\n",
|
||||
" selector = SelectFromModel(estimator=estimator, threshold=-np.inf, max_features=max_features).fit(X, y)\n",
|
||||
" X_transformed = selector.transform(X)\n",
|
||||
" selected_features_mask = selector.get_support()\n",
|
||||
" return X_transformed, selected_features_mask\n",
|
||||
"\n",
|
||||
"class TradingApp:\n",
|
||||
" def __init__(self, symbol, lot_size, magic_number):\n",
|
||||
" self.symbol = symbol\n",
|
||||
" self.lot_size = lot_size\n",
|
||||
" self.magic_number = magic_number\n",
|
||||
" self.pipeline = None # We'll store the loaded classification pipeline here\n",
|
||||
" self.last_retrain_time = None\n",
|
||||
"\n",
|
||||
" def get_data(self, symbol, n, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Fetch 'n' bars of historical data for the given symbol and timeframe.\n",
|
||||
" \"\"\"\n",
|
||||
" rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, n)\n",
|
||||
" rates_frame = pd.DataFrame(rates)\n",
|
||||
" rates_frame['time'] = pd.to_datetime(rates_frame['time'], unit='s')\n",
|
||||
" rates_frame.set_index('time', inplace=True)\n",
|
||||
" return rates_frame\n",
|
||||
"\n",
|
||||
" def add_all_ta_features(self, df):\n",
|
||||
" \"\"\"\n",
|
||||
" Add technical analysis features to the DataFrame using the 'ta' library.\n",
|
||||
" \"\"\"\n",
|
||||
" df = ta.add_all_ta_features(\n",
|
||||
" df, open=\"open\", high=\"high\", low=\"low\", close=\"close\", volume=\"tick_volume\", fillna=True\n",
|
||||
" )\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
" def load_pipeline(self, pipeline_path):\n",
|
||||
" \"\"\"\n",
|
||||
" Loads a pre-trained classification pipeline (e.g., 'best_rf_pipeline.pkl').\n",
|
||||
" This pipeline is expected to produce SHIFTED labels [0,1,2].\n",
|
||||
" \"\"\"\n",
|
||||
" self.pipeline = joblib.load(pipeline_path)\n",
|
||||
" logging.info(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
" log_and_print(f\"Loaded pipeline from {pipeline_path}\")\n",
|
||||
"\n",
|
||||
" def ml_signal_generation(self, symbol, n_bars, timeframe):\n",
|
||||
" \"\"\"\n",
|
||||
" Generate buy/sell signals using the loaded classification pipeline.\n",
|
||||
" The pipeline outputs SHIFTED labels in {0,1,2} => we SHIFT them back to {-1,0,+1}.\n",
|
||||
" We'll interpret +1 => buy, -1 => sell, 0 => no trade.\n",
|
||||
" \"\"\"\n",
|
||||
" if self.pipeline is None:\n",
|
||||
" logging.error(\"No pipeline loaded. Call load_pipeline(...) first.\")\n",
|
||||
" return False, False, True, True\n",
|
||||
"\n",
|
||||
" # 1) Fetch new data\n",
|
||||
" df = self.get_data(symbol, n_bars, timeframe)\n",
|
||||
"\n",
|
||||
" # 2) Add TA features\n",
|
||||
" df = self.add_all_ta_features(df)\n",
|
||||
" df.fillna(method='ffill', inplace=True)\n",
|
||||
"\n",
|
||||
" # 3) Prepare the features\n",
|
||||
" X_new = df # The pipeline must handle columns in the correct order.\n",
|
||||
"\n",
|
||||
" # 4) Predict SHIFTED classes\n",
|
||||
" preds_shifted = self.pipeline.predict(X_new)\n",
|
||||
" # SHIFT them back: 0->-1, 1->0, 2->+1\n",
|
||||
" preds = preds_shifted - 1\n",
|
||||
"\n",
|
||||
" # Get the latest predicted class\n",
|
||||
" latest_pred = preds[-1]\n",
|
||||
" # If latest_pred == +1 => buy signal\n",
|
||||
" # If latest_pred == -1 => sell signal\n",
|
||||
" # If 0 => do nothing\n",
|
||||
" buy_signal = (latest_pred == 1)\n",
|
||||
" sell_signal = (latest_pred == -1)\n",
|
||||
"\n",
|
||||
" return buy_signal, sell_signal, not buy_signal, not sell_signal\n",
|
||||
"\n",
|
||||
" def orders(self, symbol, lot, is_buy=True, id_position=None, sl=None, tp=None):\n",
|
||||
" \"\"\"\n",
|
||||
" Place an order (BUY or SELL) for the specified symbol and lot size.\n",
|
||||
" \"\"\"\n",
|
||||
" symbol_info = mt5.symbol_info(symbol)\n",
|
||||
" if symbol_info is None:\n",
|
||||
" log_and_print(f\"Symbol {symbol} not found, can't place order.\", is_error=True)\n",
|
||||
" return \"Symbol not found\"\n",
|
||||
"\n",
|
||||
" # Make sure symbol is visible\n",
|
||||
" if not symbol_info.visible:\n",
|
||||
" if not mt5.symbol_select(symbol, True):\n",
|
||||
" log_and_print(f\"Failed to select symbol {symbol}\", is_error=True)\n",
|
||||
" return \"Symbol not visible or could not be selected.\"\n",
|
||||
"\n",
|
||||
" tick_info = mt5.symbol_info_tick(symbol)\n",
|
||||
" if tick_info is None:\n",
|
||||
" log_and_print(f\"Could not get tick info for {symbol}.\", is_error=True)\n",
|
||||
" return \"Tick info unavailable\"\n",
|
||||
"\n",
|
||||
" # Check for valid bid/ask\n",
|
||||
" if tick_info.bid <= 0 or tick_info.ask <= 0:\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Zero or invalid bid/ask for {symbol}: bid={tick_info.bid}, ask={tick_info.ask}\",\n",
|
||||
" is_error=True\n",
|
||||
" )\n",
|
||||
" return \"Invalid prices\"\n",
|
||||
"\n",
|
||||
" # LOT SIZE VALIDATION\n",
|
||||
" lot = max(lot, symbol_info.volume_min)\n",
|
||||
" step = symbol_info.volume_step\n",
|
||||
" if step > 0:\n",
|
||||
" remainder = lot % step\n",
|
||||
" if remainder != 0:\n",
|
||||
" lot = lot - remainder + step\n",
|
||||
" if lot > symbol_info.volume_max:\n",
|
||||
" lot = symbol_info.volume_max\n",
|
||||
"\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Adjusted lot size to {lot} (min={symbol_info.volume_min}, \"\n",
|
||||
" f\"step={symbol_info.volume_step}, max={symbol_info.volume_max})\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Force ORDER_FILLING_IOC\n",
|
||||
" filling_mode = 1 # ORDER_FILLING_IOC\n",
|
||||
"\n",
|
||||
" order_type = mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL\n",
|
||||
" order_price = tick_info.ask if is_buy else tick_info.bid\n",
|
||||
" deviation = 20\n",
|
||||
"\n",
|
||||
" request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": lot,\n",
|
||||
" \"type\": order_type,\n",
|
||||
" \"deviation\": deviation,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": filling_mode,\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" if sl is not None:\n",
|
||||
" request[\"sl\"] = sl\n",
|
||||
" if tp is not None:\n",
|
||||
" request[\"tp\"] = tp\n",
|
||||
" if id_position is not None:\n",
|
||||
" request[\"position\"] = id_position\n",
|
||||
"\n",
|
||||
" log_and_print(f\"Sending order request: {request}\")\n",
|
||||
" result = mt5.order_send(request)\n",
|
||||
"\n",
|
||||
" order_type_str = \"BUY\" if is_buy else \"SELL\"\n",
|
||||
" if result is None or result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Order failed for {symbol}\"\n",
|
||||
" if result:\n",
|
||||
" error_message += f\", retcode={result.retcode}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\\n\"\n",
|
||||
" f\"Request: {request}\\n\"\n",
|
||||
" f\"Result: {result}\"\n",
|
||||
" )\n",
|
||||
" # If you want notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(f\"Order failed details: {additional_info}\", is_error=True)\n",
|
||||
" else:\n",
|
||||
" success_message = f\"Order successful for {symbol}, comment={result.comment}\"\n",
|
||||
" additional_info = (\n",
|
||||
" f\"Date/Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\"\n",
|
||||
" f\"Order Type: {order_type_str}\\n\"\n",
|
||||
" f\"Lot Size: {lot}\\n\"\n",
|
||||
" f\"SL: {sl if sl else 'None'}\\n\"\n",
|
||||
" f\"TP: {tp if tp else 'None'}\\n\"\n",
|
||||
" f\"Comment: {COMMENT_ML}\"\n",
|
||||
" )\n",
|
||||
" # If you want notifications, you could log or handle them differently here.\n",
|
||||
" log_and_print(success_message)\n",
|
||||
"\n",
|
||||
" def get_positions_by_magic(self, symbol, magic_number):\n",
|
||||
" \"\"\"\n",
|
||||
" Retrieve positions for a specific symbol and magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" all_positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not all_positions:\n",
|
||||
" log_and_print(\"No positions found.\", is_error=False)\n",
|
||||
" return []\n",
|
||||
" return [pos for pos in all_positions if pos.magic == magic_number]\n",
|
||||
"\n",
|
||||
" def run_strategy(self, symbol, lot, buy_signal, sell_signal):\n",
|
||||
" \"\"\"\n",
|
||||
" Run the trading strategy logic based on buy/sell signals.\n",
|
||||
" \"\"\"\n",
|
||||
" log_and_print(\"------------------------------------------------------------------\")\n",
|
||||
" log_and_print(\n",
|
||||
" f\"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}, \"\n",
|
||||
" f\"SYMBOL: {symbol}, BUY SIGNAL: {buy_signal}, SELL SIGNAL: {sell_signal}\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" positions = self.get_positions_by_magic(symbol, self.magic_number)\n",
|
||||
" has_buy = any(pos.type == mt5.POSITION_TYPE_BUY for pos in positions)\n",
|
||||
" has_sell = any(pos.type == mt5.POSITION_TYPE_SELL for pos in positions)\n",
|
||||
"\n",
|
||||
" if buy_signal and not has_buy:\n",
|
||||
" if has_sell:\n",
|
||||
" log_and_print(\"Existing sell positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=True):\n",
|
||||
" log_and_print(\"Sell positions closed. Placing new buy order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close sell positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=True)\n",
|
||||
" elif sell_signal and not has_sell:\n",
|
||||
" if has_buy:\n",
|
||||
" log_and_print(\"Existing buy positions found. Attempting to close...\")\n",
|
||||
" if self.close_position(symbol, is_buy=False):\n",
|
||||
" log_and_print(\"Buy positions closed. Placing new sell order.\")\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Failed to close buy positions.\")\n",
|
||||
" else:\n",
|
||||
" self.orders(symbol, lot, is_buy=False)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Appropriate position already exists or no signal to act on.\")\n",
|
||||
"\n",
|
||||
" def close_position(self, symbol, is_buy):\n",
|
||||
" \"\"\"\n",
|
||||
" Closes positions of the opposite type (BUY/SELL) for this app's magic number.\n",
|
||||
" \"\"\"\n",
|
||||
" positions = mt5.positions_get(symbol=symbol)\n",
|
||||
" if not positions:\n",
|
||||
" log_and_print(f\"No positions to close for symbol: {symbol}\")\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" initial_balance = mt5.account_info().balance\n",
|
||||
" closed_any = False\n",
|
||||
"\n",
|
||||
" for position in positions:\n",
|
||||
" # Close positions of the opposite type with the same magic number\n",
|
||||
" if position.magic == self.magic_number and (\n",
|
||||
" (is_buy and position.type == mt5.POSITION_TYPE_SELL) or\n",
|
||||
" (not is_buy and position.type == mt5.POSITION_TYPE_BUY)\n",
|
||||
" ):\n",
|
||||
" close_request = {\n",
|
||||
" \"action\": mt5.TRADE_ACTION_DEAL,\n",
|
||||
" \"symbol\": symbol,\n",
|
||||
" \"volume\": position.volume,\n",
|
||||
" \"type\": mt5.ORDER_TYPE_BUY if position.type == mt5.POSITION_TYPE_SELL else mt5.ORDER_TYPE_SELL,\n",
|
||||
" \"position\": position.ticket,\n",
|
||||
" \"deviation\": 20,\n",
|
||||
" \"magic\": self.magic_number,\n",
|
||||
" \"comment\": COMMENT_ML,\n",
|
||||
" \"type_time\": mt5.ORDER_TIME_GTC,\n",
|
||||
" \"type_filling\": mt5.ORDER_FILLING_RETURN,\n",
|
||||
" }\n",
|
||||
" result = mt5.order_send(close_request)\n",
|
||||
" if result.retcode != mt5.TRADE_RETCODE_DONE:\n",
|
||||
" error_message = f\"Failed to close position {position.ticket} for {symbol}: {result.retcode}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you want notifications, you could log or handle them differently here.\n",
|
||||
" else:\n",
|
||||
" log_and_print(f\"Successfully closed position {position.ticket} for {symbol}\")\n",
|
||||
" closed_any = True\n",
|
||||
"\n",
|
||||
" if closed_any:\n",
|
||||
" final_balance = mt5.account_info().balance\n",
|
||||
" profit = final_balance - initial_balance\n",
|
||||
" success_message = f\"Closed positions successfully, Profit: {profit}\"\n",
|
||||
" log_and_print(success_message)\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
" return False\n",
|
||||
"\n",
|
||||
" def check_and_execute_trades(self):\n",
|
||||
" \"\"\"\n",
|
||||
" Convenience method to perform the entire flow:\n",
|
||||
" generate signals, run strategy, and deselect symbol.\n",
|
||||
" \"\"\"\n",
|
||||
" mt5.symbol_select(self.symbol, True)\n",
|
||||
" buy, sell, _, _ = self.ml_signal_generation(self.symbol, N_BARS, TIMEFRAME)\n",
|
||||
" self.run_strategy(self.symbol, self.lot_size, buy, sell)\n",
|
||||
" mt5.symbol_select(self.symbol, False)\n",
|
||||
" log_and_print(\"Waiting for new signals...\")\n",
|
||||
"\n",
|
||||
"def is_market_open():\n",
|
||||
" \"\"\"\n",
|
||||
" Check if the current time is within the typical Forex trading session, adjusted for CET/CEST.\n",
|
||||
" Market closes at Friday 10:00 PM CET and opens at Sunday 11:00 PM CET. \n",
|
||||
" It is closed all day Saturday.\n",
|
||||
" \"\"\"\n",
|
||||
" current_time_utc = datetime.utcnow()\n",
|
||||
" # Adjust for Central European Time (UTC+1) or Central European Summer Time (UTC+2)\n",
|
||||
" current_time_cet = (\n",
|
||||
" current_time_utc + timedelta(hours=2) \n",
|
||||
" if time.localtime().tm_isdst \n",
|
||||
" else current_time_utc + timedelta(hours=1)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Friday after 10 PM CET\n",
|
||||
" if current_time_cet.weekday() == 4 and current_time_cet.hour >= 22:\n",
|
||||
" return False\n",
|
||||
" # Sunday before 11 PM CET\n",
|
||||
" elif current_time_cet.weekday() == 6 and current_time_cet.hour < 23:\n",
|
||||
" return False\n",
|
||||
" # All day Saturday\n",
|
||||
" elif current_time_cet.weekday() == 5:\n",
|
||||
" return False\n",
|
||||
" return True\n",
|
||||
"\n",
|
||||
"if __name__ == \"__main__\":\n",
|
||||
" try:\n",
|
||||
" if not mt5.initialize(login=name, server=serv, password=key):\n",
|
||||
" log_and_print(\"Failed to initialize MetaTrader 5\", is_error=True)\n",
|
||||
" exit()\n",
|
||||
"\n",
|
||||
" app = TradingApp(symbol=SYMBOL, lot_size=LOT_SIZE, magic_number=MAGIC_NUMBER)\n",
|
||||
"\n",
|
||||
" # 1) Load the classification pipeline\n",
|
||||
" pipeline_path = \"models/saved_models/best_rf_mb_pipeline.pkl\"\n",
|
||||
" app.load_pipeline(pipeline_path)\n",
|
||||
"\n",
|
||||
" while True:\n",
|
||||
" log_and_print(\"Checking market status...\")\n",
|
||||
" if is_market_open():\n",
|
||||
" log_and_print(\"Market is open. Executing trades...\")\n",
|
||||
"\n",
|
||||
" # 2) Generate signals using the loaded pipeline\n",
|
||||
" # This pipeline is classification-based => SHIFTED labels [0,1,2]\n",
|
||||
" # ml_signal_generation() SHIFTs them back to [-1,0,+1] for signals\n",
|
||||
" buy_signal, sell_signal, _, _ = app.ml_signal_generation(\n",
|
||||
" symbol=app.symbol,\n",
|
||||
" n_bars=N_BARS,\n",
|
||||
" timeframe=TIMEFRAME\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # 3) Run strategy\n",
|
||||
" app.run_strategy(app.symbol, app.lot_size, buy_signal, sell_signal)\n",
|
||||
" else:\n",
|
||||
" log_and_print(\"Market is closed. No actions performed.\")\n",
|
||||
"\n",
|
||||
" time.sleep(SLEEP_TIME)\n",
|
||||
"\n",
|
||||
" except KeyboardInterrupt:\n",
|
||||
" log_and_print(\"Shutdown signal received.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" except Exception as e:\n",
|
||||
" error_message = f\"An error occurred: {e}\"\n",
|
||||
" log_and_print(error_message, is_error=True)\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n",
|
||||
" finally:\n",
|
||||
" mt5.shutdown()\n",
|
||||
" log_and_print(\"MetaTrader 5 shutdown completed.\")\n",
|
||||
" # If you need a notification here, handle it (e.g., log, email, etc.).\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "ml",
|
||||
"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.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
Reference in New Issue
Block a user