aboutsummaryrefslogtreecommitdiff
path: root/internal/cryptomarket/chart.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-05 14:22:14 -0400
committerhistoria <[not public]>2026-10-05 14:22:14 -0400
commit90ad8c0c33b519d5830c69d4128bdc984b5e9de8 (patch)
treec10bc0bebfbed6fde01a7f08cf6e2d98809b91a7 /internal/cryptomarket/chart.go
parentc84a9944356f590d90d819d652bdf1349ebacd40 (diff)
downloadthehouseoficarus-90ad8c0c33b519d5830c69d4128bdc984b5e9de8.tar.gz
feat: cryptocurrency exchange added to casino
Diffstat (limited to 'internal/cryptomarket/chart.go')
-rw-r--r--internal/cryptomarket/chart.go78
1 files changed, 78 insertions, 0 deletions
diff --git a/internal/cryptomarket/chart.go b/internal/cryptomarket/chart.go
new file mode 100644
index 0000000..c1fef29
--- /dev/null
+++ b/internal/cryptomarket/chart.go
@@ -0,0 +1,78 @@
+package cryptomarket
+
+import (
+ "fmt"
+ "math"
+ "strings"
+ "time"
+)
+
+// Chart uses one column per completed daily candle, auto-scales for BTC and
+// tiny tokens alike, and delegates color to the player's configured palette.
+func Chart(candles []Candle, width int, unicode bool, paint func(string, bool) string) []string {
+ width = max(12, min(80, width))
+ label := 12
+ count := width - label - 2
+ if count < 2 || len(candles) < 2 {
+ return []string{"Not enough completed daily candles."}
+ }
+ if len(candles) > count {
+ candles = candles[len(candles)-count:]
+ }
+ type bar struct{ o, h, l, c float64 }
+ bars := []bar{}
+ low, high := math.Inf(1), math.Inf(-1)
+ for _, c := range candles {
+ if c.Validate() != nil {
+ return []string{"Invalid candle history."}
+ }
+ o, _ := rat(c.Open).Float64()
+ h, _ := rat(c.High).Float64()
+ l, _ := rat(c.Low).Float64()
+ v, _ := rat(c.Close).Float64()
+ bars = append(bars, bar{o, h, l, v})
+ low = math.Min(low, l)
+ high = math.Max(high, h)
+ }
+ if high == low {
+ padding := math.Max(high*0.01, 1e-18)
+ low -= padding
+ high += padding
+ }
+ const height = 10
+ row := func(p float64) int { return max(0, min(height-1, int(math.Round((high-p)/(high-low)*(height-1))))) }
+ grid := make([][]string, height)
+ for y := range grid {
+ grid[y] = make([]string, len(bars))
+ for x := range grid[y] {
+ grid[y][x] = " "
+ }
+ }
+ wick, body, doji, axis := "|", "#", "-", "|"
+ if unicode {
+ wick, body, doji, axis = "│", "█", "━", "│"
+ }
+ for x, b := range bars {
+ top, bottom := min(row(b.o), row(b.c)), max(row(b.o), row(b.c))
+ for y := row(b.h); y <= row(b.l); y++ {
+ ch := wick
+ if y >= top && y <= bottom {
+ ch = body
+ if top == bottom {
+ ch = doji
+ }
+ }
+ if paint != nil {
+ ch = paint(ch, b.c >= b.o)
+ }
+ grid[y][x] = ch
+ }
+ }
+ out := []string{"USD / credits; daily candles (UTC). Up/down colored; body=open-close, wick=range."}
+ for y := range grid {
+ v := high - float64(y)*(high-low)/(height-1)
+ out = append(out, fmt.Sprintf("%11.5g %s%s", v, axis, strings.Join(grid[y], "")))
+ }
+ out = append(out, fmt.Sprintf("%s to %s", time.Unix(candles[0].Time, 0).UTC().Format("2006-01-02"), time.Unix(candles[len(candles)-1].Time, 0).UTC().Format("2006-01-02")))
+ return out
+}