package vo import ( "fmt" "math" ) // Price is a value object representing a positive price with decimal precision. type Price struct { value float64 } // NewPrice creates a validated Price. Value must be positive. func NewPrice(v float64) (Price, error) { if v < 0 { return Price{}, fmt.Errorf("price must be non-negative, got %f", v) } if math.IsNaN(v) || math.IsInf(v, 0) { return Price{}, fmt.Errorf("price must be a finite number, got %f", v) } return Price{value: v}, nil } // MustNewPrice creates a Price, panicking on invalid input. Use only in tests. func MustNewPrice(v float64) Price { p, err := NewPrice(v) if err != nil { panic(err) } return p } // Zero returns a zero price. func ZeroPrice() Price { return Price{value: 0} } // Float64 returns the underlying float64 value. func (p Price) Float64() float64 { return p.value } // IsZero returns true if the price is zero. func (p Price) IsZero() bool { return p.value == 0 } // IsPositive returns true if the price is strictly greater than zero. func (p Price) IsPositive() bool { return p.value > 0 } // GreaterThan returns true if this price is greater than other. func (p Price) GreaterThan(other Price) bool { return p.value > other.value } // LessThan returns true if this price is less than other. func (p Price) LessThan(other Price) bool { return p.value < other.value } // Equal returns true if both prices are equal. func (p Price) Equal(other Price) bool { return p.value == other.value } // Add returns a new Price that is the sum of two prices. func (p Price) Add(other Price) Price { return Price{value: p.value + other.value} } // Multiply returns price * quantity as a float64 (for notional calculation). func (p Price) Multiply(qty int) float64 { return p.value * float64(qty) } // MidPrice returns the midpoint between two prices. func MidPrice(a, b Price) Price { return Price{value: (a.value + b.value) / 2} } // Spread returns (ask - bid) / bid as a float64 ratio. func Spread(bid, ask Price) float64 { if bid.IsZero() { return 0 } return (ask.value - bid.value) / bid.value } // RoundTo rounds the price to the given number of decimal places. func (p Price) RoundTo(decimals int) Price { ratio := math.Pow(10, float64(decimals)) return Price{value: math.Round(p.value*ratio) / ratio} } // String returns a human-readable representation. func (p Price) String() string { return fmt.Sprintf("%.4f", p.value) }