diff options
| author | historia <[not public]> | 2026-10-05 14:22:14 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-10-05 14:22:14 -0400 |
| commit | 90ad8c0c33b519d5830c69d4128bdc984b5e9de8 (patch) | |
| tree | c10bc0bebfbed6fde01a7f08cf6e2d98809b91a7 /internal/cryptomarket/money.go | |
| parent | c84a9944356f590d90d819d652bdf1349ebacd40 (diff) | |
| download | thehouseoficarus-90ad8c0c33b519d5830c69d4128bdc984b5e9de8.tar.gz | |
feat: cryptocurrency exchange added to casino
Diffstat (limited to 'internal/cryptomarket/money.go')
| -rw-r--r-- | internal/cryptomarket/money.go | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/internal/cryptomarket/money.go b/internal/cryptomarket/money.go new file mode 100644 index 0000000..8154623 --- /dev/null +++ b/internal/cryptomarket/money.go @@ -0,0 +1,72 @@ +// Package cryptomarket implements a fictional-credit exchange, not real orders. +package cryptomarket + +import ( + "fmt" + "math" + "math/big" + "regexp" + "strconv" +) + +const MaxMoney int64 = 1_000_000_000_000 +const MaxPositions = 50 + +// Decimal strings survive YAML/JSON without a float conversion. In particular, +// neither SHIB prices nor fractional BTC quantities are rounded to credits. +var decimalPattern = regexp.MustCompile(`^(0|[1-9][0-9]{0,14})(\.[0-9]{1,18})?$`) + +func Decimal(s string) (*big.Rat, error) { + if !decimalPattern.MatchString(s) { + return nil, fmt.Errorf("use a positive decimal (no exponent), at most 15 integer and 18 fractional digits") + } + r, ok := new(big.Rat).SetString(s) + if !ok || r.Sign() <= 0 { + return nil, fmt.Errorf("amount must be positive") + } + return r, nil +} + +func rat(s string) *big.Rat { r, _ := new(big.Rat).SetString(s); return r } +func mul(a, b *big.Rat) *big.Rat { return new(big.Rat).Mul(a, b) } +func add(a, b *big.Rat) *big.Rat { return new(big.Rat).Add(a, b) } +func sub(a, b *big.Rat) *big.Rat { return new(big.Rat).Sub(a, b) } +func integer(n int64) *big.Rat { return new(big.Rat).SetInt64(n) } + +// Round rounds the complete cash amount once, nearest credit, ties away from +// zero. Never round per coin, and never silently clamp an overflow. +func Round(r *big.Rat) (int64, error) { + if r == nil { + return 0, fmt.Errorf("invalid amount") + } + a := new(big.Rat).Abs(r) + n, rem := new(big.Int), new(big.Int) + n.QuoRem(a.Num(), a.Denom(), rem) + if rem.Lsh(rem, 1).Cmp(a.Denom()) >= 0 { + n.Add(n, big.NewInt(1)) + } + if !n.IsInt64() || n.Int64() > MaxMoney { + return 0, fmt.Errorf("amount exceeds exchange accounting limit") + } + v := n.Int64() + if r.Sign() < 0 { + v = -v + } + return v, nil +} +func Sum(a, b int64) (int64, error) { + if a < -MaxMoney || a > MaxMoney || b < -MaxMoney || b > MaxMoney || a+b < -MaxMoney || a+b > MaxMoney { + return 0, fmt.Errorf("exchange balance limit exceeded") + } + return a + b, nil +} +func floatRat(f float64) (*big.Rat, error) { + if math.IsNaN(f) || math.IsInf(f, 0) || f < 0 { + return nil, fmt.Errorf("invalid model value") + } + r, ok := new(big.Rat).SetString(strconv.FormatFloat(f, 'f', 18, 64)) + if !ok { + return nil, fmt.Errorf("invalid model value") + } + return r, nil +} |
