correct way to calculate lot size and send trade in python to mql5 ?

 

Hello and thanks for reading.

edit : it seems i have posted in the wrong section being tired. i am so sorry about this.

First I must apologize if this has been answered somewhere else. I've tried looking here and with google , but everything I found was either to complicated for me to understand  , or incomplete,  or in mql4 or 5. I am terribly sorry if this is a duplicate. Being frustrated and tired by this , it is obviously easier for me to ask. If there is a kind soul that could point me in the right direction with a workflow , point me to a nice resource or show me the correct way to do this , I would be extremely grateful. 

I'm trying to fetch the symbol properties and calculate the lot size with a risk in money, however I cannot find the correct way to do it so it works on any instrument.

This is my code  :

def round_to_tick(price, tick_size):
    return round(price / tick_size) * tick_size

def calculate_long(entry_price, atr_value, tick_size, digits):
    stop_loss = round(entry_price - (2 * atr_value), digits)
    take_profit = round(entry_price + (4 * atr_value), digits)
    stop_loss = round_to_tick(stop_loss, tick_size)
    take_profit = round_to_tick(take_profit, tick_size)
    return stop_loss, take_profit

def calculate_short(entry_price, atr_value, tick_size, digits):
    stop_loss = round(entry_price + (2 * atr_value), digits)
    take_profit = round(entry_price - (4 * atr_value), digits)
    stop_loss = round_to_tick(stop_loss, tick_size)
    take_profit = round_to_tick(take_profit, tick_size)
    return stop_loss, take_profit

def get_modified_entry_price(currency, signal_type):
    tick = mt5.symbol_info_tick(currency)
    if tick is None:
        log_to_trade_log(f"Failed to get tick data for {currency}")
        return None
    return tick.bid if signal_type == "LONG" else tick.ask

def get_lot(currency, signal_type, stop_loss, open_price, money_amount, contract_size, tick_size, tick_value, digits, min_vol, max_vol, step_vol):
    log_to_trade_log(f"Symbol info for {currency}: contract_size={contract_size}, tick_size={tick_size}, tick_value={tick_value}, digits={digits}, min_vol={min_vol}, max_vol={max_vol}, step_vol={step_vol}")

    # Ensure that the retrieved values are valid
    if contract_size <= 0 or tick_size <= 0 or tick_value <= 0:
        log_to_trade_log(f"Error: Invalid symbol information for {currency}")
        return 0

    # Calculate the value per tick
    value_per_tick = tick_value / tick_size
    log_to_trade_log(f"Value per tick: {value_per_tick}")

    # Calculate the price difference for the stop loss
    price_difference = abs(open_price - stop_loss)
    log_to_trade_log(f"Price difference for stop loss: {price_difference}")

    lot_size = max_vol
    while lot_size >= min_vol:
        potential_loss = price_difference * lot_size * contract_size / open_price
        log_to_trade_log(f"Potential loss for lot size {lot_size}: {potential_loss}")
        if potential_loss <= money_amount:
            log_to_trade_log(f"Calculated lot size: {lot_size}")
            return lot_size
        lot_size = round(lot_size - step_vol, digits)

    log_to_trade_log("No valid lot size found within the specified risk.")
    return 0

def check_and_send_trade(currency, signal_type, end, rates_frame):
    if end != len(rates_frame) - 2:
        log_to_main_log(f"Signal at index {end} is not the most recent one. Skipping trade.")
        return

    entry_price = rates_frame['close'][end]
    atr_value = rates_frame['atr'][end]

    # Retrieve symbol information
    symbol = mt5.symbol_info(currency)
    if symbol is None:
        log_to_trade_log(f"Symbol {currency} not found.")
        return

    symbol_info_dict = symbol._asdict()
    tick_size = symbol_info_dict.get('trade_tick_size')
    digits = symbol_info_dict.get('digits')
    contract_size = symbol_info_dict.get('trade_contract_size')
    tick_value = symbol_info_dict.get('trade_tick_value')
    min_vol = symbol_info_dict.get('volume_min')
    max_vol = symbol_info_dict.get('volume_max')
    step_vol = symbol_info_dict.get('volume_step')

    log_to_trade_log(f"Symbol info for {currency}: tick_size={tick_size}, digits={digits}, contract_size={contract_size}, tick_value={tick_value}, min_vol={min_vol}, max_vol={max_vol}, step_vol={step_vol}")

    if signal_type == "LONG":
        stop_loss, take_profit = calculate_long(entry_price, atr_value, tick_size, digits)
    else:
        stop_loss, take_profit = calculate_short(entry_price, atr_value, tick_size, digits)

    # Calculate lot size
    lot_size = get_lot(currency, signal_type, stop_loss, entry_price, RISK_MONEY, contract_size, tick_size, tick_value, digits, min_vol, max_vol, step_vol)
    if lot_size == 0:
        log_to_trade_log("Failed to calculate a valid lot size.")
        return

    # Log trade properties with date and time
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    log_message = f"Date and Time: {current_time}, Currency: {currency}, Signal Type: {signal_type}, Entry Price: {entry_price}, Stop Loss: {stop_loss}, Take Profit: {take_profit}, Lot Size: {lot_size}"
    log_to_trade_log('-' * 80)
    log_to_trade_log(log_message)

    # Modify entry price based on current Bid or Ask
    modified_entry_price = get_modified_entry_price(currency, signal_type)
    if modified_entry_price is None:
        return

    # Prepare the trade request
    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": currency,
        "volume": lot_size,
        "type": mt5.ORDER_TYPE_BUY if signal_type == "LONG" else mt5.ORDER_TYPE_SELL,
        "price": modified_entry_price,
        "sl": stop_loss,
        "tp": take_profit,
        "magic": 1,
        "comment": "Trade executed by script",
        "type_filling": mt5.ORDER_FILLING_RETURN,
    }

    # Send the trade request
    result = mt5.order_send(request)

    # Check the execution result
    if result is None:
        log_to_trade_log("Order send failed, result is None")
        return

    log_to_trade_log('*' * 40)
    if result.retcode != mt5.TRADE_RETCODE_DONE:
        log_to_trade_log(f"Order send failed, retcode={result.retcode}")
        result_dict = result._asdict()
        for field in result_dict.keys():
            log_to_trade_log(f"   {field}={result_dict[field]}")
            if field == "request":
                traderequest_dict = result_dict[field]._asdict()
                for tradereq_field in traderequest_dict:
                    log_to_trade_log(f"       traderequest: {tradereq_field}={traderequest_dict[tradereq_field]}")
    else:
        log_to_trade_log(f"Order send done, opened position with POSITION_TICKET={result.order}")
    log_to_trade_log('*' * 40)
The main problem I have is that my broker returns tick_size=1e-05 , and I have no idea how to round the tps and sls to the correct tick_size , nor calculate the volume for the trade correctly. Ive read about deltaPerLot but i didnt understand how to use it.

Again , apologies for being a lazy bastard and a half brain. 

and thank you very much in advance for any help you could provide.

Wishing you a great day and a great life.
 

test code with a function that works. some checks are missing so you shouldnt use this out of the box. hope it helps someone anyway. Its not elegant but does the job

import MetaTrader5 as mt5
from datetime import datetime

# Initialize MetaTrader 5
def initialize_mt5():
    if not mt5.initialize():
        print(f"initialize() failed, error code = {mt5.last_error()}")
        return False
    print("MetaTrader 5 initialized.")
    return True

# Function to calculate lot size using MetaTrader 5 values
def calculate_lot_size(currency, signal_type, stop_loss, entry_price, risk_money):
    symbol_info = mt5.symbol_info(currency)
    if symbol_info is None:
        print(f"Symbol {currency} not found.")
        return 0

    volume_step = symbol_info.volume_step
    max_volume = symbol_info.volume_max
    min_volume = symbol_info.volume_min

    lot_size = min_volume
    while lot_size <= max_volume:
        if signal_type == "LONG":
            potential_loss = mt5.order_calc_profit(mt5.ORDER_TYPE_BUY, currency, lot_size, entry_price, stop_loss)
        else:
            potential_loss = mt5.order_calc_profit(mt5.ORDER_TYPE_SELL, currency, lot_size, entry_price, stop_loss)

        print(f"Lot Size: {lot_size}, Potential Loss: {potential_loss}, Risk Money: {risk_money}")

        if abs(potential_loss) >= risk_money:
            print(f"Calculated lot size: {lot_size}")
            return lot_size

        lot_size = round(lot_size + volume_step, 2)

    print("No valid lot size found within the specified risk.")
    return 0

# Mock function to simulate logging
def log_to_trade_log(message):
    print(message)

# Test the calculate_lot_size function
def test_calculate_lot_size():
    currency = "EURUSD"
    risk_money = 100  # Risk amount in dollars

    # Test long trade
    signal_type = "LONG"
    entry_price = 1.2345
    stop_loss = 1.2245  # Adjusted stop loss to increase potential loss
    lot_size = calculate_lot_size(currency, signal_type, stop_loss, entry_price, risk_money)
    print(f"Test Long Trade: Lot Size = {lot_size}")

    # Test short trade
    signal_type = "SHORT"
    entry_price = 1.2340
    stop_loss = 1.2440  # Adjusted stop loss to increase potential loss
    lot_size = calculate_lot_size(currency, signal_type, stop_loss, entry_price, risk_money)
    print(f"Test Short Trade: Lot Size = {lot_size}")

if __name__ == "__main__":
    # Initialize MetaTrader 5
    if initialize_mt5():
        test_calculate_lot_size()
        mt5.shutdown()