Skip to content

MT5Service - Trading Operations Methods (Mid-Level API)

⚠️ Important: Read This First

MT5Service is an architectural layer between high-level (Sugar) and low-level (Account) APIs.

Understanding the 6 Methods:

Method Direct Use Value Architectural Role
check_order() VERY HIGH - Extracts deeply nested protobuf → OrderCheckResult dataclass (8 fields) ✅ Used by Sugar
place_order() HIGH - Flattens protobuf → OrderResult dataclass (10 fields) ✅ Used by Sugar
modify_order() HIGH - Flattens protobuf → OrderResult dataclass (10 fields) ✅ Used by Sugar
close_order() LOW - Unpacks data.returned_code from protobuf → int ✅ Used by Sugar
calculate_margin() LOW - Unpacks data.margin from protobuf → float ✅ Used by Sugar
calculate_profit() LOW - Unpacks data.profit from protobuf → float ✅ Used by Sugar

What This Means:

Architecture (3 layers):

MT5Sugar (HIGH)     →  service.place_order()           ← Sugar uses Service methods
MT5Service (MID)    →  account.order_send()            ← Service unpacks protobuf + creates dataclasses
MT5Account (LOW)    →  gRPC call → protobuf Data objects

Value breakdown:

  • VERY HIGH (1 method): check_order() - extracts deeply nested mrpc_mql_trade_check_result structure
  • HIGH (2 methods): place_order(), modify_order() - flatten 10 protobuf fields into clean dataclass
  • LOW (3 methods): Simple unpacking of single protobuf field (returned_code, margin, profit)

For direct MT5Service usage:

  • Highly recommended: Use check_order(), place_order(), modify_order() - significant dataclass conversion value
  • Optional: close_order(), calculate_margin(), calculate_profit() - minor unpacking value

For MT5Sugar users:

  • ✅ All methods work perfectly - the layer serves its architectural purpose

API Layer: MID-LEVEL - wrappers over MT5Account with clean dataclass returns

Implementation:

These methods are implemented in src/pymt5/mt5_service.py, which wraps package/MetaRpcMT5/helpers/mt5_account.py low-level API with dataclass conversion for trading results.

Example files:

  • examples/2_service/04_service_demo.py - comprehensive demo of all MT5Service methods including trading operations (STEP 5)

Why These Methods Exist

Real Value: Protobuf Unpacking + Dataclass Conversion

3 methods add VERY HIGH/HIGH value through complex dataclass conversion, 3 methods add LOW value through simple unpacking:

Problem with MT5Account (Low-level):

# MT5Account returns complex protobuf objects
request = trading_helper_pb2.OrderSendRequest()
request.symbol = "EURUSD"
request.volume = 0.1
request.action = 1  # TRADE_ACTION_DEAL
request.type = 0    # ORDER_TYPE_BUY

result_data = await account.order_send(request, None, None)
# result_data is protobuf OrderSendData with 10 nested fields
if result_data.returned_code == 10009:  # TRADE_RETCODE_DONE
    print(f"Deal: {result_data.deal}, Order: {result_data.order}")
    # Must manually access each of 10 protobuf fields

# Checking order requires extracting DEEPLY NESTED structure:
check_response = await account.order_check(request, None, None)
result = check_response.mrpc_mql_trade_check_result  # ← Deeply nested!
if result.returned_code == 0:
    print(f"Valid! Margin: {result.margin}")

Solution with MT5Service (Mid-level):

# Service creates clean dataclasses - no manual field extraction
request = trading_helper_pb2.OrderSendRequest()
request.symbol = "EURUSD"
request.volume = 0.1
request.action = 1
request.type = 0

result = await service.place_order(request)
# result is OrderResult dataclass with 10 clear fields
if result.returned_code == 10009:
    print(f"Deal: {result.deal}, Order: {result.order}")
    # Clean dataclass access with type hints

# Check order returns clean dataclass - no nesting:
check = await service.check_order(request)
# check is OrderCheckResult dataclass with 8 fields - already extracted!
if check.returned_code == 0:
    print(f"Valid! Margin: {check.margin}")

What MT5Service provides:

  1. Complex dataclass conversion (3 methods with HIGH/VERY HIGH value):

  2. check_order(): Extracts deeply nested mrpc_mql_trade_check_result → OrderCheckResult (8 fields)

  3. place_order(): Flattens 10 protobuf fields → OrderResult dataclass
  4. modify_order(): Flattens 10 protobuf fields → OrderResult dataclass

  5. Simple unpacking (3 methods with LOW value):

  6. close_order(): Unpacks data.returned_code → int

  7. calculate_margin(): Unpacks data.margin → float
  8. calculate_profit(): Unpacks data.profit → float

  9. Cleaner API:

  10. No need to manually access nested protobuf structures

  11. Type hints for IDE autocomplete
  12. Clear field names in dataclasses

Architectural purpose:

  • MT5Sugar uses these methods to maintain layered architecture (Sugar → Service → Account)
  • Direct users benefit significantly from dataclass conversion (especially check_order/place_order/modify_order)

All 6 Methods

Method Returns Description
place_order() OrderResult Send market/pending order to broker
modify_order() OrderResult Modify existing order or position (SL/TP)
close_order() int Close position or delete pending order
check_order() OrderCheckResult Validate order before sending
calculate_margin() float Calculate required margin for an order
calculate_profit() float Calculate potential profit for a trade

Key Concepts

Order Types

  • Market Order: Executes immediately at current market price (BUY/SELL)
  • Pending Order: Executes when price reaches specified level (BUY_LIMIT, SELL_LIMIT, BUY_STOP, SELL_STOP)

Return Codes

All trading operations return a return code indicating success/failure:

  • 10009 (TRADE_RETCODE_DONE): Success - order placed/modified/closed
  • 10004 (TRADE_RETCODE_REQUOTE): Requote - price changed, retry
  • 10006 (TRADE_RETCODE_REJECT): Rejected by broker
  • 10007 (TRADE_RETCODE_CANCEL): Cancelled by trader
  • 10013 (TRADE_RETCODE_INVALID_VOLUME): Invalid volume
  • 10014 (TRADE_RETCODE_INVALID_PRICE): Invalid price
  • 10015 (TRADE_RETCODE_INVALID_STOPS): Invalid SL/TP

Always check returned_code == 10009 for success!

Pre-Trade Validation

Before sending orders, use check_order() to validate: - Sufficient margin - Valid volume/price/stops - Trading allowed


➕ Dataclasses (DTOs)

OrderResult

@dataclass
class OrderResult:
    """
    Result of a trading operation.

    ADVANTAGE: Clean dataclass instead of protobuf OrderSendData/OrderModifyData.
    """
    returned_code: int      # Operation return code (10009 = TRADE_RETCODE_DONE)
    deal: int               # Deal ticket number (if executed)
    order: int              # Order ticket number (if placed)
    volume: float           # Executed volume confirmed by broker
    price: float            # Execution price confirmed by broker
    bid: float              # Current Bid price
    ask: float              # Current Ask price
    comment: str            # Broker comment or error description
    request_id: int         # Request ID set by terminal
    ret_code_external: int  # External return code

Usage:

result = await service.place_order(request)
if result.returned_code == 10009:
    print(f"Success! Deal: {result.deal}, Order: {result.order}")
else:
    print(f"Failed: {result.comment}")

OrderCheckResult

@dataclass
class OrderCheckResult:
    """Result of order validation."""
    returned_code: int      # Validation code (0 = success)
    balance: float          # Balance after deal
    equity: float           # Equity after deal
    profit: float           # Profit
    margin: float           # Required margin
    margin_free: float      # Free margin after
    margin_level: float     # Margin level after (%)
    comment: str            # Error description

Usage:

check = await service.check_order(request)
if check.returned_code == 0:
    print(f"Valid! Required margin: ${check.margin:.2f}")
else:
    print(f"Invalid: {check.comment}")

Method Signatures

1) place_order

async def place_order(
    self,
    request: Any,  # trading_helper_pb2.OrderSendRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> OrderResult

Send market/pending order to broker.

Args:

  • request: OrderSendRequest protobuf with order parameters

Returns: OrderResult dataclass with deal/order tickets and execution details

Technical: Low-level returns OrderSendData protobuf with nested broker response fields. This wrapper flattens protobuf into OrderResult dataclass with 10 fields.

Check returned_code == 10009 (TRADE_RETCODE_DONE) for successful execution.

OrderSendRequest fields:

request.symbol = "EURUSD"           # Symbol
request.volume = 0.1                # Volume in lots
request.action = 1                  # TRADE_ACTION_DEAL (market order)
request.type = 0                    # ORDER_TYPE_BUY
request.price = 0.0                 # 0 for market orders
request.sl = 1.08000                # Stop Loss (optional)
request.tp = 1.09000                # Take Profit (optional)
request.deviation = 20              # Max price deviation in points
request.comment = "My order"        # Optional comment

2) modify_order

async def modify_order(
    self,
    request: Any,  # trading_helper_pb2.OrderModifyRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> OrderResult

Modify existing order or position (SL/TP).

Args:

  • request: OrderModifyRequest protobuf with modification parameters

Returns: OrderResult with modification details

Technical: Low-level returns OrderModifyData protobuf (same structure as OrderSendData). This wrapper flattens into OrderResult.

Used to:

  • Change SL/TP on open positions
  • Modify pending order price/SL/TP

OrderModifyRequest fields:

request.order = 12345678            # Ticket number to modify
request.sl = 1.08500                # New Stop Loss
request.tp = 1.09500                # New Take Profit
request.price = 1.08000             # New order price (pending orders only)

3) close_order

async def close_order(
    self,
    request: Any,  # trading_helper_pb2.OrderCloseRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> int

Close position or delete pending order.

Args:

  • request: OrderCloseRequest protobuf with ticket to close

Returns: int return code directly (10009 = success)

Technical: Low-level returns OrderCloseData with data.returned_code wrapper. This auto-extracts the int return code.

How it works:

  • For positions: Creates opposite market order
  • For pending orders: Sends delete request

OrderCloseRequest fields:

request.order = 12345678            # Ticket number to close
request.volume = 0.0                # 0 = close all, or partial volume

4) check_order

async def check_order(
    self,
    request: Any,  # trade_functions_pb2.OrderCheckRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> OrderCheckResult

Validate order before sending to broker.

Args:

  • request: OrderCheckRequest protobuf (similar to OrderSendRequest)

Returns: OrderCheckResult dataclass with validation details

Technical: Low-level returns OrderCheckResponse with deeply nested mrpc_mql_trade_check_result. This wrapper extracts 8 validation fields.

Use this before place_order() to:

  • Pre-validate margin requirements without sending to broker
  • Check if order parameters are valid
  • See impact on account (balance, equity, margin)

OrderCheckRequest fields:

request.symbol = "EURUSD"
request.volume = 0.1
request.action = 1                  # TRADE_ACTION_DEAL
request.type = 0                    # ORDER_TYPE_BUY
request.price = 0.0

returned_code = 0 means valid order


5) calculate_margin

async def calculate_margin(
    self,
    request: Any,  # trade_functions_pb2.OrderCalcMarginRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> float

Calculate required margin for an order.

Args: - request: OrderCalcMarginRequest protobuf

Returns: float margin value directly (no Data struct)

Technical: Low-level returns OrderCalcMarginResponse with data.margin wrapper. This auto-extracts margin float.

Use to:

  • Check margin requirements before placing order
  • Validate if you have enough free margin
  • Calculate position sizing

OrderCalcMarginRequest fields:

request.symbol = "EURUSD"
request.volume = 0.1
request.action = 1                  # TRADE_ACTION_DEAL
request.type = 0                    # ORDER_TYPE_BUY

6) calculate_profit

async def calculate_profit(
    self,
    request: Any,  # trade_functions_pb2.OrderCalcProfitRequest
    deadline: Optional[datetime] = None,
    cancellation_event: Optional[Any] = None,
) -> float

Calculate potential profit for a trade.

Args:

  • request: OrderCalcProfitRequest protobuf

Returns: float profit value directly (no Data struct)

Technical: Low-level returns OrderCalcProfitResponse with data.profit wrapper. This auto-extracts profit float.

Use to:

  • Calculate P&L for hypothetical trade given entry/exit prices and volume
  • Validate risk/reward ratio
  • Calculate TP target profit

OrderCalcProfitRequest fields:

request.symbol = "EURUSD"
request.volume = 0.1
request.action = 1                  # TRADE_ACTION_DEAL
request.type = 0                    # ORDER_TYPE_BUY
request.price_open = 1.08000        # Entry price
request.price_close = 1.09000       # Exit price

🔗 Usage Examples

Example 1: Placing Market Buy Order

from pymt5 import MT5Service
from MetaRpcMT5 import mt5_term_api_trading_helper_pb2 as trading_helper_pb2

async def place_market_buy(service: MT5Service, symbol: str, volume: float):
    """Place a market BUY order."""

    # Create request
    request = trading_helper_pb2.OrderSendRequest()
    request.symbol = symbol
    request.volume = volume
    request.action = 1              # TRADE_ACTION_DEAL (market order)
    request.type = 0                # ORDER_TYPE_BUY
    request.price = 0.0             # 0 for market orders
    request.deviation = 20          # Max 20 points deviation
    request.comment = "Market BUY"

    # Send order
    result = await service.place_order(request)

    # Check result
    if result.returned_code == 10009:  # TRADE_RETCODE_DONE
        print(f"SUCCESS!")
        print(f"  Deal: {result.deal}")
        print(f"  Order: {result.order}")
        print(f"  Price: {result.price}")
        print(f"  Volume: {result.volume}")
    else:
        print(f"FAILED: {result.comment}")
        print(f"  Return code: {result.returned_code}")

    return result

Example 2: Placing Order with SL/TP

async def place_order_with_stops(
    service: MT5Service,
    symbol: str,
    volume: float,
    sl: float,
    tp: float
):
    """Place market order with Stop Loss and Take Profit."""

    request = trading_helper_pb2.OrderSendRequest()
    request.symbol = symbol
    request.volume = volume
    request.action = 1              # TRADE_ACTION_DEAL
    request.type = 0                # ORDER_TYPE_BUY
    request.price = 0.0
    request.sl = sl                 # Stop Loss
    request.tp = tp                 # Take Profit
    request.deviation = 20
    request.comment = "Order with SL/TP"

    result = await service.place_order(request)

    if result.returned_code == 10009:
        print(f"Order placed: {result.order}")
        print(f"  SL: {sl}, TP: {tp}")
    else:
        print(f"Failed: {result.comment}")

    return result

Example 3: Validating Order Before Placing

from MetaRpcMT5 import mt5_term_api_trade_functions_pb2 as trade_functions_pb2

async def safe_place_order(
    service: MT5Service,
    symbol: str,
    volume: float
) -> bool:
    """Validate order before placing."""

    # Step 1: Check order
    check_request = trade_functions_pb2.OrderCheckRequest()
    check_request.symbol = symbol
    check_request.volume = volume
    check_request.action = 1
    check_request.type = 0
    check_request.price = 0.0

    check_result = await service.check_order(check_request)

    if check_result.returned_code != 0:
        print(f"Order validation FAILED: {check_result.comment}")
        return False

    print("Order validation PASSED:")
    print(f"  Required margin: ${check_result.margin:.2f}")
    print(f"  Free margin after: ${check_result.margin_free:.2f}")
    print(f"  Margin level after: {check_result.margin_level:.2f}%")

    # Step 2: Place order
    order_request = trading_helper_pb2.OrderSendRequest()
    order_request.symbol = symbol
    order_request.volume = volume
    order_request.action = 1
    order_request.type = 0
    order_request.price = 0.0
    order_request.deviation = 20

    result = await service.place_order(order_request)

    if result.returned_code == 10009:
        print(f"Order placed successfully: {result.order}")
        return True
    else:
        print(f"Order placement failed: {result.comment}")
        return False

Example 4: Modifying Position SL/TP

async def modify_position_stops(
    service: MT5Service,
    ticket: int,
    new_sl: float,
    new_tp: float
):
    """Modify Stop Loss and Take Profit of existing position."""

    request = trading_helper_pb2.OrderModifyRequest()
    request.order = ticket          # Ticket to modify
    request.sl = new_sl             # New Stop Loss
    request.tp = new_tp             # New Take Profit

    result = await service.modify_order(request)

    if result.returned_code == 10009:
        print(f"Position {ticket} modified successfully")
        print(f"  New SL: {new_sl}")
        print(f"  New TP: {new_tp}")
    else:
        print(f"Modification failed: {result.comment}")

    return result

Example 5: Closing Position

async def close_position(service: MT5Service, ticket: int):
    """Close an open position."""

    request = trading_helper_pb2.OrderCloseRequest()
    request.order = ticket
    request.volume = 0.0            # 0 = close entire position

    return_code = await service.close_order(request)

    if return_code == 10009:
        print(f"Position {ticket} closed successfully")
        return True
    else:
        print(f"Failed to close position {ticket}, code: {return_code}")
        return False

Example 6: Calculating Required Margin

async def check_margin_requirement(
    service: MT5Service,
    symbol: str,
    volume: float
):
    """Calculate required margin for a trade."""

    request = trade_functions_pb2.OrderCalcMarginRequest()
    request.symbol = symbol
    request.volume = volume
    request.action = 1              # TRADE_ACTION_DEAL
    request.type = 0                # ORDER_TYPE_BUY

    margin = await service.calculate_margin(request)

    print(f"Required margin for {volume} lots of {symbol}: ${margin:.2f}")

    # Check if we have enough free margin
    from MetaRpcMT5 import mt5_term_api_account_information_pb2 as account_info_pb2
    free_margin = await service.get_account_double(
        account_info_pb2.ACCOUNT_MARGIN_FREE
    )

    if free_margin >= margin:
        print(f"OK: You have ${free_margin:.2f} free margin")
        return True
    else:
        print(f"INSUFFICIENT: You need ${margin:.2f} but only have ${free_margin:.2f}")
        return False

Example 7: Calculating Potential Profit

async def calculate_trade_profit(
    service: MT5Service,
    symbol: str,
    volume: float,
    entry_price: float,
    exit_price: float
):
    """Calculate potential profit for a trade."""

    request = trade_functions_pb2.OrderCalcProfitRequest()
    request.symbol = symbol
    request.volume = volume
    request.action = 1              # TRADE_ACTION_DEAL
    request.type = 0                # ORDER_TYPE_BUY
    request.price_open = entry_price
    request.price_close = exit_price

    profit = await service.calculate_profit(request)

    print(f"Trade Calculation for {symbol}:")
    print(f"  Volume: {volume} lots")
    print(f"  Entry: {entry_price}")
    print(f"  Exit: {exit_price}")
    print(f"  Profit: ${profit:.2f}")

    return profit

Example 8: Complete Safe Trading Flow

async def safe_trade_with_validation(
    service: MT5Service,
    symbol: str,
    volume: float,
    sl_points: int,
    tp_points: int
):
    """Complete safe trading flow with all validations."""

    from MetaRpcMT5 import mt5_term_api_market_info_pb2 as market_info_pb2

    # Step 1: Get current price
    tick = await service.get_symbol_tick(symbol)
    print(f"Current price: {tick.ask}")

    # Step 2: Calculate SL/TP prices
    point = await service.get_symbol_double(
        symbol, market_info_pb2.SYMBOL_POINT
    )
    sl = tick.ask - (sl_points * point)
    tp = tick.ask + (tp_points * point)

    print(f"Calculated SL: {sl:.5f}, TP: {tp:.5f}")

    # Step 3: Calculate required margin
    margin_request = trade_functions_pb2.OrderCalcMarginRequest()
    margin_request.symbol = symbol
    margin_request.volume = volume
    margin_request.action = 1
    margin_request.type = 0

    required_margin = await service.calculate_margin(margin_request)
    print(f"Required margin: ${required_margin:.2f}")

    # Step 4: Check free margin
    from MetaRpcMT5 import mt5_term_api_account_information_pb2 as account_info_pb2
    free_margin = await service.get_account_double(
        account_info_pb2.ACCOUNT_MARGIN_FREE
    )

    if free_margin < required_margin:
        print(f"INSUFFICIENT MARGIN: Need ${required_margin:.2f}, have ${free_margin:.2f}")
        return None

    # Step 5: Calculate potential profit
    profit_request = trade_functions_pb2.OrderCalcProfitRequest()
    profit_request.symbol = symbol
    profit_request.volume = volume
    profit_request.action = 1
    profit_request.type = 0
    profit_request.price_open = tick.ask
    profit_request.price_close = tp

    potential_profit = await service.calculate_profit(profit_request)
    print(f"Potential TP profit: ${potential_profit:.2f}")

    # Step 6: Validate order
    check_request = trade_functions_pb2.OrderCheckRequest()
    check_request.symbol = symbol
    check_request.volume = volume
    check_request.action = 1
    check_request.type = 0
    check_request.price = 0.0

    check_result = await service.check_order(check_request)

    if check_result.returned_code != 0:
        print(f"Order validation failed: {check_result.comment}")
        return None

    print("Order validation passed!")

    # Step 7: Place order
    order_request = trading_helper_pb2.OrderSendRequest()
    order_request.symbol = symbol
    order_request.volume = volume
    order_request.action = 1
    order_request.type = 0
    order_request.price = 0.0
    order_request.sl = sl
    order_request.tp = tp
    order_request.deviation = 20
    order_request.comment = "Safe validated order"

    result = await service.place_order(order_request)

    if result.returned_code == 10009:
        print(f"\nSUCCESS! Order placed:")
        print(f"  Ticket: {result.order}")
        print(f"  Price: {result.price}")
        print(f"  SL: {sl:.5f}, TP: {tp:.5f}")
        return result
    else:
        print(f"\nFAILED: {result.comment}")
        return None

When to Use Each Method

Use place_order()

Use when:

  • Opening new positions (market or pending orders)
  • Need to specify SL/TP with order
  • Ready to send order to broker

Important:

  • Always check returned_code == 10009 for success
  • Use check_order() first for validation

Use modify_order()

Use when:

  • Changing SL/TP on open position
  • Modifying pending order price
  • Adjusting stops based on market conditions

Use close_order()

Use when:

  • Closing open position
  • Deleting pending order
  • Emergency exit from trade

Use check_order()

Use when:

  • Pre-validating order before placing
  • Checking margin requirements
  • Seeing impact on account before trading

ALWAYS use before place_order() for safety!


Use calculate_margin()

Use when:

  • Planning position size
  • Checking if you have enough margin
  • Calculating max position size

Use calculate_profit()

Use when:

  • Calculating risk/reward ratio
  • Setting TP targets
  • Analyzing trade potential

Recommendations

  1. Always validate first - Use check_order() before place_order()
  2. Check return codes - returned_code == 10009 means success
  3. Calculate margin - Ensure sufficient free margin before trading
  4. Use SL/TP - Always set Stop Loss and Take Profit
  5. Handle errors - Check result.comment for error descriptions
  6. Validate volumes - Check symbol's VOLUME_MIN, VOLUME_MAX, VOLUME_STEP

Safe trading pattern:

# 1. Calculate margin
margin = await service.calculate_margin(margin_request)

# 2. Check order
check = await service.check_order(check_request)
if check.returned_code != 0:
    return

# 3. Place order
result = await service.place_order(order_request)
if result.returned_code == 10009:
    print("Success!")


Common Return Codes

Code Name Meaning
10009 TRADE_RETCODE_DONE Success - request completed
10004 TRADE_RETCODE_REQUOTE Requote - price changed
10006 TRADE_RETCODE_REJECT Request rejected
10013 TRADE_RETCODE_INVALID_VOLUME Invalid volume
10014 TRADE_RETCODE_INVALID_PRICE Invalid price
10015 TRADE_RETCODE_INVALID_STOPS Invalid SL/TP
10016 TRADE_RETCODE_INVALID_FILL Invalid filling mode
10018 TRADE_RETCODE_MARKET_CLOSED Market is closed
10019 TRADE_RETCODE_NO_MONEY Not enough money


Summary

Real Value Assessment

6 methods with mixed value levels - 3 with HIGH/VERY HIGH value (dataclass conversion), 3 with LOW value (simple unpacking):

Method Value Level What It Does
check_order() VERY HIGH Extracts deeply nested mrpc_mql_trade_check_result → OrderCheckResult (8 fields)
place_order() HIGH Flattens 10 protobuf fields → OrderResult dataclass
modify_order() HIGH Flattens 10 protobuf fields → OrderResult dataclass
close_order() LOW Unpacks data.returned_code from protobuf → int
calculate_margin() LOW Unpacks data.margin from protobuf → float
calculate_profit() LOW Unpacks data.profit from protobuf → float

Why these methods have value:

MT5Account returns protobuf objects:

# Low-level returns:
result_data: OrderSendData = await account.order_send(...)  # 10 protobuf fields
check_response: OrderCheckResponse = await account.order_check(...)  # Deeply nested!
close_data: OrderCloseData = await account.order_close(...)  # data.returned_code wrapper
margin_response: OrderCalcMarginResponse = await account.order_calc_margin(...)  # data.margin

MT5Service unpacks and converts:

# Mid-level returns:
result: OrderResult = await service.place_order(...)              # Flattened dataclass (10 fields)
check: OrderCheckResult = await service.check_order(...)          # Extracted dataclass (8 fields)
code: int = await service.close_order(...)                        # Unpacked int
margin: float = await service.calculate_margin(...)               # Unpacked float

Key advantages:

  1. Complex dataclass conversion (HIGH/VERY HIGH value):

  2. check_order() - Extracts deeply nested protobuf structure (VERY valuable!)

  3. place_order(), modify_order() - Flatten 10 fields into clean OrderResult dataclass

  4. Simple unpacking (LOW value):

  5. close_order(), calculate_margin(), calculate_profit() - Simple single-field extraction

  6. Type hints - Full IDE autocomplete support for all dataclass fields

  7. Clear return codes - All methods return standardized codes for error handling

Best practices:

  1. ALWAYS use check_order() before place_order() for safety - it extracts complex validation data
  2. ALWAYS check returned_code == 10009 for success
  3. ALWAYS calculate margin before trading
  4. ALWAYS use Stop Loss and Take Profit
  5. ALWAYS handle errors (check result.comment)

Bottom line:

  • For direct users: Highly recommend using check_order(), place_order(), modify_order() - significant dataclass conversion value
  • Optional: close_order(), calculate_margin(), calculate_profit() - minor unpacking, could call Account directly
  • For Sugar users: Methods serve architectural purpose perfectly
  • vs MT5Account: Significant improvement through dataclass conversion, especially for check_order()