MT5Service - Trading Methods (Mid-Level API)ΒΆ
6 methods for managing trading operations with clean Go types instead of protobuf
π§© API Layer: MID-LEVEL - wrappers over MT5Account with convenient DTO structures
Implementation:
These methods are implemented in examples/mt5/MT5Service.go, which wraps package/Helpers/MT5Account.go low-level API with convenient helpers for ease of use.
Demo files:
-
examples/demos/service/04_service_demo.go- comprehensive examples of all service wrapper methods -
examples/demos/service/05_service_streaming.go- streaming methods examples
π― Why These Methods ExistΒΆ
Problem: In MT5Account, trading operations return complex protobuf objects with nested structures:
// Low-level (MT5Account)
data, err := account.OrderSend(ctx, &pb.OrderSendRequest{...})
if data.ReturnedCode == 10009 {
fmt.Printf("Order: %d, Deal: %d\n", data.Order, data.Deal)
}
// data.RetCodeExternal, data.Comment - all scattered across fields
Solution: MT5Service returns clean DTO structures with clear fields:
// Mid-level (MT5Service)
result, err := service.PlaceOrder(ctx, req)
if result.ReturnedCode == 10009 {
fmt.Printf("Order: %d, Deal: %d\n", result.Order, result.Deal)
}
// Everything in one OrderResult structure
Advantages:
- β Clean Go structures instead of protobuf Data
- β OrderResult and OrderCheckResult instead of nested MqlTrade*
- β Clear field names (ReturnedCode, Margin, MarginFree)
- β Automatic unpacking of nested structures
- β Simple methods for margin and profit calculation
π All 6 MethodsΒΆ
| Method | Returns | Low-Level Equivalent |
|---|---|---|
PlaceOrder(ctx, req) |
*OrderResult |
OrderSend(ctx, req) + unpacking |
ModifyOrder(ctx, req) |
*OrderResult |
OrderModify(ctx, req) + unpacking |
CloseOrder(ctx, req) |
uint32 |
OrderClose(ctx, req) + .ReturnedCode |
CheckOrder(ctx, req) |
*OrderCheckResult |
OrderCheck(ctx, req) + extracting MqlTradeCheckResult |
CalculateMargin(ctx, req) |
float64 |
OrderCalcMargin(ctx, req) + .Margin |
CalculateProfit(ctx, req) |
float64 |
OrderCalcProfit(ctx, req) + .Profit |
π¦ DTO StructuresΒΆ
OrderResultΒΆ
type OrderResult struct {
ReturnedCode uint32 // Return code (10009 = TRADE_RETCODE_DONE)
Deal uint64 // Deal ticket (if executed)
Order uint64 // Order ticket (if placed)
Volume float64 // Executed volume confirmed by broker
Price float64 // Execution price confirmed by broker
Bid float64 // Current Bid price
Ask float64 // Current Ask price
Comment string // Broker comment or error description
RequestID uint32 // Request ID set by terminal
RetCodeExternal int32 // Return code from external trading system
}
Advantage:
All information about the trading operation result in one structure.
OrderCheckResultΒΆ
type OrderCheckResult struct {
ReturnedCode uint32 // Validation result code (0 = success)
Balance float64 // Account balance after deal execution
Equity float64 // Account equity after deal execution
Profit float64 // Floating profit after deal
Margin float64 // Margin requirements for the order
MarginFree float64 // Free margin after deal
MarginLevel float64 // Margin level after deal (%)
Comment string // Error description (if validation failed)
}
Advantage:
Shows what the account state will be after order execution.
π Method SignaturesΒΆ
1) PlaceOrderΒΆ
func (s *MT5Service) PlaceOrder(
ctx context.Context,
req *pb.OrderSendRequest,
) (*OrderResult, error)
Sends a market or pending order to MT5 terminal.
Success check:
if result.ReturnedCode == 10009 { // TRADE_RETCODE_DONE
fmt.Printf("Success! Order: %d, Deal: %d\n", result.Order, result.Deal)
}
2) ModifyOrderΒΆ
func (s *MT5Service) ModifyOrder(
ctx context.Context,
req *pb.OrderModifyRequest,
) (*OrderResult, error)
Modifies an existing order or position (changing SL/TP/price).
3) CloseOrderΒΆ
Closes a position or deletes a pending order.
Returns only ReturnedCode (10009 = success). Simpler than PlaceOrder for closing.
4) CheckOrderΒΆ
func (s *MT5Service) CheckOrder(
ctx context.Context,
req *pb.OrderCheckRequest,
) (*OrderCheckResult, error)
Validates order before sending to broker.
Use before PlaceOrder to avoid rejections.
5) CalculateMarginΒΆ
func (s *MT5Service) CalculateMargin(
ctx context.Context,
req *pb.OrderCalcMarginRequest,
) (float64, error)
Calculates required margin for a potential order.
Use before opening a position to check margin sufficiency.
6) CalculateProfitΒΆ
func (s *MT5Service) CalculateProfit(
ctx context.Context,
req *pb.OrderCalcProfitRequest,
) (float64, error)
Calculates potential profit for a hypothetical order.
Useful for profit/risk calculations before placing real orders.
π‘ Usage ExamplesΒΆ
Example 1: Opening a Market BUY PositionΒΆ
// β
MT5Service - returns clean OrderResult (not protobuf)
ctx := context.Background()
// 1. Create order request
req := &pb.OrderSendRequest{
Symbol: "EURUSD",
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: 0.1,
Price: 0, // market price
Sl: 0,
Tp: 0,
Deviation: 10,
}
// 2. Send order to broker
result, err := service.PlaceOrder(ctx, req)
if err != nil {
return fmt.Errorf("order failed: %w", err)
}
// 3. Check result code
if result.ReturnedCode != 10009 {
fmt.Printf("β Error: %s (code: %d)\n", result.Comment, result.ReturnedCode)
return fmt.Errorf("order rejected")
}
// 4. Success - display execution details
fmt.Printf("β
Order executed successfully:\n")
fmt.Printf(" Order ticket: %d\n", result.Order)
fmt.Printf(" Deal ticket: %d\n", result.Deal)
fmt.Printf(" Price: %.5f\n", result.Price)
fmt.Printf(" Volume: %.2f\n", result.Volume)
Advantage over Low-Level:
Returns *OrderResult (clean Go struct) instead of *pb.OrderSendData (protobuf). Same code structure, but cleaner types.
Example 2: Validating Order Before PlacementΒΆ
// β BEFORE (MT5Account) - nested MqlTradeCheckResult structure:
checkReq := &pb.OrderCheckRequest{
Symbol: "EURUSD",
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: 1.0,
}
// Low-level call
checkData, err := account.OrderCheck(ctx, checkReq)
if err != nil {
return err
}
// β Need to access nested structure
result := checkData.MqlTradeCheckResult
// Check validation result
if result.ReturnedCode != 0 {
fmt.Printf("β Invalid order: %s\n", result.Comment)
return nil
}
fmt.Printf("β
Valid! Margin required: %.2f, Free margin after: %.2f\n",
result.Margin, result.FreeMargin)
// β
AFTER (MT5Service) - direct OrderCheckResult:
checkReq := &pb.OrderCheckRequest{
Symbol: "EURUSD",
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: 1.0,
}
// Mid-level call - returns clean struct directly
result, err := service.CheckOrder(ctx, checkReq)
if err != nil {
return err
}
// Check validation result
if result.ReturnedCode != 0 {
fmt.Printf("β Invalid order: %s\n", result.Comment)
return nil
}
fmt.Printf("β
Valid! Margin required: %.2f, Free margin after: %.2f\n",
result.Margin, result.MarginFree)
Code reduction: 15% (removed checkData.MqlTradeCheckResult indirection)
Example 3: Calculating Margin Before Opening PositionΒΆ
// β BEFORE (MT5Account) - extract .Margin from data:
// 1. Create margin calculation request
req := &pb.OrderCalcMarginRequest{
Symbol: "EURUSD",
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: 1.0,
Price: 0,
}
// 2. Calculate required margin
data, err := account.OrderCalcMargin(ctx, req)
if err != nil {
return err
}
requiredMargin := data.Margin // β unpacking
// 3. Get current free margin
freeMarginData, _ := account.AccountInfoDouble(ctx, &pb.AccountInfoDoubleRequest{
PropertyId: pb.AccountInfoDoublePropertyType_ACCOUNT_MARGIN_FREE,
})
freeMargin := freeMarginData.GetRequestedValue() // β unpacking
// 4. Check sufficiency
if freeMargin < requiredMargin {
fmt.Printf("β Insufficient margin: %.2f (need: %.2f)\n", freeMargin, requiredMargin)
}
// β
AFTER (MT5Service) - returns float64 directly:
// 1. Create margin calculation request
req := &pb.OrderCalcMarginRequest{
Symbol: "EURUSD",
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: 1.0,
Price: 0,
}
// 2. Calculate required margin (returns float64 directly)
requiredMargin, err := service.CalculateMargin(ctx, req)
if err != nil {
return err
}
// 3. Get current free margin (also returns float64 directly)
freeMargin, _ := service.GetAccountDouble(ctx,
pb.AccountInfoDoublePropertyType_ACCOUNT_MARGIN_FREE)
// 4. Check sufficiency
if freeMargin < requiredMargin {
fmt.Printf("β Insufficient margin: %.2f (need: %.2f)\n",
freeMargin, requiredMargin)
}
Code reduction: 25% (no data.Margin and .GetRequestedValue() calls)
Example 4: Complete Pre-Placement ValidationΒΆ
// β
MT5Service - compact and clear code
func SafePlaceOrder(service *mt5.MT5Service, symbol string, volume float64) error {
ctx := context.Background()
// 1. Validate order
checkReq := &pb.OrderCheckRequest{
Symbol: symbol,
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: volume,
}
// Run validation check
checkResult, err := service.CheckOrder(ctx, checkReq)
if err != nil {
return fmt.Errorf("order check failed: %w", err)
}
// Check validation result code
if checkResult.ReturnedCode != 0 {
return fmt.Errorf("invalid order: %s", checkResult.Comment)
}
// Display predicted account state
fmt.Printf("β
Order valid:\n")
fmt.Printf(" Margin required: %.2f\n", checkResult.Margin)
fmt.Printf(" Free margin after: %.2f\n", checkResult.MarginFree)
fmt.Printf(" Margin level after: %.2f%%\n", checkResult.MarginLevel)
// 2. Place order
orderReq := &pb.OrderSendRequest{
Symbol: symbol,
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_DEAL,
Type: pb.ENUM_ORDER_TYPE_ORDER_TYPE_BUY,
Volume: volume,
Price: 0,
Deviation: 10,
}
// Send order to broker
result, err := service.PlaceOrder(ctx, orderReq)
if err != nil {
return fmt.Errorf("order placement failed: %w", err)
}
// Check execution result
if result.ReturnedCode != 10009 {
return fmt.Errorf("order rejected: %s", result.Comment)
}
// Display execution details
fmt.Printf("β
Order placed successfully:\n")
fmt.Printf(" Order: %d\n", result.Order)
fmt.Printf(" Deal: %d\n", result.Deal)
fmt.Printf(" Price: %.5f\n", result.Price)
fmt.Printf(" Volume: %.2f\n", result.Volume)
return nil
}
Example 5: Modifying SL/TP of Existing PositionΒΆ
// β
MT5Service - position modification
func ModifyPositionSLTP(service *mt5.MT5Service, ticket uint64, sl, tp float64) error {
ctx := context.Background()
// 1. Create modification request
req := &pb.OrderModifyRequest{
Position: ticket,
Action: pb.ENUM_TRADE_REQUEST_ACTIONS_TRADE_ACTION_SLTP,
Sl: sl,
Tp: tp,
}
// 2. Send modification request
result, err := service.ModifyOrder(ctx, req)
if err != nil {
return fmt.Errorf("modify failed: %w", err)
}
// 3. Check modification result
if result.ReturnedCode != 10009 {
return fmt.Errorf("modify rejected: %s", result.Comment)
}
// 4. Success
fmt.Printf("β
Position %d modified: SL=%.5f, TP=%.5f\n", ticket, sl, tp)
return nil
}
π§ When to UseΒΆ
β PlaceOrderΒΆ
Use when:
- Opening market positions (BUY/SELL)
- Placing pending orders (Limit/Stop)
- Need Deal and Order ticket information
Example:
result, _ := service.PlaceOrder(ctx, orderReq)
if result.ReturnedCode == 10009 {
fmt.Printf("Deal: %d\n", result.Deal)
}
β CheckOrderΒΆ
Use when:
- Validating order before placement
- Need information about future account state
- Calculating margin level after execution
Example:
checkResult, _ := service.CheckOrder(ctx, checkReq)
if checkResult.MarginLevel < 200.0 {
return fmt.Errorf("margin level too low")
}
β CalculateMargin / CalculateProfitΒΆ
Use when:
- Calculating required margin for position
- Estimating potential profit/loss
- Performing risk management calculations
Example:
margin, _ := service.CalculateMargin(ctx, marginReq)
profit, _ := service.CalculateProfit(ctx, profitReq)
riskRewardRatio := profit / (margin * 0.02) // 2% risk
π Performance ComparisonΒΆ
| Task | Low-Level (lines of code) | Mid-Level (lines of code) | Reduction |
|---|---|---|---|
| Place order | 15-20 lines | 15-20 lines | 0% (but cleaner types) |
| Check order | 8-10 lines | 6-8 lines | 15-25% |
| Calculate margin | 4-5 lines | 2-3 lines | 25-40% |
| Full validation + placement | 30-35 lines | 25-30 lines | 15-20% |
Conclusion: Code is not necessarily shorter, but significantly cleaner and more understandable.
π‘ RecommendationsΒΆ
- Always use CheckOrder before PlaceOrder in production
- Check ReturnedCode - 10009 = success, everything else = error
- Use CalculateMargin to verify margin before opening position
- Read Comment in OrderResult/OrderCheckResult for error details
- For closing positions use CloseOrder instead of PlaceOrder
Return codes:
10009- TRADE_RETCODE_DONE (success)10004- TRADE_RETCODE_REQUOTE (requote)10006- TRADE_RETCODE_REJECT (rejected)10013- TRADE_RETCODE_INVALID_PRICE (invalid price)10014- TRADE_RETCODE_INVALID_STOPS (invalid SL/TP)10019- TRADE_RETCODE_NO_MONEY (insufficient funds)
π Related SectionsΒΆ
- MT5Service Overview - mid-level API overview
- Account Methods (Mid-Level) - account methods
- Symbol Methods (Mid-Level) - getting prices and symbol parameters
- Position & Orders Methods (Mid-Level) - getting position list
- Position & Orders Information (Low-Level) - low-level position/orders API
- Trading Operations (Low-Level) - low-level trading API
- MT5Account Master Overview - low-level API master reference
π― SummaryΒΆ
MT5Service Trading methods solve the main task - remove protobuf ceremony:
- β No need to unpack
data.MqlTradeCheckResult - β No need to call
.Marginor.GetRequestedValue() - β
Get clean DTOs (
*OrderResult,*OrderCheckResult) - β Code reads like normal Go
- β All trading operations in one place