ππ Close All Positions (CloseAllPositions)ΒΆ
Sugar method: Closes all open positions at once across all symbols.
API Information:
- Method:
sugar.CloseAllPositions() - Timeout: 10 seconds to list the positions, then 30 seconds for each close (every close has its own timeout)
- Returns: Number of positions closed, and an error when the list could not be read or when at least one close failed (the error names every failed ticket; the count is still valid)
π Method SignatureΒΆ
How it works: it reads the open positions (GetOpenedOrders(...).PositionInfos - pending orders are not touched),
sends pb.OrderCloseRequest{Ticket: pos.Ticket} for each one and counts the closes that returned code 10009.
A close that fails or is rejected does not stop the loop (it is not retried), but it is collected and reported:
the returned error reads "N of M position(s) not closed: position #T: <error>" / "position #T: close rejected,
code: C (<meaning>)", one entry per failed ticket.
func (s *MT5Sugar) CloseAllPositions() (int, error) {
return s.closePositions(func(*pb.PositionInfo) bool { return true })
}
// closePositions closes every open position the filter selects; failures are collected, not hidden.
func (s *MT5Sugar) closePositions(selected func(*pb.PositionInfo) bool) (int, error) {
listCtx, cancelList := context.WithTimeout(s.ctx, 10*time.Second)
data, err := s.service.GetOpenedOrders(listCtx, pb.BMT5_ENUM_OPENED_ORDER_SORT_TYPE_BMT5_OPENED_ORDER_SORT_BY_OPEN_TIME_ASC)
cancelList()
if err != nil {
return 0, fmt.Errorf("failed to get positions: %w", err)
}
closed := 0
var failures []error
for _, pos := range data.PositionInfos {
if !selected(pos) {
continue
}
ctx, cancel := context.WithTimeout(s.ctx, 30*time.Second)
retCode, err := s.service.CloseOrder(ctx, &pb.OrderCloseRequest{Ticket: pos.Ticket})
cancel()
switch {
case err != nil:
failures = append(failures, fmt.Errorf("position #%d: %w", pos.Ticket, err))
case retCode != 10009:
failures = append(failures, fmt.Errorf("position #%d: close rejected, code: %d (%s)", pos.Ticket, retCode, helpers.GetRetCodeMessage(retCode)))
default:
closed++
}
}
if len(failures) > 0 {
return closed, fmt.Errorf("%d of %d position(s) not closed: %w", len(failures), len(failures)+closed, errors.Join(failures...))
}
return closed, nil
}
CloseAllBySymbol(symbol) uses the same closePositions with the filter pos.Symbol == symbol.
π½ Input / β¬οΈ OutputΒΆ
| Input | Type | Description |
|---|---|---|
| None | - | No parameters required |
| Output | Type | Description |
|---|---|---|
int |
int |
Number of positions successfully closed (return code 10009) |
error |
error |
"failed to get positions: ..." if the listing fails (count 0); "N of M position(s) not closed: ..." naming every failed ticket when some closes failed (count still valid); nil when every selected position was closed |
π¬ Just the EssentialsΒΆ
- What it is: Emergency "close everything" button - closes all open positions.
- Why you need it: Emergency exit, end of trading day, major news event.
- Sanity check: Closes ALL positions - no undo! Use with caution.
err == nilmeans every position in the list was closed; with an error,closedstill tells how many were.
β οΈ DANGER ZONEΒΆ
This method closes EVERY open position!
- No confirmation prompt
- No way to undo
- Affects all symbols
- Pending orders stay in place (only positions are closed)
- Use carefully in production!
π― When to UseΒΆ
β Emergency exit - Market crash, major news, need to exit NOW
β End of day - Close all positions before weekend
β Account protection - Margin call prevention
β Strategy reset - Start fresh, close everything
β NOT for selective closing - Use ClosePosition() or CloseAllBySymbol() instead
π Usage ExamplesΒΆ
1) Basic usage - emergency closeΒΆ
fmt.Println("β οΈ EMERGENCY: Closing all positions!")
closed, err := sugar.CloseAllPositions()
if err != nil {
// listing failed, or some closes failed (the error names each ticket)
fmt.Printf("β Closed %d, but: %v\n", closed, err)
return
}
fmt.Printf("β
Closed %d positions\n", closed)
2) Close all with confirmationΒΆ
positions, _ := sugar.GetOpenPositions()
if len(positions) == 0 {
fmt.Println("No positions to close")
return
}
fmt.Printf("β οΈ WARNING: About to close %d positions\n", len(positions))
// Show what will be closed
totalProfit := 0.0
for _, pos := range positions {
fmt.Printf(" - #%d: %s %.2f lots ($%.2f)\n",
pos.Ticket, pos.Symbol, pos.Volume, pos.Profit)
totalProfit += pos.Profit
}
fmt.Printf("\nTotal P/L: $%.2f\n", totalProfit)
fmt.Println("\nClosing all positions in 3 seconds...")
time.Sleep(3 * time.Second)
closed, err := sugar.CloseAllPositions()
if err != nil {
fmt.Printf("β οΈ Closed %d of %d positions: %v\n", closed, len(positions), err)
} else {
fmt.Printf("β
Closed %d of %d positions (P/L before closing: $%.2f)\n",
closed, len(positions), totalProfit)
}
3) End of trading day routineΒΆ
func EndOfDayClose(sugar *mt5.MT5Sugar) {
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
fmt.Println("β END OF DAY ROUTINE β")
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
// Check current positions
positions, _ := sugar.GetOpenPositions()
fmt.Printf("Open positions: %d\n", len(positions))
if len(positions) == 0 {
fmt.Println("β
No positions to close - ready for weekend")
return
}
// Show summary
var totalProfit float64
buyCount, sellCount := 0, 0
for _, pos := range positions {
totalProfit += pos.Profit
if pos.Type == pb.BMT5_ENUM_POSITION_TYPE_BMT5_POSITION_TYPE_BUY {
buyCount++
} else {
sellCount++
}
}
fmt.Printf("\nSummary:\n")
fmt.Printf(" BUY positions: %d\n", buyCount)
fmt.Printf(" SELL positions: %d\n", sellCount)
fmt.Printf(" Total P/L: $%.2f\n", totalProfit)
// Close all
fmt.Println("\nClosing all positions...")
closed, err := sugar.CloseAllPositions()
if err != nil {
// names every position that was not closed
fmt.Printf("β οΈ Only %d of %d positions closed: %v\n", closed, len(positions), err)
return
}
fmt.Println("β
All positions closed - have a nice weekend!")
}
// Usage: Run at 16:55 on Friday
EndOfDayClose(sugar)
4) Close all on drawdown thresholdΒΆ
maxDrawdown := -500.0 // $500 max loss
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for range ticker.C {
equity, _ := sugar.GetEquity()
profit, _ := sugar.GetProfit()
fmt.Printf("Equity: $%.2f, Floating P/L: $%.2f\n", equity, profit)
if profit <= maxDrawdown {
fmt.Printf("π¨ DRAWDOWN LIMIT REACHED: $%.2f\n", profit)
fmt.Println("Closing all positions NOW!")
closed, err := sugar.CloseAllPositions()
if err != nil {
fmt.Printf("β %d closed, some not: %v\n", closed, err)
} else {
fmt.Printf("β
%d positions closed - loss limited\n", closed)
}
return
}
}
5) Close all before major newsΒΆ
// News event in 5 minutes
newsTime := time.Now().Add(5 * time.Minute)
fmt.Printf("Major news at %s - closing all positions\n",
newsTime.Format("15:04"))
positions, _ := sugar.GetOpenPositions()
if len(positions) > 0 {
fmt.Printf("Closing %d positions before news...\n", len(positions))
closed, err := sugar.CloseAllPositions()
if err != nil {
fmt.Printf("β %d of %d closed: %v\n", closed, len(positions), err)
return
}
fmt.Printf("β
All %d positions closed - ready for news event\n", closed)
} else {
fmt.Println("No positions to close")
}
6) Close all and show summaryΒΆ
positions, _ := sugar.GetOpenPositions()
if len(positions) == 0 {
fmt.Println("No positions to close")
return
}
fmt.Printf("Closing %d positions:\n", len(positions))
// Record positions before closing
type ClosedPosition struct {
Ticket uint64
Symbol string
Type string
Volume float64
Profit float64
}
var closedPositions []ClosedPosition
for _, pos := range positions {
posType := "SELL"
if pos.Type == pb.BMT5_ENUM_POSITION_TYPE_BMT5_POSITION_TYPE_BUY {
posType = "BUY"
}
closedPositions = append(closedPositions, ClosedPosition{
Ticket: pos.Ticket,
Symbol: pos.Symbol,
Type: posType,
Volume: pos.Volume,
Profit: pos.Profit,
})
}
// Close all
closed, err := sugar.CloseAllPositions()
if err != nil {
// the error names the tickets that were not closed; closed is still valid
fmt.Printf("Error: %v\n", err)
}
// Show what was requested (closed is less than len(closedPositions) when err != nil)
fmt.Printf("\nβ
Closed %d of %d positions:\n", closed, len(closedPositions))
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
totalProfit := 0.0
for _, pos := range closedPositions {
fmt.Printf("#%d: %s %s %.2f lots - $%.2f\n",
pos.Ticket, pos.Symbol, pos.Type, pos.Volume, pos.Profit)
totalProfit += pos.Profit
}
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
fmt.Printf("Total P/L: $%.2f\n", totalProfit)
7) Close all with error handlingΒΆ
positions, _ := sugar.GetOpenPositions()
totalPositions := len(positions)
if totalPositions == 0 {
fmt.Println("No positions to close")
return
}
fmt.Printf("Attempting to close %d positions...\n", totalPositions)
closed, err := sugar.CloseAllPositions()
fmt.Printf("CloseAllPositions reported %d closed\n", closed)
// Verify all closed (err, printed below, names every failed close)
time.Sleep(2 * time.Second)
remainingPositions, _ := sugar.GetOpenPositions()
if len(remainingPositions) == 0 {
fmt.Printf("β
Success: All %d positions closed\n", totalPositions)
} else {
fmt.Printf("β οΈ Warning: %d positions still open\n", len(remainingPositions))
fmt.Println("Remaining positions:")
for _, pos := range remainingPositions {
fmt.Printf(" - #%d: %s %.2f lots\n", pos.Ticket, pos.Symbol, pos.Volume)
}
}
if err != nil {
fmt.Printf("Error occurred: %v\n", err)
}
8) Scheduled close all (cron-style)ΒΆ
func ScheduledCloseAll(sugar *mt5.MT5Sugar, closeTime string) {
fmt.Printf("Scheduled close at %s\n", closeTime)
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
for range ticker.C {
now := time.Now().Format("15:04")
if now == closeTime {
fmt.Printf("π %s - Closing all positions\n", closeTime)
positions, _ := sugar.GetOpenPositions()
if len(positions) > 0 {
closed, err := sugar.CloseAllPositions()
if err != nil {
fmt.Printf("%d of %d closed: %v\n", closed, len(positions), err)
} else {
fmt.Printf("β
Closed %d of %d positions\n", closed, len(positions))
}
} else {
fmt.Println("No positions to close")
}
return
}
fmt.Printf("Waiting... (current: %s, target: %s)\n", now, closeTime)
}
}
// Usage: Close all positions at 16:55
ScheduledCloseAll(sugar, "16:55")
9) Close all on margin levelΒΆ
dangerMarginLevel := 150.0 // 150%
ticker := time.NewTicker(15 * time.Second)
defer ticker.Stop()
for range ticker.C {
marginLevel, _ := sugar.GetMarginLevel()
fmt.Printf("Margin Level: %.0f%%\n", marginLevel)
if marginLevel < dangerMarginLevel {
fmt.Printf("π¨ DANGER: Margin level %.0f%% < %.0f%%\n",
marginLevel, dangerMarginLevel)
positions, _ := sugar.GetOpenPositions()
fmt.Printf("Closing all %d positions to protect account!\n", len(positions))
closed, err := sugar.CloseAllPositions()
if err != nil {
fmt.Printf("β EMERGENCY CLOSE INCOMPLETE (%d closed): %v\n", closed, err)
} else {
fmt.Printf("β
%d of %d positions closed\n", closed, len(positions))
}
return
}
}
10) Advanced close all with retryΒΆ
func CloseAllPositionsWithRetry(sugar *mt5.MT5Sugar, maxRetries int) error {
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
fmt.Println("β CLOSING ALL POSITIONS β")
fmt.Println("βββββββββββββββββββββββββββββββββββββββββ")
positions, _ := sugar.GetOpenPositions()
if len(positions) == 0 {
fmt.Println("No positions to close")
return nil
}
initialCount := len(positions)
fmt.Printf("Positions to close: %d\n\n", initialCount)
for attempt := 1; attempt <= maxRetries; attempt++ {
fmt.Printf("Attempt %d/%d...\n", attempt, maxRetries)
if _, err := sugar.CloseAllPositions(); err != nil {
fmt.Printf(" Not all closed: %v\n", err)
}
// Check how many closed
time.Sleep(2 * time.Second)
remainingPositions, _ := sugar.GetOpenPositions()
closedCount := initialCount - len(remainingPositions)
fmt.Printf(" Closed: %d/%d\n", closedCount, initialCount)
if len(remainingPositions) == 0 {
fmt.Println("\nβ
All positions closed successfully!")
return nil
}
if attempt < maxRetries {
fmt.Printf(" %d positions still open - retrying...\n\n", len(remainingPositions))
time.Sleep(2 * time.Second)
}
}
// Final check
finalPositions, _ := sugar.GetOpenPositions()
return fmt.Errorf("failed to close all positions - %d still open", len(finalPositions))
}
// Usage:
err := CloseAllPositionsWithRetry(sugar, 3)
π Related MethodsΒΆ
π¬ Selective close methods:
ClosePosition()- Close single positionCloseAllBySymbol()- Close all positions for specific symbol
π¬ Position info:
GetOpenPositions()- See what will be closedGetProfit()/GetTotalProfit()- Check total floating P/L before closing
β οΈ Common PitfallsΒΆ
1) Not checking what you're closingΒΆ
// β WRONG - blindly closing everything
sugar.CloseAllPositions()
// β
CORRECT - check first
positions, _ := sugar.GetOpenPositions()
fmt.Printf("About to close %d positions\n", len(positions))
// Show details, then close
2) No verification after closeΒΆ
// β WRONG - assuming success (ignores the error that names failed closes)
sugar.CloseAllPositions()
fmt.Println("Done!")
// β
CORRECT - check the error, then verify
closed, err := sugar.CloseAllPositions()
fmt.Printf("Closed %d positions\n", closed)
if err != nil {
fmt.Printf("β οΈ %v\n", err)
}
time.Sleep(2 * time.Second)
remaining, _ := sugar.GetOpenPositions()
if len(remaining) > 0 {
fmt.Printf("β οΈ %d positions still open!\n", len(remaining))
}
3) Using in loops/triggers without protectionΒΆ
// β WRONG - might trigger multiple times
if someCondition {
sugar.CloseAllPositions() // Could run every tick!
}
// β
CORRECT - add flag
var alreadyClosed bool
if someCondition && !alreadyClosed {
sugar.CloseAllPositions()
alreadyClosed = true
}
π Pro TipsΒΆ
-
Always preview - Check what you're closing first
-
Record P/L - Save profit/loss before closing
-
Verify closure - Check no positions remain after
-
Use sparingly - This is an emergency tool
-
Add confirmation - Especially in production systems
π¨ Production WarningΒΆ
In production:
- Add multiple confirmations before closing all
- Log all positions before closing
- Implement rate limiting (don't allow rapid repeated calls)
- Consider user confirmation for manual systems
- Have rollback plan if needed
See also: ClosePosition.md, GetOpenPositions.md