1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
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 {
return []string{"Chart too narrow; widen wrap_width."}
}
if 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
}
|