Skip to content

πŸ“…πŸŽ― Get Deals Date Range (GetDealsDateRange)ΒΆ

Sugar method: Returns closed positions ("deals") whose open time lies within a custom date range.

API Information:

  • Method: sugar.GetDealsDateRange(from, to)
  • Timeout: 30 seconds
  • Returns: Slice of position history info (one entry per closed position; a position closed in parts has one entry per partial close), sorted by open time (oldest first)

πŸ“‹ Method SignatureΒΆ

func (s *MT5Sugar) GetDealsDateRange(from, to time.Time) ([]*pb.PositionHistoryInfo, error)

How it works:

func (s *MT5Sugar) GetDealsDateRange(from, to time.Time) ([]*pb.PositionHistoryInfo, error) {
    return s.positionsOpenedBetween(from, to, 30*time.Second)
}

positionsOpenedBetween reads PositionsHistory (sorted by open time) with cursor paging - page size 1000, PageToken = the previous NextPageToken - until the last page, so long ranges are complete. The range filters by the position's open time (a position opened before from and closed inside the range is not included). Still-open positions are not in the list. The timeout covers all pages together. The arguments are not validated.

Time zone note: from/to are real moments: they are sent in the request's UTC fields (PositionOpenTimeFromUtc / PositionOpenTimeToUtc), so the result does not depend on the broker's time zone. Build them in whatever zone you think in: time.Now().AddDate(0, 0, -7), time.Date(..., time.Local), time.Date(..., time.UTC), or time.Date(..., brokerZone) for the broker's calendar day.


πŸ”½ Input / ⬆️ OutputΒΆ

Input Type Description
from time.Time Start of the open-time range (inclusive)
to time.Time End of the open-time range (inclusive)
Output Type Description
[]*pb.PositionHistoryInfo slice Closed positions opened in range
error error "PositionsHistory failed: ..." if a page query failed (or the timeout expired)

πŸ’¬ Just the EssentialsΒΆ

  • What it is: Get closed trades that were opened within a custom time range you specify.
  • Why you need it: Custom period analysis, specific timeframe reports.
  • Sanity check: Returns an empty (possibly nil) slice if no deals in range - check len(deals). Both from and to are inclusive.

🎯 When to Use¢

βœ… Custom reports - Analyze specific time periods

βœ… Period review - Review account history for exact date ranges

βœ… Comparison - Compare different periods

βœ… Historical analysis - Study past performance patterns


πŸ”— Usage ExamplesΒΆ

1) Basic usage - last 7 daysΒΆ

to := time.Now()
from := to.AddDate(0, 0, -7) // 7 days ago

deals, err := sugar.GetDealsDateRange(from, to)
if err != nil {
    fmt.Printf("Error: %v\n", err)
    return
}

if len(deals) == 0 {
    fmt.Println("No deals in last 7 days")
    return
}

fmt.Printf("Last 7 days: %d deals\n\n", len(deals))

totalProfit := 0.0
for i, deal := range deals {
    fmt.Printf("%d. #%d %s: $%.2f\n",
        i+1, deal.PositionTicket, deal.Symbol, deal.Profit)
    totalProfit += deal.Profit
}

fmt.Printf("\nTotal: $%.2f\n", totalProfit)

2) Specific month analysis (last month)ΒΆ

now := time.Now()

// First day of last month
firstOfLastMonth := time.Date(now.Year(), now.Month()-1, 1, 0, 0, 0, 0, now.Location())

// First day of this month (exclusive end)
firstOfThisMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())

deals, _ := sugar.GetDealsDateRange(
    firstOfLastMonth,
    firstOfThisMonth.Add(-time.Second),
)

if len(deals) == 0 {
    fmt.Println("No deals last month")
    return
}

totalProfit := 0.0
for _, deal := range deals {
    totalProfit += deal.Profit
}

monthName := firstOfLastMonth.Format("January 2006")

fmt.Printf("%s Performance:\n", monthName)
fmt.Printf("  Trades:  %d\n", len(deals))
fmt.Printf("  Profit:  $%.2f\n", totalProfit)

3) Compare two equal periodsΒΆ

// This week
now := time.Now()
weekday := int(now.Weekday())
if weekday == 0 {
    weekday = 7
}
thisWeekStart := now.AddDate(0, 0, -(weekday - 1))
thisWeekStart = time.Date(thisWeekStart.Year(), thisWeekStart.Month(),
    thisWeekStart.Day(), 0, 0, 0, 0, thisWeekStart.Location())

thisWeekDeals, _ := sugar.GetDealsDateRange(thisWeekStart, now)

// Last week
lastWeekStart := thisWeekStart.AddDate(0, 0, -7)
lastWeekEnd := thisWeekStart.Add(-time.Second)

lastWeekDeals, _ := sugar.GetDealsDateRange(lastWeekStart, lastWeekEnd)

thisWeekProfit := 0.0
for _, deal := range thisWeekDeals {
    thisWeekProfit += deal.Profit
}

lastWeekProfit := 0.0
for _, deal := range lastWeekDeals {
    lastWeekProfit += deal.Profit
}

fmt.Println("Week Comparison:")
fmt.Println("─────────────────────────────────────────")
fmt.Printf("Last week:  %d trades, $%.2f\n",
    len(lastWeekDeals), lastWeekProfit)
fmt.Printf("This week:  %d trades, $%.2f\n",
    len(thisWeekDeals), thisWeekProfit)

diff := thisWeekProfit - lastWeekProfit
if diff > 0 {
    fmt.Printf("Improvement: +$%.2f πŸ“ˆ\n", diff)
} else if diff < 0 {
    fmt.Printf("Decline: $%.2f πŸ“‰\n", diff)
} else {
    fmt.Println("No change ➑️")
}

4) Specific date range (e.g., between two events)ΒΆ

// Example: Performance between two specific dates
from := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
to := time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC)

deals, _ := sugar.GetDealsDateRange(from, to)

if len(deals) == 0 {
    fmt.Printf("No deals between %s and %s\n",
        from.Format("Jan 2"), to.Format("Jan 2"))
    return
}

totalProfit := 0.0
for _, deal := range deals {
    totalProfit += deal.Profit
}

fmt.Printf("Period: %s - %s\n",
    from.Format("Jan 2, 2006"),
    to.Format("Jan 2, 2006"))
fmt.Printf("Trades:  %d\n", len(deals))
fmt.Printf("Profit:  $%.2f\n", totalProfit)

5) Last N trading daysΒΆ

func GetLastNTradingDays(sugar *mt5.MT5Sugar, n int) ([]*pb.PositionHistoryInfo, error) {
    to := time.Now()
    from := to.AddDate(0, 0, -n)

    return sugar.GetDealsDateRange(from, to)
}

// Usage: Last 30 trading days
deals, _ := GetLastNTradingDays(sugar, 30)

if len(deals) > 0 {
    totalProfit := 0.0
    for _, deal := range deals {
        totalProfit += deal.Profit
    }

    fmt.Printf("Last 30 days:\n")
    fmt.Printf("  Trades: %d\n", len(deals))
    fmt.Printf("  Profit: $%.2f\n", totalProfit)
    fmt.Printf("  Avg/day: $%.2f\n", totalProfit/30.0)
}

6) Quarter analysis (Q1, Q2, etc.)ΒΆ

func GetQuarterDeals(sugar *mt5.MT5Sugar, year, quarter int) ([]*pb.PositionHistoryInfo, error) {
    var startMonth time.Month
    switch quarter {
    case 1:
        startMonth = time.January
    case 2:
        startMonth = time.April
    case 3:
        startMonth = time.July
    case 4:
        startMonth = time.October
    default:
        return nil, fmt.Errorf("invalid quarter: %d", quarter)
    }

    from := time.Date(year, startMonth, 1, 0, 0, 0, 0, time.UTC)
    to := from.AddDate(0, 3, 0).Add(-time.Second) // 3 months later minus 1 second

    return sugar.GetDealsDateRange(from, to)
}

// Usage: Q1 2024
deals, _ := GetQuarterDeals(sugar, 2024, 1)

if len(deals) > 0 {
    totalProfit := 0.0
    for _, deal := range deals {
        totalProfit += deal.Profit
    }

    fmt.Println("Q1 2024 Performance:")
    fmt.Printf("  Trades: %d\n", len(deals))
    fmt.Printf("  Profit: $%.2f\n", totalProfit)
}

7) Year-to-date performanceΒΆ

now := time.Now()
startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, now.Location())

deals, _ := sugar.GetDealsDateRange(startOfYear, now)

if len(deals) == 0 {
    fmt.Println("No deals year-to-date")
    return
}

totalProfit := 0.0
winCount := 0

for _, deal := range deals {
    totalProfit += deal.Profit
    if deal.Profit > 0 {
        winCount++
    }
}

winRate := float64(winCount) / float64(len(deals)) * 100

fmt.Println("╔═══════════════════════════════════════╗")
fmt.Println("β•‘      YEAR-TO-DATE PERFORMANCE         β•‘")
fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")

fmt.Printf("Year:        %d\n", now.Year())
fmt.Printf("Trades:      %d\n", len(deals))
fmt.Printf("Win rate:    %.1f%%\n", winRate)
fmt.Printf("Total P/L:   $%.2f\n", totalProfit)

daysInYear := now.YearDay()
avgPerDay := totalProfit / float64(daysInYear)
fmt.Printf("Avg/day:     $%.2f\n", avgPerDay)

8) Hourly range analysis (specific trading hours)ΒΆ

// Analyze performance during specific hours (e.g., 9 AM - 5 PM)
from := time.Now().AddDate(0, 0, -30) // Last 30 days
to := time.Now()

deals, _ := sugar.GetDealsDateRange(from, to)

if len(deals) == 0 {
    fmt.Println("No deals in range")
    return
}

// Filter by closing hour (CloseTime is broker server time)
tradingHoursDeals := []*pb.PositionHistoryInfo{}
for _, deal := range deals {
    hour := deal.CloseTime.AsTime().Hour()
    if hour >= 9 && hour < 17 { // 9 AM to 5 PM
        tradingHoursDeals = append(tradingHoursDeals, deal)
    }
}

tradingHoursProfit := 0.0
for _, deal := range tradingHoursDeals {
    tradingHoursProfit += deal.Profit
}

fmt.Println("Trading Hours Analysis (9 AM - 5 PM):")
fmt.Println("─────────────────────────────────────────")
fmt.Printf("Total deals:         %d\n", len(deals))
fmt.Printf("In trading hours:    %d (%.1f%%)\n",
    len(tradingHoursDeals),
    float64(len(tradingHoursDeals))/float64(len(deals))*100)
fmt.Printf("Trading hours P/L:   $%.2f\n", tradingHoursProfit)

9) Compare same period from different yearsΒΆ

// This year January
thisYearJan := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
thisYearFeb := time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC)

thisYearDeals, _ := sugar.GetDealsDateRange(thisYearJan, thisYearFeb.Add(-time.Second))

// Last year January
lastYearJan := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
lastYearFeb := time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)

lastYearDeals, _ := sugar.GetDealsDateRange(lastYearJan, lastYearFeb.Add(-time.Second))

thisYearProfit := 0.0
for _, deal := range thisYearDeals {
    thisYearProfit += deal.Profit
}

lastYearProfit := 0.0
for _, deal := range lastYearDeals {
    lastYearProfit += deal.Profit
}

fmt.Println("January Year-over-Year Comparison:")
fmt.Println("─────────────────────────────────────────")
fmt.Printf("Jan 2023:  %d trades, $%.2f\n",
    len(lastYearDeals), lastYearProfit)
fmt.Printf("Jan 2024:  %d trades, $%.2f\n",
    len(thisYearDeals), thisYearProfit)

if thisYearProfit > lastYearProfit {
    fmt.Println("πŸ“ˆ Better performance this year")
} else if thisYearProfit < lastYearProfit {
    fmt.Println("πŸ“‰ Worse performance this year")
} else {
    fmt.Println("➑️  Same performance")
}

10) Advanced date range analyzerΒΆ

type DateRangeStats struct {
    From            time.Time
    To              time.Time
    TotalDeals      int
    TotalProfit     float64
    WinRate         float64
    BestDay         string
    BestDayProfit   float64
    WorstDay        string
    WorstDayProfit  float64
    DaysWithTrades  int
}

func AnalyzeDateRange(sugar *mt5.MT5Sugar, from, to time.Time) (*DateRangeStats, error) {
    deals, err := sugar.GetDealsDateRange(from, to)
    if err != nil {
        return nil, err
    }

    stats := &DateRangeStats{
        From:       from,
        To:         to,
        TotalDeals: len(deals),
    }

    if len(deals) == 0 {
        return stats, nil
    }

    winCount := 0
    dailyProfit := make(map[string]float64)

    for _, deal := range deals {
        stats.TotalProfit += deal.Profit

        if deal.Profit > 0 {
            winCount++
        }

        dayKey := deal.CloseTime.AsTime().Format("2006-01-02")
        dailyProfit[dayKey] += deal.Profit
    }

    stats.WinRate = float64(winCount) / float64(len(deals)) * 100
    stats.DaysWithTrades = len(dailyProfit)

    // Find best/worst days
    stats.BestDayProfit = -999999
    stats.WorstDayProfit = 999999

    for day, profit := range dailyProfit {
        if profit > stats.BestDayProfit {
            stats.BestDayProfit = profit
            stats.BestDay = day
        }

        if profit < stats.WorstDayProfit {
            stats.WorstDayProfit = profit
            stats.WorstDay = day
        }
    }

    return stats, nil
}

func (s *DateRangeStats) Print() {
    fmt.Println("╔═══════════════════════════════════════╗")
    fmt.Println("β•‘      DATE RANGE ANALYSIS              β•‘")
    fmt.Println("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")

    fmt.Printf("From:              %s\n", s.From.Format("2006-01-02"))
    fmt.Printf("To:                %s\n\n", s.To.Format("2006-01-02"))

    if s.TotalDeals == 0 {
        fmt.Println("No trading activity in this period")
        return
    }

    duration := s.To.Sub(s.From)
    days := int(duration.Hours() / 24)

    fmt.Printf("Total Deals:       %d\n", s.TotalDeals)
    fmt.Printf("Total Profit:      $%.2f\n", s.TotalProfit)
    fmt.Printf("Win Rate:          %.1f%%\n\n", s.WinRate)

    fmt.Printf("Period Length:     %d days\n", days)
    fmt.Printf("Days with Trades:  %d\n", s.DaysWithTrades)

    if days > 0 {
        fmt.Printf("Avg Profit/Day:    $%.2f\n\n", s.TotalProfit/float64(days))
    }

    fmt.Printf("Best Day:          %s ($%.2f)\n", s.BestDay, s.BestDayProfit)
    fmt.Printf("Worst Day:         %s ($%.2f)\n", s.WorstDay, s.WorstDayProfit)
}

// Usage:
from := time.Now().AddDate(0, -1, 0) // 1 month ago
to := time.Now()

stats, _ := AnalyzeDateRange(sugar, from, to)
stats.Print()

🍬 Pre-defined periods:

  • GetDealsToday() - Today's deals (00:00 to now)
  • GetDealsYesterday() - Yesterday's deals (full day)
  • GetDealsThisWeek() - This week's deals (Monday to now)
  • GetDealsThisMonth() - This month's deals (1st to now)

🍬 Profit calculations:

  • GetProfitToday(), GetProfitThisWeek(), GetProfitThisMonth() - Profit totals (sum of Profit)

⚠️ Common Pitfalls¢

1) Inverted date rangeΒΆ

// ❌ WRONG - from > to
to := time.Now()
from := to.AddDate(0, 0, 7) // 7 days in future!
deals, _ := sugar.GetDealsDateRange(from, to) // Arguments are not validated - no useful result

// βœ… CORRECT - from < to
from := to.AddDate(0, 0, -7) // 7 days in past
deals, _ := sugar.GetDealsDateRange(from, to)

2) Not handling timezoneΒΆ

// ❌ WRONG - writing the broker's calendar day in time.UTC
// (from/to are real moments: this is the UTC day, not the broker's day)
from := time.Date(2024, 1, 15, 0, 0, 0, 0, time.UTC)
to := time.Date(2024, 1, 15, 23, 59, 59, 0, time.UTC)

// βœ… CORRECT - build the boundaries in the zone whose calendar you mean
broker := time.FixedZone("broker", 2*60*60) // e.g. a broker at UTC+2
from := time.Date(2024, 1, 15, 0, 0, 0, 0, broker)
to := time.Date(2024, 1, 15, 23, 59, 59, 0, broker)

2b) Expecting positions opened before fromΒΆ

// ❌ WRONG - a position opened Dec 31 and closed Jan 2 is NOT in January's result
deals, _ := sugar.GetDealsDateRange(jan1, jan31)

// βœ… CORRECT - start earlier and filter on CloseTime yourself
deals, _ := sugar.GetDealsDateRange(jan1.AddDate(0, 0, -30), jan31)

3) Exclusive vs inclusive rangeΒΆ

// ❌ WRONG - assuming exclusive end
to := time.Date(2024, 1, 31, 0, 0, 0, 0, time.UTC) // Excludes Jan 31!

// βœ… CORRECT - include full last day
to := time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC)

πŸ’Ž Pro TipsΒΆ

  1. Both inclusive - Both from and to are inclusive

  2. Timezone aware - from/to are real moments sent as UTC; the broker's time zone does not shift them

  3. Flexible - Most flexible history method for custom periods

  4. Performance - 30-second timeout for large ranges

  5. Empty OK - Returns empty slice if no trades (not error)

  6. Profit field only - Profit does not include commission, swap or fee


πŸ“Š Common Date Range PatternsΒΆ

// Last 7 days
from := time.Now().AddDate(0, 0, -7)
to := time.Now()

// Last 30 days
from := time.Now().AddDate(0, 0, -30)
to := time.Now()

// Last month (complete)
now := time.Now()
firstOfThisMonth := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
firstOfLastMonth := firstOfThisMonth.AddDate(0, -1, 0)
from := firstOfLastMonth
to := firstOfThisMonth.Add(-time.Second)

// This year
from := time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Local)
to := time.Now()

// Specific month
from := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
to := time.Date(2024, 1, 31, 23, 59, 59, 0, time.UTC)

See also: GetDealsToday.md, GetDealsThisWeek.md, GetDealsThisMonth.md