0% found this document useful (0 votes)
2 views

DS Query

The document is a Python script for a trading bot using MetaTrader 5 and Google Generative AI. It includes configurations for trading parameters, technical indicators, risk management, and connection handling. The script also contains functions for calculating various financial metrics and executing trades based on market analysis.

Uploaded by

dustinincambodia
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

DS Query

The document is a Python script for a trading bot using MetaTrader 5 and Google Generative AI. It includes configurations for trading parameters, technical indicators, risk management, and connection handling. The script also contains functions for calculating various financial metrics and executing trades based on market analysis.

Uploaded by

dustinincambodia
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 28

import MetaTrader5 as mt5

import pandas as pd
import numpy as np
import google.generativeai as genai
import os
import sys
import time
import traceback
from datetime import datetime, timedelta
import re
import inspect

# Configure Gemini with demo API key


genai.configure(api_key="AIzaSyB5NfUCqy3vmgPTSlFFraU37-e_u_Y-KWk")

def debug_print_disabled():
"""Control debug printing for specific functions"""
stack = inspect.stack()[1][3]
return DEBUG_LOG and not any(x in stack for x in ['ATR', 'volume', 'Volume'])

# Suppress unnecessary logs


os.environ['GRPC_VERBOSITY'] = 'NONE'
os.environ['GRPC_TRACE'] = 'none'
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
sys.stderr = open(os.devnull, 'w')

# MT5 Login Details


MT5_ACCOUNT = 243159856
MT5_PASSWORD = "Password1985!!!"
MT5_SERVER = "Exness-MT5Trial14"

# Trade configuration
TRADE_CONFIG = {
'magic': 12345678901, # Unique magic number for Tri-Point Trading System
'comment': 'GeminiBot', # Simplified static comment
'lot': 0.01
}
# Execution parameters
MAX_SLIPPAGE_PIPS = 3
MARKET_DEVIATION = 30

def get_order_expiration(timeframe):
"""Calculate order expiration as (timeframe duration - 30 seconds)"""
timeframe_minutes = {
'M1': 1,
'M5': 5,
'M15': 15,
'M30': 30,
'H1': 60,
'H4': 240
}.get(timeframe, 1)

total_seconds = max(30, (timeframe_minutes * 60) - 30) # Full timeframe minus


30 seconds
minutes = total_seconds // 60
seconds = total_seconds % 60
return minutes, seconds

# Timeframe setup
time_frame_order = ['M1', 'M5', 'M15', 'M30', 'H1', 'H4']
mt5_timeframes = {
'M1': mt5.TIMEFRAME_M1,
'M5': mt5.TIMEFRAME_M5,
'M15': mt5.TIMEFRAME_M15,
'M30': mt5.TIMEFRAME_M30,
'H1': mt5.TIMEFRAME_H1,
'H4': mt5.TIMEFRAME_H4
}

# Market correlations
CORRELATION_SYMBOLS = ['DXY', 'USDJPY', 'EURUSD', 'AUDUSD', 'USDCHF']

# Risk management
RISK_PERCENT = 1.0
MIN_CONFIDENCE = 4
PARTIAL_TP_RATIO = 0.5
MIN_LOT_SIZE = 0.01
MAX_LOT_SIZE = 0.1

# Indicator parameters
MFI_PERIOD = 14
ROC_PERIOD = 14
CMF_PERIOD = 20
VOLUME_MA_PERIOD = 20
ATR_PERIOD = 14
VOLUME_THRESHOLD = 1.5

# Price formatting configuration

def format_price(price):
"""Format price to specified precision"""
return f"{price:.2f}"

# Format templates
SIGNAL_TEMPLATE = """
Signal {num}: [{description}]
└─ Direction: {direction}
└─ 🎯 Entry: {entry:.2f}
└─ ⛔ SL: {sl:.2f}
└─ 🎯 TP: {tp:.2f}
└─ 📌 Confidence: {confidence_stars} ({confidence}/5)
└─ 💡 Rationale: {rationale}
"""

ANALYSIS_TEMPLATE = """
════════════════════════════════════════════════════════════
📊 MARKET ANALYSIS REPORT 📊
════════════════════════════════════════════════════════════
└─ 📈 Symbol: {symbol}
└─ 💵 Current Price: {price:.2f}
└─ ⏰ Time: {timestamp}

{technical}

{fundamental}

{correlation}
════════════════════════════════════════════════════════════
🚀 TRADE SIGNALS 🚀
════════════════════════════════════════════════════════════
{signals}
"""

# Regular expressions
SIGNAL_PATTERN = re.compile(r"""
Signal\s*(\d+)\s*:.*?
Direction\s*:\s*(BUY|SELL)\s*
(?:.*?Entry)[\s*:\-]*(\d+\.?\d*)
.*?SL[\s*:\-]*(\d+\.?\d*)
.*?TP[\s*:\-]*(\d+\.?\d*)
.*?Confidence\s*:\s*(⭐+)
.*?Rationale\s*:\s*(.*?)
(?=Signal|\Z)
""", re.DOTALL | re.VERBOSE)

# System constants
DEBUG_LOG = True
LOG_FILE = "ai_trading_log.txt"
MIN_RISK_REWARD = 2.0
MIN_STOP_DISTANCE_POINTS = 100
ORDER_DEVIATION = 20
PRICE_TOLERANCE = 0.001
MIN_DISTANCE_POINTS = 150 # Further reduced to allow closer stop orders
MIN_CONFIDENCE_TO_TRADE = 4
SWING_LOOKBACK = 100
MIN_TOUCHES = 3
MAX_LEVELS = 5
MAX_OPEN_TRADES = 3
MAX_RETRIES = 3
RETRY_DELAY = 5
API_TIMEOUT = 30

# Add new constants at the top with system constants


CONNECTION_CHECK_DELAY = 2 # Seconds to wait between connection checks
MAX_CONNECTION_RETRIES = 3 # Maximum number of connection retry attempts
SERVER_TIMEOUT = 10 # Seconds to wait for server response
TERMINAL_TIMEOUT = 5000 # Terminal timeout in milliseconds
MIN_ORDER_DELAY = 1 # Minimum delay between order attempts
CONNECTION_STABLE_TIME = 5 # Time to wait after connection before orders

def print_title_bar():
print("\n" + "═"*60)
print("🔱 TRI-POINT GEMINI TRADING BOT 🔱".center(60))
print("═"*60 + "\n")
return "gemini"

def initialize_mt5():
"""Initialize MT5 with proper timeout settings"""
mt5.shutdown() # Ensure clean state
time.sleep(1) # Brief pause before initialization

if not mt5.initialize(timeout=TERMINAL_TIMEOUT):
print("❌ MT5 initialization failed")
return False

print("🔒 Authenticating trading account...")


authorized = mt5.login(
login=MT5_ACCOUNT,
password=MT5_PASSWORD,
server=MT5_SERVER,
timeout=SERVER_TIMEOUT
)
if not authorized:
print(f"❌ Login failed: {mt5.last_error()}")
mt5.shutdown()
return False

print(f"✅ Connected to account #{MT5_ACCOUNT}")


return True

def verify_mt5_connection():
"""Enhanced connection verification"""
try:
if not mt5.terminal_info() or not mt5.terminal_info().connected:
print("📡 MT5 terminal not responding, reconnecting...")
mt5.shutdown()
time.sleep(CONNECTION_CHECK_DELAY)
if initialize_mt5():
time.sleep(CONNECTION_STABLE_TIME) # Wait for connection to
stabilize
return True
return False

# Test connection with a simple request


if not mt5.symbol_info('EURUSD'):
print("📡 MT5 server not responding, reconnecting...")
mt5.shutdown()
time.sleep(CONNECTION_CHECK_DELAY)
if initialize_mt5():
time.sleep(CONNECTION_STABLE_TIME)
return True
return False

return True
except Exception as e:
print(f"Connection verification error: {str(e)}")
return False

def handle_timeout_error(result, attempt):


"""Handle timeout and connection errors"""
if not result or result.retcode == 10027: # Timeout or connection error
print(f"⚠️ Server timeout on attempt {attempt + 1}")
mt5.shutdown()
time.sleep(CONNECTION_CHECK_DELAY * 2) # Double delay for timeout

if verify_mt5_connection():
return True # Connection restored
return False
return True # No timeout error

# Technical indicators
def calculate_mfi(data, period=MFI_PERIOD):
"""Calculate Money Flow Index"""
typical_price = (data['high'] + data['low'] + data['close']) / 3
raw_money_flow = typical_price * data['tick_volume']
positive_flow = np.where(typical_price > typical_price.shift(1),
raw_money_flow, 0)
negative_flow = np.where(typical_price < typical_price.shift(1),
raw_money_flow, 0)

mf_ratio = (pd.Series(positive_flow).rolling(period).sum() /
pd.Series(negative_flow).rolling(period).sum().replace(0, 1e-10))
return 100 - (100 / (1 + mf_ratio))

def calculate_adl(data):
"""Calculate Accumulation/Distribution Line"""
clv = ((2 * data['close'] - data['high'] - data['low']) /
(data['high'] - data['low']).replace(0, 1e-10))
return (clv * data['tick_volume']).cumsum()

def calculate_roc(data, period=ROC_PERIOD):


"""Calculate Rate of Change"""
return ((data['close'] - data['close'].shift(period)) /
data['close'].shift(period)) * 100

def calculate_cmf(data, period=CMF_PERIOD):


"""Calculate Chaikin Money Flow"""
clv = ((2 * data['close'] - data['high'] - data['low']) /
(data['high'] - data['low']).replace(0, 1e-10))
cmf = (clv * data['tick_volume']).rolling(period).sum()
cmf /= data['tick_volume'].rolling(period).sum().replace(0, 1e-10)
return cmf

def calculate_rsi(data, period=14):


delta = data['close'].diff()
gain = delta.where(delta > 0, 0.0)
loss = -delta.where(delta < 0, 0.0)
avg_gain = gain.ewm(alpha=1/period, adjust=False).mean()
avg_loss = loss.ewm(alpha=1/period, adjust=False).mean()
rs = avg_gain / avg_loss.replace(0, 1e-10)
return 100 - (100 / (1 + rs))

def calculate_macd(data, fast=12, slow=26, signal=9):


exp1 = data['close'].ewm(span=fast, adjust=False).mean()
exp2 = data['close'].ewm(span=slow, adjust=False).mean()
macd = exp1 - exp2
signal_line = macd.ewm(span=signal, adjust=False).mean()
return macd, signal_line

def calculate_stochastic(data, k_period=14, d_period=3):


low_min = data['low'].rolling(k_period).min()
high_max = data['high'].rolling(k_period).max()
k = 100 * ((data['close'] - low_min) / (high_max - low_min))
d = k.rolling(d_period).mean()
return k, d

def calculate_adx(data, period=14):


high = data['high']
low = data['low']
close = data['close']

plus_dm = high.diff()
minus_dm = low.diff()
plus_dm[plus_dm < 0] = 0
minus_dm[minus_dm > 0] = 0

tr1 = pd.DataFrame(high - low)


tr2 = pd.DataFrame(abs(high - close.shift(1)))
tr3 = pd.DataFrame(abs(low - close.shift(1)))
frames = [tr1, tr2, tr3]
tr = pd.concat(frames, axis=1, join='inner').max(axis=1)
atr = tr.rolling(period).mean()

plus_di = 100 * (plus_dm.rolling(period).mean() / atr)


minus_di = abs(100 * (minus_dm.rolling(period).mean() / atr))
dx = (abs(plus_di - minus_di) / abs(plus_di + minus_di)) * 100
adx = dx.rolling(period).mean()
return adx, plus_di, minus_di

def calculate_atr(data, period=ATR_PERIOD):


high = data['high']
low = data['low']
close = data['close'].shift(1)

tr1 = high - low


tr2 = abs(high - close)
tr3 = abs(low - close)

tr = pd.DataFrame({'TR1': tr1, 'TR2': tr2, 'TR3': tr3}).max(axis=1)


return tr.rolling(window=period).mean()

def analyze_volume(data):
vol_ma = data['tick_volume'].rolling(window=VOLUME_MA_PERIOD).mean()
volume_spikes = data['tick_volume'] > (vol_ma * VOLUME_THRESHOLD)
recent_vol_trend = 'increasing' if
data['tick_volume'].tail(5).is_monotonic_increasing else \
'decreasing' if
data['tick_volume'].tail(5).is_monotonic_decreasing else \
'neutral'
price_changes = data['close'].diff()
vol_price_corr = data['tick_volume'].corr(abs(price_changes))

return {
'volume_ma': vol_ma,
'volume_spikes': volume_spikes,
'vol_trend': recent_vol_trend,
'vol_price_correlation': vol_price_corr
}

def calculate_cci(data, period=20):


tp = (data['high'] + data['low'] + data['close']) / 3
sma_tp = tp.rolling(window=period).mean()
mad = tp.rolling(window=period).apply(lambda x: abs(x - x.mean()).mean())
mad = mad.replace(0, 1e-10)
return (tp - sma_tp) / (0.015 * mad)

def calculate_obv(data):
obv = pd.Series(index=data.index, dtype=float)
obv.iloc[0] = 0
for i in range(1, len(data)):
if data['close'].iloc[i] > data['close'].iloc[i-1]:
obv.iloc[i] = obv.iloc[i-1] + data['tick_volume'].iloc[i]
elif data['close'].iloc[i] < data['close'].iloc[i-1]:
obv.iloc[i] = obv.iloc[i-1] - data['tick_volume'].iloc[i]
else:
obv.iloc[i] = obv.iloc[i-1]
return obv

def calculate_williams_r(data, period=14):


highest_high = data['high'].rolling(window=period).max()
lowest_low = data['low'].rolling(window=period).min()
return -100 * ((highest_high - data['close']) / (highest_high - lowest_low))

def get_ma_bias(symbol, timeframe):


"""Determine moving average bias (BULLISH/BEARISH) for given symbol and
timeframe"""
try:
data = fetch_intraday_data(symbol, num_candles=50)
if data is None or timeframe not in data:
return None

timeframe_data = data[timeframe]
if timeframe_data is None or len(timeframe_data) < 20:
return None

# Calculate short and long MAs


ma_short = timeframe_data['close'].rolling(window=8).mean()
ma_long = timeframe_data['close'].rolling(window=20).mean()

# Get latest values


price = timeframe_data['close'].iloc[-1]
ma_short_val = ma_short.iloc[-1]
ma_long_val = ma_long.iloc[-1]

# Validate values
if pd.isna(price) or pd.isna(ma_short_val) or pd.isna(ma_long_val):
return None

# Determine bias
if price > ma_short_val > ma_long_val:
return "BULLISH"
elif price < ma_short_val < ma_long_val:
return "BEARISH"
return None

except Exception as e:
print(f"Error calculating MA bias: {str(e)}")
return None

def fetch_intraday_data(symbol, num_candles=200):


all_data = {}
for tf in time_frame_order:
mt5_tf = mt5_timeframes[tf]
rates = mt5.copy_rates_from_pos(symbol, mt5_tf, 0, num_candles)
if rates is None or rates.size == 0:
continue

df = pd.DataFrame(rates)
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
# Moving averages
for ma_period in [8, 20, 50, 100, 200]:
df[f'MA{ma_period}'] = df['close'].rolling(ma_period).mean()

# Core indicators
df['RSI'] = calculate_rsi(df)
df['MACD'], df['MACD_Signal'] = calculate_macd(df)
df['Stoch_K'], df['Stoch_D'] = calculate_stochastic(df)
df['ADX'], df['Plus_DI'], df['Minus_DI'] = calculate_adx(df)
df['ATR'] = calculate_atr(df)
df['CCI'] = calculate_cci(df)
df['OBV'] = calculate_obv(df)
df['Williams_R'] = calculate_williams_r(df)

# New indicators
df['MFI'] = calculate_mfi(df)
df['ADL'] = calculate_adl(df)
df['ROC'] = calculate_roc(df)
df['CMF'] = calculate_cmf(df)

# Volume analysis
volume_metrics = analyze_volume(df)
df['Volume_MA'] = volume_metrics['volume_ma']
df['Volume_Spikes'] = volume_metrics['volume_spikes']

all_data[tf] = df
return all_data

def get_current_price(symbol):
try:
tick = mt5.symbol_info_tick(symbol)
return {'bid': tick.bid, 'ask': tick.ask} if tick else None
except Exception as e:
print(f"Price check error: {str(e)}")
return None

def check_existing_trade(trade_type):
positions = mt5.positions_get()
orders = mt5.orders_get()
all_trades = []
if positions: all_trades.extend(positions)
if orders: all_trades.extend(orders)

for trade in all_trades:


if hasattr(trade, 'magic') and trade.magic == TRADE_CONFIG['magic']:
return True
return False

def manage_existing_order(symbol, trade_type, new_entry, new_sl, new_tp):


orders = mt5.orders_get(symbol=symbol)
if not orders:
return False

for order in orders:


if order.magic == TRADE_CONFIG['magic']:
price_diff = abs(order.price_open - new_entry)
sl_diff = abs(order.sl - new_sl)
tp_diff = abs(order.tp - new_tp)
threshold = new_entry * 0.005
if any([diff > threshold for diff in [price_diff, sl_diff, tp_diff]]):
print(f"Updating {trade_type} order parameters")
request = {
"action": mt5.TRADE_ACTION_MODIFY,
"position": order.ticket,
"price": new_entry,
"sl": new_sl,
"tp": new_tp,
}
result = mt5.order_send(request)
if result.retcode == mt5.TRADE_RETCODE_DONE:
print("Order successfully updated")
return True
else:
print(f"Update failed: {mt5.last_error()}")
return False
else:
print("Existing order parameters still valid")
return True
return False

def execute_market_order(symbol, trade_type, direction, entry, sl, tp, price_info,


volume):
if not mt5.initialize():
print("Reconnecting to MT5...")
if not initialize_mt5(): # Explicitly check if initialization is successful
print("MT5 initialization failed, cannot place pending order.")
return False

order_type = mt5.ORDER_TYPE_BUY if direction == 'buy' else mt5.ORDER_TYPE_SELL


symbol_info = mt5.symbol_info(symbol)
if not symbol_info:
print(f"Failed to get symbol info for {symbol}")
return False

point = symbol_info.point
min_stop_distance = 1500 * point

if abs(entry - sl) < min_stop_distance:


print(f"Stop loss too close (min {min_stop_distance:.5f})")
return False

# Initialize signal parameters with default values


signal_params = {
'confidence': 0.0 # Default confidence value
}

# Format the comment with proper error handling


try:
comment =
TRADE_CONFIG['comment'].format(confidence=signal_params['confidence'])
except KeyError:
comment = TRADE_CONFIG['comment'].format(confidence=0.0) # Fallback value

request = {
"action": mt5.TRADE_ACTION_DEAL,
"symbol": symbol,
"volume": volume,
"type": order_type,
"price": price_info['ask'] if direction == 'buy' else price_info['bid'],
"sl": sl,
"tp": tp,
"deviation": 20,
"magic": TRADE_CONFIG['magic'],
"comment": comment,
"type_time": mt5.ORDER_TIME_GTC,
"type_filling": mt5.ORDER_FILLING_FOK,
}

result = mt5.order_send(request)
if not result:
error_message = mt5.last_error() # Capture last error
print(f"No response from MT5. MT5 Error: {error_message}") # Log detailed
error
return False

if result.retcode != mt5.TRADE_RETCODE_DONE:
print(f"Market order failed: {mt5.last_error()}")
return False

print(f"\n✅ MARKET {direction.upper()} EXECUTED")


print(f"Type: {trade_type.capitalize()}")
print(f"Entry: {result.price:.5f}")
print(f"SL: {sl:.5f}")
print(f"TP: {tp:.5f}")
return True

def execute_pending_order(symbol, trade_type, direction, entry, sl, tp,


current_price, volume, signal_params):
if not mt5.initialize():
print("Reconnecting to MT5...")
initialize_mt5()

expired_orders = [o for o in mt5.orders_get() if o.time_expiration <


datetime.now().timestamp()]
for order in expired_orders:
mt5.order_send({"action": mt5.TRADE_ACTION_REMOVE, "order": order.ticket})

order_type = mt5.ORDER_TYPE_BUY_STOP if direction == 'buy' else


mt5.ORDER_TYPE_SELL_STOP
timeframe = 'M5' # Default timeframe for order expiration
expiry_minutes, expiry_seconds = get_order_expiration(timeframe)
expiration = datetime.now() + timedelta(minutes=expiry_minutes,
seconds=expiry_seconds)

# Initialize signal parameters with default values


signal_params = {
'confidence': 0.0 # Default confidence value
}

# Format the comment with proper error handling


try:
comment =
TRADE_CONFIG['comment'].format(confidence=signal_params['confidence'])
except KeyError:
comment = TRADE_CONFIG['comment'].format(confidence=0.0) # Fallback value
request = {
"action": mt5.TRADE_ACTION_PENDING,
"symbol": symbol,
"volume": volume,
"type": order_type,
"price": entry,
"sl": sl,
"tp": tp,
"deviation": 50,
"magic": TRADE_CONFIG['magic'],
"comment": comment,
"type_time": mt5.ORDER_TIME_SPECIFIED,
"type_filling": mt5.ORDER_FILLING_RETURN,
"expiration": expiration.timestamp()
}

for attempt in range(MAX_RETRIES): # Retry loop for pending orders


log_debug(f"Pending order request (Attempt {attempt + 1})", request)
result = mt5.order_send(request)
if result and result.retcode == mt5.TRADE_RETCODE_DONE:
print(f"""
✅ Pending Order Successfully Placed (Attempt {attempt + 1})
Type: Pending
Direction: {direction.upper()}
Entry: {entry:.5f}
Stop Loss: {sl:.5f}
Take Profit: {tp:.5f}
Volume: {volume:.2f}
""")
return True
else:
error_message = mt5.last_error() if result else "No response from MT5"
print(f"❌ Pending order failed (Attempt {attempt + 1}/{MAX_RETRIES}):
{error_message}")
if attempt < MAX_RETRIES - 1:
print(f"⏳ Retrying pending order in {RETRY_DELAY} seconds...")
time.sleep(RETRY_DELAY)
else:
print(f"❌ Max retries reached for pending order on {symbol}")
return False
if not mt5.initialize(): # Re-initialize MT5 on retry
print("Failed to re-initialize MT5 for retry.")
return False
print("MT5 re-initialized for retry.")

return False # Max retries reached

print(f"\n⏳ PENDING {direction.upper()} ORDER PLACED")


print(f"Type: {trade_type.capitalize()}")
print(f"Trigger Price: {entry:.5f}")
print(f"Current Price: {current_price:.5f}")
print(f"SL: {sl:.5f}")
print(f"TP: {tp:.5f}")
print(f"Expires: {expiration.strftime('%Y-%m-%d %H:%M')}")

if result: # Check if result is not None before accessing its attributes


if result.retcode == mt5.TRADE_RETCODE_DONE:
return True
else:
error_message = mt5.last_error()
print(f"Detailed MT5 OrderSend Error: {result}") # Print full result
for debugging
print(f"MT5 Error Code: {result.retcode}")
print(f"MT5 Error Description: {error_message}")
return False
else:
print("MT5 order_send() returned None. No result object to inspect.")
return False

def check_existing_positions(symbol):
positions = mt5.positions_get(symbol=symbol)
return positions if positions is not None else []

def is_signal_matching_position(position, signal):


pos_type = 'buy' if position.type == mt5.POSITION_TYPE_BUY else 'sell'
if pos_type != signal['direction']:
return False

tolerance = float(signal['entry']) * 0.005


entry_matches = abs(position.price_open - float(signal['entry'])) <= tolerance
sl_matches = abs(position.sl - float(signal['sl'])) <= tolerance
tp_matches = abs(position.tp - float(signal['tp'])) <= tolerance

return entry_matches and sl_matches and tp_matches

def calculate_position_size(symbol, entry_price, stop_loss,


risk_percent=RISK_PERCENT):
try:
account_info = mt5.account_info()
if not account_info:
return MIN_LOT_SIZE

symbol_info = mt5.symbol_info(symbol)
if not symbol_info:
return MIN_LOT_SIZE

risk_amount = account_info.balance * (risk_percent / 100)


sl_points = abs(entry_price - stop_loss) / symbol_info.point
tick_value = symbol_info.trade_tick_value

position_size = (risk_amount / (sl_points * tick_value))


lot_step = symbol_info.volume_step
position_size = round(position_size / lot_step) * lot_step

# Apply min/max constraints


position_size = max(MIN_LOT_SIZE, min(position_size, MAX_LOT_SIZE))
position_size = min(max(position_size, symbol_info.volume_min),
symbol_info.volume_max)

return position_size

except Exception as e:
print(f"Error calculating position size: {str(e)}")
return 0.01

def evaluate_signal_quality(signal_params):
try:
confidence = int(signal_params['confidence'])
if confidence < MIN_CONFIDENCE_TO_TRADE:
return False, f"Confidence ({confidence}) below minimum required
({MIN_CONFIDENCE_TO_TRADE})"
return True, "Signal meets confidence criteria"
except Exception as e:
return False, f"Error evaluating signal: {str(e)}"

def validate_trade_parameters(symbol, direction, entry, sl, tp):


try:
symbol_info = mt5.symbol_info(symbol)
if not symbol_info:
return False, "Invalid symbol"

point = symbol_info.point
sl_distance = abs(entry - sl) / point

if sl_distance < MIN_STOP_DISTANCE_POINTS:


return False, f"Stop loss too close (min {MIN_STOP_DISTANCE_POINTS}
points)"

return True, "Parameters validated"


except Exception as e:
return False, f"Validation error: {str(e)}"

def validate_order_price(symbol, order_type, price):


try:
symbol_info = mt5.symbol_info(symbol)
if not symbol_info:
return False, "Symbol info not available"

current_price = get_current_price(symbol)
if not current_price:
return False, "Current price not available"

ask = current_price['ask']
bid = current_price['bid']
point = symbol_info.point

# Get volatility data


data = fetch_intraday_data(symbol, num_candles=50)['M5']
atr = data['ATR'].iloc[-1]

# Dynamic spread multiplier based on volatility


spread = (ask - bid) / point
atr_points = atr / point
# Spread multiplier between 1.0-2.0 with higher ATR contribution
spread_multiplier = 1.0 + min(1.0, atr_points / 5000)

# Dynamic slippage buffer based on volatility (1-5 pips)


slippage_buffer = max(1.0, min(5.0, atr_points / 2000))

# Calculate minimum distance with dynamic adjustments


min_distance = (MIN_DISTANCE_POINTS * spread_multiplier) + slippage_buffer

if order_type == mt5.ORDER_TYPE_BUY_STOP:
min_price = ask + (min_distance * point)
if price <= min_price:
# Suggest price with additional buffer
suggested_price = min_price + (200 * point) # Increased buffer to
200 points
return False, (f"Buy stop price ({price}) must be above
{min_price:.5f} (ask + {min_distance:.0f} points). "
f"Spread: {spread:.0f} points, ATR: {atr/point:.0f}
points. "
f"Suggested price: {suggested_price:.5f}")

elif order_type == mt5.ORDER_TYPE_SELL_STOP:


max_price = bid - (min_distance * point)
if price >= max_price:
# Suggest price with additional buffer
suggested_price = max_price - (100 * point) # 100 point buffer
return False, (f"Sell stop price ({price}) must be below
{max_price:.5f} (bid - {min_distance:.0f} points). "
f"Spread: {spread:.0f} points, ATR: {atr/point:.0f}
points. "
f"Suggested price: {suggested_price:.5f}")

return True, "Price validated"

except Exception as e:
return False, f"Price validation error: {str(e)}"

def detect_support_resistance(df, lookback=SWING_LOOKBACK,


min_touches=MIN_TOUCHES):
try:
recent_data = df.tail(lookback)
highs = []
lows = []

for i in range(2, len(recent_data) - 2):


if (recent_data['high'].iloc[i] > recent_data['high'].iloc[i-1] and
recent_data['high'].iloc[i] > recent_data['high'].iloc[i-2] and
recent_data['high'].iloc[i] > recent_data['high'].iloc[i+1] and
recent_data['high'].iloc[i] > recent_data['high'].iloc[i+2]):
highs.append(recent_data['high'].iloc[i])

if (recent_data['low'].iloc[i] < recent_data['low'].iloc[i-1] and


recent_data['low'].iloc[i] < recent_data['low'].iloc[i-2] and
recent_data['low'].iloc[i] < recent_data['low'].iloc[i+1] and
recent_data['low'].iloc[i] < recent_data['low'].iloc[i+2]):
lows.append(recent_data['low'].iloc[i])

def group_levels(levels):
grouped = []
for price in levels:
found_group = False
for group in grouped:
if abs(price - sum(group)/len(group)) <= price *
PRICE_TOLERANCE:
group.append(price)
found_group = True
break
if not found_group:
grouped.append([price])
return [sum(group)/len(group) for group in grouped if len(group) >=
min_touches]

resistance_levels = sorted(group_levels(highs), reverse=True)[:MAX_LEVELS]


support_levels = sorted(group_levels(lows))[:MAX_LEVELS]

return {'support': support_levels, 'resistance': resistance_levels}

except Exception as e:
log_debug("Error detecting S/R levels", str(e))
return {'support': [], 'resistance': []}

def find_optimal_sl_tp(direction, entry_price, sr_levels, risk_reward_min=2.0):


try:
supports = sr_levels['support']
resistances = sr_levels['resistance']

if direction.lower() == 'buy':
valid_sl = [s for s in supports if s < entry_price]
valid_tp = [r for r in resistances if r > entry_price]

if not valid_sl or not valid_tp:


return None, None

sl = max(valid_sl)
min_tp_distance = (entry_price - sl) * risk_reward_min
valid_tp = [tp for tp in valid_tp if tp - entry_price >=
min_tp_distance]

if not valid_tp:
return None, None

tp = min(valid_tp)

else:
valid_sl = [r for r in resistances if r > entry_price]
valid_tp = [s for s in supports if s < entry_price]

if not valid_sl or not valid_tp:


return None, None

sl = min(valid_sl)
min_tp_distance = (sl - entry_price) * risk_reward_min
valid_tp = [tp for tp in valid_tp if entry_price - tp >=
min_tp_distance]

if not valid_tp:
return None, None

tp = max(valid_tp)

return sl, tp

except Exception as e:
log_debug("Error finding optimal SL/TP", str(e))
return None, None

def validate_signal_with_sr(signal_params, sr_levels):


try:
direction = signal_params['direction']
entry = float(signal_params['entry'])
sl = float(signal_params['sl'])
tp = float(signal_params['tp'])
for level in sr_levels['support'] + sr_levels['resistance']:
if abs(entry - level) <= entry * PRICE_TOLERANCE:
return False, "Entry too close to S/R level"

optimal_sl, optimal_tp = find_optimal_sl_tp(direction, entry, sr_levels)

if not optimal_sl or not optimal_tp:


return False, "Could not find valid S/R levels for SL/TP"

sl_valid = abs(sl - optimal_sl) <= optimal_sl * PRICE_TOLERANCE


tp_valid = abs(tp - optimal_tp) <= optimal_tp * PRICE_TOLERANCE

if not sl_valid:
return False, "Stop loss not aligned with S/R levels"
if not tp_valid:
return False, "Take profit not aligned with S/R levels"

return True, "Signal validated against S/R levels"

except Exception as e:
return False, f"Error validating against S/R: {str(e)}"

def analyze_market_structure(symbol, timeframes=['H4', 'H1', 'M15']):


try:
structure = {}
for tf in timeframes:
if tf not in time_frame_order:
continue

df = fetch_intraday_data(symbol, num_candles=SWING_LOOKBACK)[tf]
sr_levels = detect_support_resistance(df)

structure[tf] = {
'sr_levels': sr_levels,
'trend': 'bullish' if df['close'].iloc[-1] > df['MA20'].iloc[-1]
else 'bearish',
'volatility': df['high'].std() / df['close'].mean()
}

return structure

except Exception as e:
log_debug("Error analyzing market structure", str(e))
return None

def count_total_trades(symbol=None):
try:
positions = mt5.positions_get(symbol=symbol)
orders = mt5.orders_get(symbol=symbol)

total_trades = 0

if positions:
total_trades += sum(1 for pos in positions if pos.magic ==
TRADE_CONFIG['magic'])

if orders:
total_trades += sum(1 for order in orders if order.magic ==
TRADE_CONFIG['magic'])

return total_trades
except Exception as e:
print(f"Error counting trades: {str(e)}")
return 0

def parse_suggested_price(error_message):
"""Extract suggested price from error message"""
pattern = r"Suggested price: ([\d\.]+)"
match = re.search(pattern, error_message)
if match:
return float(match.group(1))
return None

def execute_trade(symbol, signal_params, retry_count=0):


try:
current_trades = count_total_trades(symbol)
if current_trades >= MAX_OPEN_TRADES:
if not hasattr(execute_trade, 'max_trades_warned'):
print(f"❌ Maximum number of trades ({MAX_OPEN_TRADES}) already
reached")
execute_trade.max_trades_warned = True
return False
execute_trade.max_trades_warned = False

direction = signal_params['direction']
entry_price = float(signal_params['entry'])
sl_price = float(signal_params['sl'])
tp_price = float(signal_params['tp'])

ma_bias = get_ma_bias(symbol, mt5.TIMEFRAME_M5)


if ma_bias:
if direction.lower() == 'buy' and ma_bias != "BULLISH":
print(f"❌ Ignoring BUY signal - MA bias is {ma_bias}")
return False
if direction.lower() == 'sell' and ma_bias != "BEARISH":
print(f"❌ Ignoring SELL signal - MA bias is {ma_bias}")
return False

symbol_info = mt5.symbol_info(symbol)
price_info = get_current_price(symbol)
if not symbol_info or not price_info:
print("❌ Failed to get market info")
return False

is_valid, message = validate_trade_parameters(symbol, direction,


entry_price, sl_price, tp_price)
if not is_valid:
print(f"❌ Trade validation failed: {message}")
return False

volume = calculate_position_size(symbol, entry_price, sl_price)

is_buy = direction.lower() == 'buy'


current_price = price_info['ask'] if is_buy else price_info['bid']
price_diff = abs(entry_price - current_price)

for attempt in range(MAX_RETRIES):


# Enhanced connection verification
if not verify_mt5_connection():
print("❌ Failed to establish stable MT5 connection")
time.sleep(RETRY_DELAY)
continue

# Allow connection to stabilize


time.sleep(MIN_ORDER_DELAY)

# Market or pending order request setup


request = {
"action": mt5.TRADE_ACTION_DEAL if price_diff <= MAX_SLIPPAGE_PIPS
* symbol_info.point else mt5.TRADE_ACTION_PENDING,
"symbol": symbol,
"volume": volume,
"type": mt5.ORDER_TYPE_BUY if is_buy else mt5.ORDER_TYPE_SELL,
"price": current_price if price_diff <= MAX_SLIPPAGE_PIPS *
symbol_info.point else entry_price,
"sl": sl_price,
"tp": tp_price,
"deviation": ORDER_DEVIATION,
"magic": TRADE_CONFIG['magic'],
"comment": "GeminiBot",
"type_filling": mt5.ORDER_FILLING_FOK,
}

# Add pending order specific parameters


if request["action"] == mt5.TRADE_ACTION_PENDING:
request.update({
"type": mt5.ORDER_TYPE_BUY_STOP if is_buy else
mt5.ORDER_TYPE_SELL_STOP,
"type_time": mt5.ORDER_TIME_SPECIFIED,
"expiration": int((datetime.now() + timedelta(minutes=14,
seconds=30)).timestamp())
})

# Verify connection again before sending order


if not mt5.terminal_info().connected:
print("❌ Connection lost before order send")
continue

result = mt5.order_send(request)

if result and result.retcode == mt5.TRADE_RETCODE_DONE:


print(f"""
✅ {request['action']} Order Successfully Placed
Direction: {direction.upper()}
Entry: {result.price:.5f}
SL: {sl_price:.5f}
TP: {tp_price:.5f}
Volume: {volume:.2f}
""")
return True

error_code = result.retcode if result else "Unknown"


print(f"❌ Order failed (Attempt {attempt + 1}): Code {error_code}")

if error_code == 10027: # Server timeout


print("⚠️ Server timeout - Waiting for stable connection...")
time.sleep(CONNECTION_STABLE_TIME) # Wait longer for stability
if not verify_mt5_connection():
continue

elif error_code in [10004, 10021]: # Invalid price


suggested_price = adjust_price_for_market(symbol, direction,
entry_price)
if suggested_price:
entry_price = suggested_price
print(f"📊 Adjusted price to {entry_price:.5f}")
continue

if attempt < MAX_RETRIES - 1:


delay = RETRY_DELAY * (attempt + 1) # Progressive delay
print(f"⏳ Retrying in {delay} seconds...")
time.sleep(delay)

return False

except Exception as e:
print(f"❌ Trade execution error: {str(e)}")
return False

def adjust_price_for_market(symbol, direction, price):


"""Adjust price based on current market conditions"""
try:
current_price = get_current_price(symbol)
if not current_price:
return price

symbol_info = mt5.symbol_info(symbol)
if not symbol_info:
return price

spread = current_price['ask'] - current_price['bid']


point = symbol_info.point

if direction.lower() == 'buy':
return current_price['ask'] + (spread * 2)
else:
return current_price['bid'] - (spread * 2)

except Exception as e:
print(f"Price adjustment error: {str(e)}")
return price

def generate_analysis_prompt(symbol, all_data, current_price,


correlation_data=None):
prompt = f"""📊 {symbol} REAL-TIME ANALYSIS REQUEST
{'='*60}
🎯 CURRENT PRICE: {current_price:.5f}
⏰ TIME: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}
{'='*60}

Historical Data Analysis (Last 200 candles):


{'-'*60}"""

# Format technical factors


technical_factors = []
for tf in time_frame_order:
if tf in all_data:
df = all_data[tf]
last_row = df.iloc[-1]

# Format price levels without trailing zeros


support = f"{last_row['MA20']:.2f}".rstrip('0').rstrip('.')
resistance = f"{last_row['MA50']:.2f}".rstrip('0').rstrip('.')

# Format timeframe line


trend = 'bullish' if last_row['close'] > last_row['MA20'] else
'bearish'
line = f"{tf} timeframe: {symbol} is in a {trend} trend, with support
at {support} and resistance at {resistance}"
technical_factors.append(line)

# Join technical factors with newlines


technical_factors_str = "\n".join(technical_factors)

for tf in time_frame_order:
if tf in all_data:
df = all_data[tf]
last_row = df.iloc[-1]
vol_analysis = analyze_volume(df)

prompt += f"\n{tf} Timeframe Analysis:\n"


prompt += f"Last Close: {last_row['close']:.5f} (Current:
{current_price:.5f})\n"
prompt += f"MA8/20/50:
{last_row['MA8']:.5f}/{last_row['MA20']:.5f}/{last_row['MA50']:.5f}\n"
prompt += f"RSI: {last_row['RSI']:.2f} | ADX: {last_row['ADX']:.2f}\n"
prompt += f"MACD: {last_row['MACD']:.5f} | Signal:
{last_row['MACD_Signal']:.5f}\n"
prompt += f"Stoch K/D:
{last_row['Stoch_K']:.2f}/{last_row['Stoch_D']:.2f}\n"
prompt += f"ATR: {last_row['ATR']:.5f}\n"
prompt += f"Volume Analysis:\n"
prompt += f" - Trend: {vol_analysis['vol_trend']}\n"
prompt += f" - Price-Volume Correlation:
{vol_analysis['vol_price_correlation']:.2f}\n"
prompt += f" - Volume vs Average:
{(last_row['tick_volume']/last_row['Volume_MA']):.1f}x\n"
prompt += f"New Indicators:\n"
prompt += f"MFI: {last_row['MFI']:.1f} | CMF: {last_row['CMF']:.2f}\n"
prompt += f"ROC({ROC_PERIOD}): {last_row['ROC']:.1f}% | ADL:
{last_row['ADL']:.0f}\n"

if correlation_data:
prompt += "\n📊 CORRELATION ANALYSIS:\n"
for corr_symbol, data in correlation_data.items():
if 'H1' in data:
last_row = data['H1'].iloc[-1]
prompt += f"\n{corr_symbol} Analysis (H1):\n"
prompt += f"Current: {last_row['close']:.5f}\n"
prompt += f"MA20: {last_row['MA20']:.5f}\n"
prompt += f"RSI: {last_row['RSI']:.2f}\n"
prompt += f"Trend: {'Bullish' if last_row['close'] >
last_row['MA20'] else 'Bearish'}\n"
analysis_template = f"""
Based on the main symbol and correlation analysis above, provide exactly 3 high-
probability trade setups in order of confidence level:

🌐 Market Overview:
🔭 Technical: [Key technical factors across timeframes]
📅 Fundamental: [Current or upcoming market events]
🏛 Institutional: [Major price levels and order blocks]
📊 Correlations: [How DXY, USDJPY, EURUSD are affecting {symbol}]

🚀 High-Probability Trade Signals:

Signal 1: [MOST CONFIDENT SETUP]


Direction: [BUY/SELL]
🎯 Entry: [PRICE] | ⛔ SL: [PRICE] | 🎯 TP: [PRICE]
📌 Confidence: [⭐⭐⭐⭐⭐ (1-5)]
💡 Rationale: [Key technical, fundamental, and institutional factors]

Signal 2: [SECOND BEST SETUP]


Direction: [BUY/SELL]
🎯 Entry: [PRICE] | ⛔ SL: [PRICE] | 🎯 TP: [PRICE]
📌 Confidence: [⭐⭐⭐⭐⭐ (1-5)]
💡 Rationale: [Key technical, fundamental, and institutional factors]

Signal 3: [THIRD BEST SETUP]


Direction: [BUY/SELL]
🎯 Entry: [PRICE] | ⛔ SL: [PRICE] | 🎯 TP: [PRICE]
📌 Confidence: [⭐⭐⭐⭐⭐ (1-5)]
💡 Rationale: [Key technical, fundamental, and institutional factors]

REQUIREMENTS:
- Entry prices must be logical relative to current price {current_price:.5f}
- Each signal must have clear technical and fundamental justification
- Minimum 1:2 risk-reward ratio required
- Only provide high-probability setups (minimum 3-star confidence)
- Prioritize quality over timeframe - focus on best available setups
"""

strict_format_template = """
STRICT SIGNAL FORMAT REQUIREMENTS:
================================
1. Use exactly this structure for each signal:

Signal N:
Direction: BUY or SELL
Entry: PRICE
SL: PRICE
TP: PRICE
Confidence: ⭐⭐⭐⭐⭐
Rationale: Your analysis here

2. Rules:
- No emojis in signal parameters
- Only numerical values for prices
- Exact labels as shown above
- One line per parameter
- Clear separation between signals

Example Signal:
Signal 1:
Direction: BUY
Entry: 2015.50
SL: 2010.25
TP: 2025.75
Confidence: ⭐⭐⭐⭐
Rationale: Strong support at entry with bullish RSI divergence
"""

return prompt + strict_format_template + analysis_template

def format_section_header(text, width=60):


centered_text = text.center(width-2)
return f"""
╔{'═'*(width-2)}╗
║{centered_text}║
╚{'═'*(width-2)}╝"""

def print_analysis_results(symbol, current_price, response_text):


try:
signals = parse_ai_response(response_text)

# Format technical analysis


technical = ""
if '🔭 Technical:' in response_text:
technical = response_text.split('🔭 Technical:')[1].split('📅
Fundamental:')[0].strip()

# Format fundamental analysis


fundamental = ""
if '📅 Fundamental:' in response_text:
fundamental = response_text.split('📅 Fundamental:')[1].split('📊
Correlations:')[0].strip()

# Format correlation analysis


correlation = ""
if '📊 Correlations:' in response_text:
correlation = response_text.split('📊 Correlations:')[1].split('🚀 High-
Probability Trade Signals:')[0].strip()

# Format signals with status


formatted_signals = ""
for i, (signal_num, signal_data) in enumerate(signals.items()):
# Add status to signal template
status = signal_data.get('status', '')
if status:
status = f"\nStatus: {status}"

formatted_signals += SIGNAL_TEMPLATE.format(
num=i+1,
direction=signal_data['direction'].upper(),
entry=signal_data['entry'],
sl=signal_data['sl'],
tp=signal_data['tp'],
confidence_stars=signal_data['confidence_stars'],
rationale=signal_data['rationale'] + status
)

# Print formatted analysis once


print(ANALYSIS_TEMPLATE.format(
symbol=symbol,
price=current_price,
timestamp=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
technical=technical,
fundamental=fundamental,
correlation=correlation,
signals=formatted_signals
))

except Exception as e:
print(f"Error formatting analysis: {str(e)}")
print(response_text)

def log_debug(message, data=None):


if not DEBUG_LOG:
return

emoji_map = {
'Sending prompt to AI': '🤖',
'AI Response received': '📝',
'Original AI Response': '📄',
'Parsed Signal': '🎯',
'Signal parsing error': '⚠️',
'AI API Error': '🔌',
'Order request': '📝',
}

for key, emoji in emoji_map.items():


if key in message:
message = f"{emoji} {message}"
break
else:
message = f"ℹ️ {message}"

important_messages = ['Sending prompt', 'AI Response', 'Parsed Signal']


should_print = any(msg in message for msg in important_messages)

if should_print:
if data and 'Order request' not in message:
print(f"\n{message}")
elif 'Parsed Signal' in message:
print(f"\n{message}: {data['direction'].upper()} @ {data['entry']}")
else:
print(f"\n{message}")

log_message = f"{message}\n{data if data else ''}"


with open(LOG_FILE, 'a', encoding='utf-8') as f:
f.write(f"{log_message}\n{'═'*60}\n")

def parse_ai_response(response_text):
signals = {}
seen_signals = set() # Track seen signals to prevent duplicates

try:
# Skip logging original AI response
cleaned_text = (response_text.replace('*', '')
.replace('[', '')
.replace(']', '')
.replace('\r', '\n')
.replace('→', '')
.replace('•', '')
.strip())
cleaned_text = re.sub(r'\s+', ' ', cleaned_text)

if "Signal 1:" not in cleaned_text:


return signals

matches = re.finditer(SIGNAL_PATTERN, cleaned_text)

for match in matches:


try:
# Create signal hash to detect duplicates
signal_hash = hash((
match.group(2).lower(), # direction
round(float(match.group(3)), 2), # entry
round(float(match.group(4)), 2), # sl
round(float(match.group(5)), 2) # tp
))

if signal_hash in seen_signals:
continue

seen_signals.add(signal_hash)

signal_data = {
'direction': match.group(2).lower(),
'entry': float(match.group(3)),
'sl': float(match.group(4)),
'tp': float(match.group(5)),
'confidence_stars': match.group(6),
'confidence': len(match.group(6)),
'rationale': match.group(7).strip()
}

signal_num = match.group(1)
signals[f'signal{signal_num}'] = signal_data

except Exception as e:
continue

except Exception as e:
pass

return signals

def format_ai_response(response_text, symbol, current_price, timeframe):


try:
signals = parse_ai_response(response_text)

# Format sections with consistent spacing


def format_section(content, title):
if not content:
return ""
return f"\n{title}\n{'─'*60}\n{content.strip()}\n"

technical = format_section(
response_text.split('🔭 Technical:')[1].split('📅 Fundamental:')
[0].strip()
if '🔭 Technical:' in response_text else "",
"📊 TECHNICAL ANALYSIS"
)

fundamental = format_section(
response_text.split('📅 Fundamental:')[1].split('📊 Correlations:')
[0].strip()
if '📅 Fundamental:' in response_text else "",
"🌍 FUNDAMENTAL ANALYSIS"
)

correlation = format_section(
response_text.split('📊 Correlations:')[1].split('🚀 High-Probability
Trade Signals:')[0].strip()
if '📊 Correlations:' in response_text else "",
"🔗 CORRELATION ANALYSIS"
)

# Format signals with consistent structure


formatted_signals = ""
for i, (signal_num, signal_data) in enumerate(signals.items()):
description = {
1: "MOST CONFIDENT SETUP",
2: "SECOND BEST SETUP",
3: "THIRD BEST SETUP"
}.get(i+1, "SETUP")

formatted_signals += SIGNAL_TEMPLATE.format(
num=i+1,
description=description,
direction=signal_data['direction'].upper(),
entry=float(signal_data['entry']),
sl=float(signal_data['sl']),
tp=float(signal_data['tp']),
confidence_stars="⭐" * signal_data['confidence'],
confidence=signal_data['confidence'],
rationale=signal_data['rationale'].strip()
)

# Return formatted analysis with consistent spacing


return ANALYSIS_TEMPLATE.format(
symbol=symbol,
price=float(current_price),
timestamp=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
technical=technical,
fundamental=fundamental,
correlation=correlation,
signals=formatted_signals.strip()
)

except Exception as e:
print(f"❌ Error formatting response: {str(e)}")
return response_text

def get_ai_response(prompt, retries=MAX_RETRIES):


for attempt in range(retries):
try:
log_debug("Sending prompt to Gemini")
model = genai.GenerativeModel('gemini-pro')
response = model.generate_content(
prompt,
generation_config={
"temperature": 0.7,
"max_output_tokens": 2048,
}
)

if response and response.text:


log_debug("Gemini Response received")
return response.text
raise Exception("Invalid response format from Gemini")

except Exception as e:
print(f"🔌 Gemini API Error (Attempt {attempt + 1}/{retries}):
{str(e)}")
if attempt < retries - 1:
print(f"⏳ Retrying in {RETRY_DELAY} seconds...")
time.sleep(RETRY_DELAY)
else:
raise Exception("❌ Failed to get AI response after maximum
retries")

def analysis_cycle(symbol, timeframe):


if not mt5.initialize():
print("🔄 Reconnecting to MT5...")
initialize_mt5()

current_price = get_current_price(symbol)
if not current_price:
print("❌ Failed to get current price")
return

all_data = {symbol: fetch_intraday_data(symbol)}


correlation_data = {}

print("\n📊 Collecting Market Data:")


print("└─📈 Primary Symbol:", symbol)
for corr_symbol in CORRELATION_SYMBOLS:
try:
correlation_data[corr_symbol] = fetch_intraday_data(corr_symbol)
print(f"└─💲 {corr_symbol}: ✅ Synchronized")
except Exception as e:
print(f"└─❌ {corr_symbol}: Failed - {str(e)}")

try:
prompt = generate_analysis_prompt(symbol, all_data[symbol],
current_price['bid'], correlation_data)
response_text = get_ai_response(prompt, retries=MAX_RETRIES)
formatted_response = format_ai_response(response_text, symbol,
current_price['bid'], timeframe)
print(formatted_response)
print("\n" + "═"*60 + "\n")
signals = parse_ai_response(formatted_response)

if not signals:
print("No valid signals to process")
return
for signal_num, signal_params in signals.items():
try:
all_signals = formatted_response.split("Signal ")
signal_idx = int(signal_num[-1])
if signal_idx < len(all_signals):
signal_text = all_signals[signal_idx]
else:
signal_text = ""

if signal_params['direction'] in ['buy', 'sell']:


is_valid, message = evaluate_signal_quality(signal_params)
if is_valid:
if execute_trade(symbol, signal_params):
signal_params['status'] = "✅ Order Executed"
else:
signal_params['status'] = "❌ Order Failed"
else:
signal_params['status'] = f"⚠️ {message}"
else:
signal_params['status'] = "❌ Invalid direction"

except Exception as e:
print(f"Error processing signal {signal_num}: {str(e)}")
continue

print("─────────────────────────────────────────────────────────\n")

except Exception as e:
print(f"\n❌ AI Analysis Error: {str(e)}")
print("Waiting for next cycle...")

def main_loop():
print_title_bar()

# Get user input for symbol


symbol = input("📈 Enter trading symbol (e.g., XAUUSD, GBPUSD):
").strip().upper()

# Timeframe selection UI
print("\n" + "═"*60)
print("⏰ TIME FRAME SELECTION".center(60))
print("═"*60)
print("\nAvailable Trading Timeframes:")
print("┌───────────────┬──────────────────────────────────────┐")
print("│ Timeframe │ Description │")
print("├───────────────┼──────────────────────────────────────┤")
print("│ M1 │ 1 Minute - Ultra Short-Term Trading │")
print("│ M5 │ 5 Minutes - Short-Term Trading │")
print("│ M15 │ 15 Minutes - Intraday Trading │")
print("│ M30 │ 30 Minutes - Swing Trading │")
print("│ H1 │ 1 Hour - Medium-Term Trading │")
print("│ H4 │ 4 Hours - Long-Term Trading │")
print("└───────────────┴──────────────────────────────────────┘")

# Validate timeframe input


while True:
timeframe = input("\nSelect your preferred timeframe (e.g., M5):
").upper().strip()
if timeframe in time_frame_order:
print(f"\n✅ Selected Timeframe: {timeframe}")
break
print("\n❌ Invalid timeframe. Please choose from: M1, M5, M15, M30, H1,
H4")

if not initialize_mt5():
return

print("\n🚀 Trading System Initialized")


print(f"📡 Real-time market monitoring active on {timeframe} timeframe for
{symbol}\n")

while True:
try:
analysis_cycle(symbol, timeframe)
if not hasattr(analysis_cycle, 'next_run'):
timeframe_minutes = {
'M1': 1,
'M5': 5,
'M15': 15,
'M30': 30,
'H1': 60,
'H4': 240
}[timeframe]

analysis_cycle.next_run = datetime.now() +
timedelta(minutes=timeframe_minutes)
print(f"\n⏰ Next analysis scheduled at
{analysis_cycle.next_run.strftime('%Y-%m-%d %H:%M:%S')}")

while datetime.now() < analysis_cycle.next_run:


time_left = analysis_cycle.next_run - datetime.now()
mins = time_left.seconds // 60
secs = time_left.seconds % 60
print(f"\r⏳ Next analysis in: {mins:02d}:{secs:02d}", end="",
flush=True)
time.sleep(1)
print("\n")
analysis_cycle.next_run = datetime.now() +
timedelta(minutes=timeframe_minutes)

except Exception as e:
print(f"\n❌ Error in main loop: {str(e)}")
time.sleep(60)
except KeyboardInterrupt:
print("\n🛑 Trading bot stopped by user.")
break # Exit loop if Ctrl+C is pressed
except Exception as e:
print(f"\n❌ Error in main loop: {str(e)}")
time.sleep(60)
finally:
mt5.shutdown()

print("\n👋 Trading bot signing off.")

if __name__ == "__main__":
main_loop()

You might also like