Skip to content

βœ… Can Open Position (CanOpenPosition)ΒΆ

Sugar method: Comprehensive validation check - verifies if you can open a position with specified volume BEFORE attempting to trade.

API Information:

  • Method: sugar.CanOpenPosition(symbol, volume)
  • Package: mt5 (MT5Sugar)
  • Underlying calls: IsSymbolAvailable(), GetSymbolInfo(), GetFreeMargin(), service.CalculateMargin()
  • Timeout: 5 seconds
  • Returns: Three values (bool, string, error)

πŸ“‹ Method SignatureΒΆ

func (s *MT5Sugar) CanOpenPosition(symbol string, volume float64) (bool, string, error)

πŸ”½ InputΒΆ

Parameter Type Description
symbol string Trading symbol (e.g., "EURUSD", "GBPUSD")
volume float64 Desired lot size (e.g., 0.1, 1.0)

⬆️ OutputΒΆ

Return Type Description
can bool true if position can be opened, false if not
reason string Explanation if can't open (empty string if can open)
error error Error if validation check itself failed

Return patterns:

// Success - can open
(true, "", nil)

// Blocked - validation failed (not an error, just can't open)
(false, "insufficient margin: need 500.00, have 300.00", nil)
(false, "volume 0.15 not a multiple of step 0.01", nil)
(false, "symbol INVALID is not available", nil)

// Error - validation check failed
(false, "", fmt.Errorf("connection timeout"))


πŸ’¬ Just the EssentialsΒΆ

  • What it is: Pre-flight check before trading - validates EVERYTHING.
  • Why you need it: ALWAYS call this before trading to avoid order rejections.
  • Sanity check: Checks 6 things: symbol availability, volume limits, volume step, margin requirement, free margin sufficiency.

🎯 Validation Checks Performed¢

This method validates:

  1. βœ… Symbol availability - Is symbol tradeable?

  2. βœ… Volume minimum - Is volume >= VolumeMin?

  3. βœ… Volume maximum - Is volume <= VolumeMax?

  4. βœ… Volume step - Is volume a multiple of VolumeStep?

  5. βœ… Margin calculation - Can we calculate required margin?

  6. βœ… Free margin - Do we have enough free margin?

If ANY check fails β†’ returns (false, reason, nil)


πŸ”— Usage ExamplesΒΆ

1) Basic usage - validate before tradingΒΆ

symbol := "EURUSD"
volume := 0.1

canOpen, reason, err := sugar.CanOpenPosition(symbol, volume)
if err != nil {
    fmt.Printf("Validation error: %v\n", err)
    return
}

if !canOpen {
    fmt.Printf("❌ Cannot open position: %s\n", reason)
    return
}

fmt.Println("βœ… Validation passed - opening position...")
ticket, _ := sugar.BuyMarket(symbol, volume)
fmt.Printf("Position #%d opened\n", ticket)

2) Complete trading workflow with validationΒΆ

symbol := "EURUSD"
riskPercent := 2.0
stopLoss := 50.0
takeProfit := 100.0

// Step 1: Calculate position size
lotSize, err := sugar.CalculatePositionSize(symbol, riskPercent, stopLoss)
if err != nil {
    fmt.Printf("Position size calculation failed: %v\n", err)
    return
}

// Step 2: Validate BEFORE trading
canOpen, reason, err := sugar.CanOpenPosition(symbol, lotSize)
if err != nil {
    fmt.Printf("Validation error: %v\n", err)
    return
}

if !canOpen {
    fmt.Printf("❌ Cannot open position: %s\n", reason)
    fmt.Printf("   Symbol:   %s\n", symbol)
    fmt.Printf("   Lot size: %.2f\n", lotSize)
    return
}

// Step 3: All checks passed - safe to trade
fmt.Println("βœ… All validations passed")
ticket, err := sugar.BuyMarketWithPips(symbol, lotSize, stopLoss, takeProfit)
if err != nil {
    fmt.Printf("Order failed: %v\n", err)
    return
}

fmt.Printf("βœ… Position #%d opened successfully\n", ticket)
fmt.Printf("   Lot size: %.2f\n", lotSize)
fmt.Printf("   SL: %.0f pips, TP: %.0f pips\n", stopLoss, takeProfit)

3) Handle different failure reasonsΒΆ

func OpenPositionSafely(sugar *mt5.MT5Sugar, symbol string, volume float64) {
    canOpen, reason, err := sugar.CanOpenPosition(symbol, volume)

    if err != nil {
        fmt.Printf("πŸ”΄ Validation check failed: %v\n", err)
        return
    }

    if !canOpen {
        // Parse the reason and provide helpful feedback
        if strings.Contains(reason, "margin") {
            fmt.Printf("πŸ’° Insufficient margin: %s\n", reason)
            fmt.Println("   β†’ Reduce position size or close other positions")
        } else if strings.Contains(reason, "minimum") {
            fmt.Printf("πŸ“‰ Volume too small: %s\n", reason)
            info, _ := sugar.GetSymbolInfo(symbol)
            fmt.Printf("   β†’ Minimum lot size: %.2f\n", info.VolumeMin)
        } else if strings.Contains(reason, "maximum") {
            fmt.Printf("πŸ“ˆ Volume too large: %s\n", reason)
            info, _ := sugar.GetSymbolInfo(symbol)
            fmt.Printf("   β†’ Maximum lot size: %.2f\n", info.VolumeMax)
        } else if strings.Contains(reason, "step") {
            fmt.Printf("βš™οΈ  Invalid volume step: %s\n", reason)
            info, _ := sugar.GetSymbolInfo(symbol)
            fmt.Printf("   β†’ Volume must be multiple of %.2f\n", info.VolumeStep)
        } else if strings.Contains(reason, "available") {
            fmt.Printf("❌ Symbol unavailable: %s\n", reason)
            fmt.Println("   β†’ Check symbol name or market hours")
        } else {
            fmt.Printf("⚠️  Cannot open: %s\n", reason)
        }
        return
    }

    // Validation passed - open position
    fmt.Println("βœ… Opening position...")
    ticket, _ := sugar.BuyMarket(symbol, volume)
    fmt.Printf("   Position #%d opened\n", ticket)
}

// Usage:
OpenPositionSafely(sugar, "EURUSD", 0.1)

4) Validate multiple positions before batch tradingΒΆ

type TradeRequest struct {
    Symbol string
    Volume float64
}

func ValidateMultiplePositions(
    sugar *mt5.MT5Sugar,
    requests []TradeRequest,
) []TradeRequest {
    validRequests := []TradeRequest{}

    fmt.Println("Validating positions...")
    fmt.Println("─────────────────────────────────────────")

    for i, req := range requests {
        canOpen, reason, err := sugar.CanOpenPosition(req.Symbol, req.Volume)

        if err != nil {
            fmt.Printf("%d. %s %.2f lots - ⚠️  Error: %v\n",
                i+1, req.Symbol, req.Volume, err)
            continue
        }

        if !canOpen {
            fmt.Printf("%d. %s %.2f lots - ❌ %s\n",
                i+1, req.Symbol, req.Volume, reason)
            continue
        }

        fmt.Printf("%d. %s %.2f lots - βœ… Valid\n",
            i+1, req.Symbol, req.Volume)
        validRequests = append(validRequests, req)
    }

    fmt.Println("─────────────────────────────────────────")
    fmt.Printf("Valid: %d/%d positions\n", len(validRequests), len(requests))

    return validRequests
}

// Usage:
requests := []TradeRequest{
    {"EURUSD", 0.1},
    {"GBPUSD", 0.2},
    {"USDJPY", 0.15},
    {"INVALID", 0.1},
}

validRequests := ValidateMultiplePositions(sugar, requests)

// Open only valid positions
for _, req := range validRequests {
    ticket, _ := sugar.BuyMarket(req.Symbol, req.Volume)
    fmt.Printf("Opened %s: #%d\n", req.Symbol, ticket)
}

5) Pre-trade checklist displayΒΆ

func ShowPreTradeChecklist(
    sugar *mt5.MT5Sugar,
    symbol string,
    volume float64,
) bool {
    fmt.Println("╔═══════════════════════════════════════╗")
    fmt.Println("β•‘      PRE-TRADE VALIDATION CHECK       β•‘")
    fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
    fmt.Printf("Symbol:     %s\n", symbol)
    fmt.Printf("Lot size:   %.2f\n\n", volume)

    // Check symbol availability
    available, _ := sugar.IsSymbolAvailable(symbol)
    if available {
        fmt.Println("βœ… Symbol available")
    } else {
        fmt.Println("❌ Symbol not available")
        return false
    }

    // Get symbol info
    info, _ := sugar.GetSymbolInfo(symbol)

    // Check volume minimum
    if volume >= info.VolumeMin {
        fmt.Printf("βœ… Volume >= minimum (%.2f)\n", info.VolumeMin)
    } else {
        fmt.Printf("❌ Volume < minimum (%.2f)\n", info.VolumeMin)
        return false
    }

    // Check volume maximum
    if volume <= info.VolumeMax {
        fmt.Printf("βœ… Volume <= maximum (%.2f)\n", info.VolumeMax)
    } else {
        fmt.Printf("❌ Volume > maximum (%.2f)\n", info.VolumeMax)
        return false
    }

    // Check volume step
    steps := volume / info.VolumeStep
    if steps == float64(int(steps)) {
        fmt.Printf("βœ… Volume is multiple of step (%.2f)\n", info.VolumeStep)
    } else {
        fmt.Printf("❌ Volume not multiple of step (%.2f)\n", info.VolumeStep)
        return false
    }

    // Check margin
    requiredMargin, _ := sugar.CalculateRequiredMargin(symbol, volume)
    freeMargin, _ := sugar.GetFreeMargin()

    fmt.Printf("   Required margin: $%.2f\n", requiredMargin)
    fmt.Printf("   Free margin:     $%.2f\n", freeMargin)

    if requiredMargin <= freeMargin {
        fmt.Println("βœ… Sufficient margin")
    } else {
        fmt.Println("❌ Insufficient margin")
        return false
    }

    fmt.Println("\nβœ… ALL CHECKS PASSED - Ready to trade!")
    return true
}

// Usage:
if ShowPreTradeChecklist(sugar, "EURUSD", 0.1) {
    ticket, _ := sugar.BuyMarket("EURUSD", 0.1)
    fmt.Printf("\nPosition opened: #%d\n", ticket)
}

6) Retry with adjusted volumeΒΆ

func OpenPositionWithAdjustment(
    sugar *mt5.MT5Sugar,
    symbol string,
    initialVolume float64,
) (uint64, error) {
    volume := initialVolume

    // Try with initial volume
    canOpen, reason, err := sugar.CanOpenPosition(symbol, volume)
    if err != nil {
        return 0, err
    }

    if canOpen {
        fmt.Printf("βœ… Opening %.2f lots...\n", volume)
        return sugar.BuyMarket(symbol, volume)
    }

    // If insufficient margin, try with reduced volume
    if strings.Contains(reason, "margin") {
        fmt.Printf("⚠️  Insufficient margin for %.2f lots\n", volume)

        // Get max capacity
        maxLots, _ := sugar.GetMaxLotSize(symbol)
        if maxLots == 0 {
            return 0, fmt.Errorf("no margin available")
        }

        // Try with max capacity
        volume = maxLots
        fmt.Printf("   Trying with reduced volume: %.2f lots\n", volume)

        canOpen, reason, err = sugar.CanOpenPosition(symbol, volume)
        if err != nil {
            return 0, err
        }

        if canOpen {
            return sugar.BuyMarket(symbol, volume)
        }
    }

    return 0, fmt.Errorf("cannot open position: %s", reason)
}

// Usage:
ticket, err := OpenPositionWithAdjustment(sugar, "EURUSD", 1.0)
if err != nil {
    fmt.Printf("Failed: %v\n", err)
} else {
    fmt.Printf("Success: #%d\n", ticket)
}

7) Comprehensive validation wrapperΒΆ

type ValidationResult struct {
    CanOpen       bool
    Reason        string
    RequiredMargin float64
    FreeMargin     float64
    MarginRatio    float64
}

func ValidatePositionDetailed(
    sugar *mt5.MT5Sugar,
    symbol string,
    volume float64,
) (*ValidationResult, error) {
    result := &ValidationResult{}

    // Run validation
    canOpen, reason, err := sugar.CanOpenPosition(symbol, volume)
    if err != nil {
        return nil, err
    }

    result.CanOpen = canOpen
    result.Reason = reason

    // Get margin details
    requiredMargin, _ := sugar.CalculateRequiredMargin(symbol, volume)
    freeMargin, _ := sugar.GetFreeMargin()

    result.RequiredMargin = requiredMargin
    result.FreeMargin = freeMargin

    if freeMargin > 0 {
        result.MarginRatio = (requiredMargin / freeMargin) * 100
    }

    return result, nil
}

func ShowValidationReport(result *ValidationResult, symbol string, volume float64) {
    fmt.Println("╔═══════════════════════════════════════════════════════╗")
    fmt.Println("β•‘          POSITION VALIDATION REPORT                   β•‘")
    fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
    fmt.Printf("Symbol:            %s\n", symbol)
    fmt.Printf("Desired volume:    %.2f lots\n\n", volume)

    fmt.Printf("Required margin:   $%.2f\n", result.RequiredMargin)
    fmt.Printf("Available margin:  $%.2f\n", result.FreeMargin)
    fmt.Printf("Margin usage:      %.1f%%\n\n", result.MarginRatio)

    if result.CanOpen {
        fmt.Println("βœ… VALIDATION PASSED")
        fmt.Println("   Position can be opened")

        if result.MarginRatio > 70 {
            fmt.Println("\n⚠️  Warning: High margin usage (>70%)")
            fmt.Println("   Consider reducing position size")
        }
    } else {
        fmt.Println("❌ VALIDATION FAILED")
        fmt.Printf("   Reason: %s\n", result.Reason)
    }
}

// Usage:
result, err := ValidatePositionDetailed(sugar, "EURUSD", 0.5)
if err != nil {
    fmt.Printf("Validation error: %v\n", err)
    return
}

ShowValidationReport(result, "EURUSD", 0.5)

if result.CanOpen {
    ticket, _ := sugar.BuyMarket("EURUSD", 0.5)
    fmt.Printf("\nPosition opened: #%d\n", ticket)
}

8) Margin safety validatorΒΆ

func ValidateWithMarginSafety(
    sugar *mt5.MT5Sugar,
    symbol string,
    volume float64,
    maxMarginPercent float64,
) (bool, string) {
    // First, standard validation
    canOpen, reason, err := sugar.CanOpenPosition(symbol, volume)
    if err != nil {
        return false, fmt.Sprintf("validation error: %v", err)
    }

    if !canOpen {
        return false, reason
    }

    // Additional check: margin usage threshold
    requiredMargin, _ := sugar.CalculateRequiredMargin(symbol, volume)
    freeMargin, _ := sugar.GetFreeMargin()

    marginUsage := (requiredMargin / freeMargin) * 100

    if marginUsage > maxMarginPercent {
        return false, fmt.Sprintf(
            "margin usage %.1f%% exceeds limit %.1f%%",
            marginUsage, maxMarginPercent,
        )
    }

    return true, ""
}

// Usage with 50% margin limit:
canOpen, reason := ValidateWithMarginSafety(sugar, "EURUSD", 0.5, 50.0)
if !canOpen {
    fmt.Printf("❌ Cannot open: %s\n", reason)
} else {
    fmt.Println("βœ… Validation passed with margin safety check")
    ticket, _ := sugar.BuyMarket("EURUSD", 0.5)
    fmt.Printf("Position opened: #%d\n", ticket)
}

9) Batch validation with statisticsΒΆ

func BatchValidateAndReport(sugar *mt5.MT5Sugar, requests []TradeRequest) {
    passCount := 0
    failCount := 0
    errorCount := 0

    failureReasons := make(map[string]int)

    fmt.Println("╔═══════════════════════════════════════════════════════╗")
    fmt.Println("β•‘          BATCH VALIDATION REPORT                      β•‘")
    fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
    fmt.Printf("Total requests: %d\n\n", len(requests))

    for i, req := range requests {
        canOpen, reason, err := sugar.CanOpenPosition(req.Symbol, req.Volume)

        status := ""
        if err != nil {
            status = fmt.Sprintf("⚠️  Error: %v", err)
            errorCount++
        } else if canOpen {
            status = "βœ… Pass"
            passCount++
        } else {
            status = fmt.Sprintf("❌ %s", reason)
            failCount++

            // Track failure reason
            failureReasons[reason]++
        }

        fmt.Printf("%d. %s %.2f lots - %s\n",
            i+1, req.Symbol, req.Volume, status)
    }

    fmt.Println("\n─────────────────────────────────────────────────────────")
    fmt.Printf("βœ… Passed:  %d (%.1f%%)\n",
        passCount, float64(passCount)/float64(len(requests))*100)
    fmt.Printf("❌ Failed:  %d (%.1f%%)\n",
        failCount, float64(failCount)/float64(len(requests))*100)
    fmt.Printf("⚠️  Errors: %d\n", errorCount)

    if len(failureReasons) > 0 {
        fmt.Println("\nFailure breakdown:")
        for reason, count := range failureReasons {
            fmt.Printf("  β€’ %s: %d times\n", reason, count)
        }
    }
}

// Usage:
requests := []TradeRequest{
    {"EURUSD", 0.1},
    {"GBPUSD", 0.2},
    {"USDJPY", 100.0}, // Too large
    {"INVALID", 0.1},  // Invalid symbol
    {"XAUUSD", 0.01},
}

BatchValidateAndReport(sugar, requests)

10) Advanced position validatorΒΆ

type PositionValidator struct {
    sugar                *mt5.MT5Sugar
    maxMarginUsagePercent float64
    maxPositions         int
}

func NewPositionValidator(
    sugar *mt5.MT5Sugar,
    maxMarginPercent float64,
    maxPositions int,
) *PositionValidator {
    return &PositionValidator{
        sugar:                sugar,
        maxMarginUsagePercent: maxMarginPercent,
        maxPositions:         maxPositions,
    }
}

func (pv *PositionValidator) Validate(
    symbol string,
    volume float64,
) (bool, string, error) {
    // Check 1: Standard validation
    canOpen, reason, err := pv.sugar.CanOpenPosition(symbol, volume)
    if err != nil {
        return false, "", err
    }

    if !canOpen {
        return false, reason, nil
    }

    // Check 2: Maximum positions limit
    posCount, _ := pv.sugar.GetOpenPositionsCount()
    if posCount >= pv.maxPositions {
        return false, fmt.Sprintf(
            "maximum positions reached (%d/%d)",
            posCount, pv.maxPositions,
        ), nil
    }

    // Check 3: Margin usage threshold
    requiredMargin, _ := pv.sugar.CalculateRequiredMargin(symbol, volume)
    freeMargin, _ := pv.sugar.GetFreeMargin()

    marginUsage := (requiredMargin / freeMargin) * 100

    if marginUsage > pv.maxMarginUsagePercent {
        return false, fmt.Sprintf(
            "margin usage %.1f%% exceeds limit %.1f%%",
            marginUsage, pv.maxMarginUsagePercent,
        ), nil
    }

    // Check 4: Symbol not already in portfolio (optional rule)
    openPositions, _ := pv.sugar.GetOpenPositions()
    for _, pos := range openPositions {
        if pos.Symbol == symbol {
            return false, fmt.Sprintf(
                "position already exists for %s (ticket #%d)",
                symbol, pos.Ticket,
            ), nil
        }
    }

    return true, "", nil
}

func (pv *PositionValidator) ValidateAndExecute(
    symbol string,
    volume float64,
    direction string,
) (uint64, error) {
    canOpen, reason, err := pv.Validate(symbol, volume)
    if err != nil {
        return 0, fmt.Errorf("validation error: %w", err)
    }

    if !canOpen {
        return 0, fmt.Errorf("validation failed: %s", reason)
    }

    // Execute trade
    if direction == "BUY" {
        return pv.sugar.BuyMarket(symbol, volume)
    } else {
        return pv.sugar.SellMarket(symbol, volume)
    }
}

func (pv *PositionValidator) ShowRules() {
    fmt.Println("╔═══════════════════════════════════════╗")
    fmt.Println("β•‘      POSITION VALIDATOR RULES         β•‘")
    fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
    fmt.Printf("Max margin usage:  %.1f%%\n", pv.maxMarginUsagePercent)
    fmt.Printf("Max positions:     %d\n", pv.maxPositions)
    fmt.Println("Symbol uniqueness: Enforced")
    fmt.Println("Broker limits:     Enforced")
}

// Usage:
validator := NewPositionValidator(sugar, 50.0, 5)
validator.ShowRules()

// Validate and execute
ticket, err := validator.ValidateAndExecute("EURUSD", 0.1, "BUY")
if err != nil {
    fmt.Printf("Failed: %v\n", err)
} else {
    fmt.Printf("βœ… Position opened: #%d\n", ticket)
}

πŸ“¦ Methods used internally:

  • IsSymbolAvailable() - Check symbol is tradeable
  • GetSymbolInfo() - Get volume limits
  • GetFreeMargin() - Get available margin
  • service.CalculateMargin() - Calculate required margin

🍬 Complementary sugar methods:

  • CalculatePositionSize() - Calculate risk-based lot size ⭐
  • GetMaxLotSize() - Get maximum tradeable volume
  • CalculateRequiredMargin() - Get margin needed
  • BuyMarket() / SellMarket() - Execute trades after validation

Recommended workflow:

// 1. Calculate lot size
lotSize, _ := sugar.CalculatePositionSize("EURUSD", 2.0, 50)

// 2. ALWAYS validate before trading
canOpen, reason, _ := sugar.CanOpenPosition("EURUSD", lotSize)
if !canOpen {
    fmt.Println("Cannot trade:", reason)
    return
}

// 3. Trade
ticket, _ := sugar.BuyMarket("EURUSD", lotSize)


⚠️ Common Pitfalls¢

1) Not calling before tradingΒΆ

// ❌ WRONG - trading without validation
ticket, _ := sugar.BuyMarket("EURUSD", 0.1)
// Order might get rejected!

// βœ… CORRECT - validate first
canOpen, reason, _ := sugar.CanOpenPosition("EURUSD", 0.1)
if !canOpen {
    fmt.Println("Cannot trade:", reason)
    return
}
ticket, _ := sugar.BuyMarket("EURUSD", 0.1)

2) Ignoring the reason when falseΒΆ

// ❌ WRONG - not checking why it failed
canOpen, _, _ := sugar.CanOpenPosition("EURUSD", 0.1)
if !canOpen {
    fmt.Println("Can't trade") // Not helpful!
}

// βœ… CORRECT - use the reason
canOpen, reason, _ := sugar.CanOpenPosition("EURUSD", 0.1)
if !canOpen {
    fmt.Printf("Can't trade: %s\n", reason) // Informative!
}

3) Confusing error vs blockedΒΆ

// ❌ WRONG - treating blocked as error
canOpen, reason, err := sugar.CanOpenPosition("EURUSD", 0.1)
if err != nil || !canOpen {
    // These are different things!
}

// βœ… CORRECT - handle separately
canOpen, reason, err := sugar.CanOpenPosition("EURUSD", 0.1)
if err != nil {
    // Actual error - validation check failed
    fmt.Printf("Error: %v\n", err)
    return
}
if !canOpen {
    // Not an error - just can't open (with reason why)
    fmt.Printf("Blocked: %s\n", reason)
    return
}

4) Not checking error returnΒΆ

// ❌ WRONG - ignoring error
canOpen, reason, _ := sugar.CanOpenPosition("EURUSD", 0.1)

// βœ… CORRECT - check error
canOpen, reason, err := sugar.CanOpenPosition("EURUSD", 0.1)
if err != nil {
    fmt.Printf("Validation error: %v\n", err)
    return
}

5) Validating but not using resultΒΆ

// ❌ WRONG - validating but ignoring result
sugar.CanOpenPosition("EURUSD", 0.1)
sugar.BuyMarket("EURUSD", 0.1) // Might still fail!

// βœ… CORRECT - use validation result
canOpen, reason, _ := sugar.CanOpenPosition("EURUSD", 0.1)
if !canOpen {
    fmt.Println("Cannot trade:", reason)
    return
}
sugar.BuyMarket("EURUSD", 0.1)

πŸ’Ž Pro TipsΒΆ

  1. ALWAYS call this - Make it mandatory before every trade

  2. Check all three returns - (bool, string, error) all matter

  3. Use the reason - Provides specific feedback on what's wrong

  4. False β‰  Error - false means "can't open" (not a system error)

  5. Validate calculated sizes - Even CalculatePositionSize() results should be validated

  6. Fast check - Lightweight, no trade execution, safe to call frequently

  7. Multi-check - Validates 6 different things in one call


πŸ“Š Return Value MeaningsΒΆ

Three return values:

(true, "", nil)
β†’ βœ… All checks passed, safe to trade

(false, "reason", nil)
β†’ ❌ Can't open (not an error, just blocked)
β†’ "reason" explains what's wrong

(false, "", error)
β†’ πŸ”΄ Validation check itself failed
β†’ System error, not a trading validation issue

Common reasons when false:
- "insufficient margin: need X, have Y"
- "volume X below minimum Y"
- "volume X exceeds maximum Y"
- "volume X not a multiple of step Y"
- "symbol X is not available"

See also: CalculatePositionSize.md, GetMaxLotSize.md, CalculateRequiredMargin.md