algorithms

package
v0.0.0-...-5e2acd7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 10, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package algorithms provides analytical and processing algorithms for LunchMoney transactions.

This package includes the following modules:

Anomaly Detection (anomaly.go)

DetectAnomalies identifies unusual transactions using statistical methods:

  • Z-score detection: Flags transactions > 2.5 standard deviations from mean
  • IQR detection: Identifies outliers using Interquartile Range
  • New merchant detection: First-time merchant transactions
  • Duplicate detection: Similar transactions within a time window
  • Large transactions: Amounts exceeding a threshold

Example:

options := algorithms.DefaultAnomalyOptions()
anomalies := algorithms.DetectAnomalies(transactions, options)
for _, anomaly := range anomalies {
    fmt.Printf("Anomaly: %s - %s (confidence: %.2f)\n",
        anomaly.Type, anomaly.Reason, anomaly.Confidence)
}

Merchant Normalization (normalize.go)

NormalizeMerchant canonicalizes merchant names for consistent comparison:

  • Strips common suffixes (Inc, LLC, Corp, store numbers)
  • Maps known variations (AMZN→Amazon, WM→Walmart)
  • Removes payment processor prefixes (TST*, SQ*, PP*)
  • Uses Levenshtein distance for fuzzy matching

Example:

normalized := algorithms.NormalizeMerchant("TST* STARBUCKS #1234")
// Returns: "STARBUCKS"

similar := algorithms.FindSimilarMerchants("STARBUCKS", knownMerchants, 3)
// Returns merchants with edit distance ≤ 3

Category Suggestions (categorize.go)

SuggestCategory recommends categories based on historical patterns:

  • Exact merchant match (highest confidence)
  • Normalized merchant match
  • Fuzzy match with Levenshtein distance ≤ 3
  • Partial text matching in payee

Example:

suggestion := algorithms.SuggestCategory(transaction, history, categories)
if suggestion.CategoryID != 0 {
    fmt.Printf("Suggested: %s (confidence: %.2f)\n",
        suggestion.CategoryName, suggestion.Confidence)
}

Analyzes spending patterns over time:

  • MovingAverage: Smooths spending data
  • CalculateBudgetPace: Tracks budget spending rate
  • CompareSpending: Compares spending between periods
  • Daily/Weekly/Monthly aggregations

Example:

result := algorithms.CalculateBudgetPace(spent, budget, daysElapsed, daysInMonth)
fmt.Printf("Status: %s, Projected: $%.2f\n",
    result.Status, result.ProjectedEnd)

comparison := algorithms.CompareSpending(currentTxs, previousTxs)
fmt.Printf("Change: %.2f%%\n", comparison.PercentChange)

Duplicate Detection (dedup.go)

Identifies potential duplicate transactions:

  • Same normalized merchant
  • Amount within ±$0.50 or ±1%
  • Date within 3 days (configurable)
  • Confidence scoring based on multiple factors

Example:

duplicates := algorithms.DetectPossibleDuplicates(transactions)
for _, dup := range duplicates {
    fmt.Printf("Possible duplicate: %s - %s (confidence: %.2f)\n",
        dup.Transaction1.Payee, dup.Reason, dup.Confidence)
}

groups := algorithms.FindDuplicateGroups(transactions)
// Returns groups of connected duplicates

Dependencies

This package requires:

  • gonum.org/v1/gonum/stat: Statistical calculations
  • github.com/agnivade/levenshtein: String similarity

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CalculateSpendingByCategory

func CalculateSpendingByCategory(transactions []api.Transaction) map[string]float64

CalculateSpendingByCategory calculates spending totals by category

func CalculateSpendingByMerchant

func CalculateSpendingByMerchant(transactions []api.Transaction) map[string]float64

CalculateSpendingByMerchant calculates spending totals by merchant

func DetectSpendingTrend

func DetectSpendingTrend(points []TrendPoint) string

DetectSpendingTrend analyzes if spending is increasing, decreasing, or stable

func ExtractMerchantCore

func ExtractMerchantCore(name string) string

ExtractMerchantCore attempts to extract the core merchant name by removing common noise words and patterns

func FindDuplicateGroups

func FindDuplicateGroups(transactions []api.Transaction) [][]api.Transaction

FindDuplicateGroups groups transactions that are likely duplicates of each other

func FindSimilarMerchants

func FindSimilarMerchants(name string, known []string, threshold int) []string

FindSimilarMerchants finds merchants similar to the given name using Levenshtein distance threshold is the maximum edit distance to consider similar (recommended: 3)

func GetCategoryStats

func GetCategoryStats(history []api.Transaction) map[int]CategoryStats

GetCategoryStats returns statistics about category usage in history

func GetPercentile

func GetPercentile(amounts []float64, percentile float64) float64

GetPercentile calculates the percentile value from a set of amounts

func GroupSimilarMerchants

func GroupSimilarMerchants(merchants []string, threshold int) map[string][]string

GroupSimilarMerchants groups merchants by similarity Returns a map where keys are canonical names and values are lists of similar variants

func MerchantSimilarity

func MerchantSimilarity(name1, name2 string) float64

MerchantSimilarity calculates similarity score between two merchant names Returns a score from 0.0 (completely different) to 1.0 (identical)

func MovingAverage

func MovingAverage(amounts []float64, window int) []float64

MovingAverage calculates the moving average of a series of amounts

func NormalizeMerchant

func NormalizeMerchant(name string) string

NormalizeMerchant canonicalizes a merchant name for comparison

func SuggestCategoryBatch

func SuggestCategoryBatch(transactions []api.Transaction, history []api.Transaction, categories []api.Category) map[int]CategorySuggestion

SuggestCategoryBatch suggests categories for multiple transactions

Types

type Anomaly

type Anomaly struct {
	Transaction api.Transaction
	Type        AnomalyType
	Confidence  float64 // 0.0 to 1.0
	Reason      string
}

Anomaly represents a detected anomaly in a transaction

func DetectAnomalies

func DetectAnomalies(transactions []api.Transaction, options AnomalyOptions) []Anomaly

DetectAnomalies identifies anomalies in a set of transactions

type AnomalyOptions

type AnomalyOptions struct {
	ZScoreThreshold        float64 // Default: 2.5
	IQRMultiplier          float64 // Default: 1.5
	LargeTransactionMin    float64 // Default: 500.0
	DuplicateWindowDays    int     // Default: 3
	DuplicateAmountDelta   float64 // Default: 0.50
	EnableZScore           bool    // Default: true
	EnableIQR              bool    // Default: true
	EnableNewMerchant      bool    // Default: true
	EnableDuplicate        bool    // Default: true
	EnableLargeTransaction bool    // Default: true
}

AnomalyOptions configures anomaly detection parameters

func DefaultAnomalyOptions

func DefaultAnomalyOptions() AnomalyOptions

DefaultAnomalyOptions returns default options for anomaly detection

type AnomalyType

type AnomalyType string

AnomalyType represents the type of anomaly detected

const (
	AnomalyUnusualAmount     AnomalyType = "unusual_amount"
	AnomalyNewMerchant       AnomalyType = "new_merchant"
	AnomalyDuplicatePossible AnomalyType = "duplicate_possible"
	AnomalyUnusualTiming     AnomalyType = "unusual_timing"
	AnomalyLargeTransaction  AnomalyType = "large_transaction"
)

type BudgetPaceResult

type BudgetPaceResult struct {
	Status         BudgetPaceStatus
	ProjectedEnd   float64 // Projected spending by end of month
	DailyRate      float64 // Current daily spending rate
	DaysRemaining  int
	PercentSpent   float64
	PercentElapsed float64
	OnTrackAmount  float64 // Amount that should be spent by now if on track
}

BudgetPaceResult contains budget pacing analysis

func CalculateBudgetPace

func CalculateBudgetPace(spent, budget float64, daysElapsed, daysInMonth int) BudgetPaceResult

CalculateBudgetPace analyzes budget pacing for the current month

type BudgetPaceStatus

type BudgetPaceStatus string

BudgetPaceStatus represents the budget pacing status

const (
	BudgetUnderBudget BudgetPaceStatus = "under_budget"
	BudgetOnTrack     BudgetPaceStatus = "on_track"
	BudgetOverBudget  BudgetPaceStatus = "over_budget"
)

type CategoryStats

type CategoryStats struct {
	Count           int
	TotalAmount     float64
	UniqueMerchants map[string]bool
}

CategoryStats holds statistics about a category

type CategorySuggestion

type CategorySuggestion struct {
	CategoryID   int
	CategoryName string
	Confidence   float64 // 0.0 to 1.0
	Reason       string
}

CategorySuggestion represents a suggested category for a transaction

func SuggestCategory

func SuggestCategory(tx api.Transaction, history []api.Transaction, categories []api.Category) CategorySuggestion

SuggestCategory suggests a category for a transaction based on historical data

type ComparisonResult

type ComparisonResult struct {
	CurrentTotal    float64
	PreviousTotal   float64
	Difference      float64
	PercentChange   float64
	CurrentCount    int
	PreviousCount   int
	AverageCurrent  float64
	AveragePrevious float64
}

ComparisonResult contains spending comparison data

func CompareSpending

func CompareSpending(current, previous []api.Transaction) ComparisonResult

CompareSpending compares spending between two time periods

type DedupOptions

type DedupOptions struct {
	MaxDaysDiff         int     // Maximum days between transactions (default: 3)
	AmountDeltaAbsolute float64 // Absolute amount difference tolerance (default: 0.50)
	AmountDeltaPercent  float64 // Percentage amount difference tolerance (default: 0.01 = 1%)
	RequireExactAmount  bool    // If true, amounts must match exactly (default: false)
	RequireSameCategory bool    // If true, categories must match (default: false)
	MinConfidence       float64 // Minimum confidence to report (default: 0.5)
}

DedupOptions configures duplicate detection parameters

func DefaultDedupOptions

func DefaultDedupOptions() DedupOptions

DefaultDedupOptions returns default options for duplicate detection

type DuplicateCandidate

type DuplicateCandidate struct {
	Transaction1 api.Transaction
	Transaction2 api.Transaction
	Confidence   float64 // 0.0 to 1.0
	Reason       string
}

DuplicateCandidate represents a pair of potentially duplicate transactions

func DetectPossibleDuplicates

func DetectPossibleDuplicates(transactions []api.Transaction) []DuplicateCandidate

DetectPossibleDuplicates identifies potential duplicate transactions

func DetectPossibleDuplicatesWithOptions

func DetectPossibleDuplicatesWithOptions(transactions []api.Transaction, options DedupOptions) []DuplicateCandidate

DetectPossibleDuplicatesWithOptions identifies duplicates with custom options

type DuplicateStats

type DuplicateStats struct {
	TotalCandidates  int
	HighConfidence   int
	MediumConfidence int
	LowConfidence    int
	PotentialSavings float64
}

DuplicateStats holds statistics about duplicate detection

func GetDuplicateStats

func GetDuplicateStats(candidates []DuplicateCandidate) DuplicateStats

GetDuplicateStats returns statistics about duplicates

type MatchType

type MatchType string

MatchType represents how a category was matched

const (
	MatchExact      MatchType = "exact_merchant"
	MatchNormalized MatchType = "normalized_merchant"
	MatchFuzzy      MatchType = "fuzzy_merchant"
	MatchPayeeText  MatchType = "payee_text"
	MatchNone       MatchType = "no_match"
)

type TrendPoint

type TrendPoint struct {
	Date  string
	Value float64
	Label string
}

TrendPoint represents a point in a trend analysis

func CalculateDailySpending

func CalculateDailySpending(transactions []api.Transaction) []TrendPoint

CalculateDailySpending calculates daily spending totals from transactions

func CalculateMonthlySpending

func CalculateMonthlySpending(transactions []api.Transaction) []TrendPoint

CalculateMonthlySpending calculates monthly spending totals from transactions

func CalculateWeeklySpending

func CalculateWeeklySpending(transactions []api.Transaction) []TrendPoint

CalculateWeeklySpending calculates weekly spending totals from transactions

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL