Trading: components 161-180
This commit is contained in:
105 files changed
+6042
-1
No files matched your search
@@ -0,0 +1,123 @@
|
||||
package mujica
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/egoist/mygo/ui"
|
||||
|
||||
"mujicaui/theme"
|
||||
)
|
||||
|
||||
// L2Level is a level's price, size and order count.
|
||||
type L2Level struct {
|
||||
Price, Size float64
|
||||
Orders int
|
||||
}
|
||||
|
||||
// Level2Data holds the bid and ask levels, best first.
|
||||
type Level2Data struct {
|
||||
Bids, Asks []L2Level
|
||||
}
|
||||
|
||||
// Level2QuotesOptions configure a Level2Quotes.
|
||||
type Level2QuotesOptions struct {
|
||||
// Decimals of prices and sizes; zero means 2 and 0.
|
||||
Decimals, SizeDecimals int
|
||||
// Levels shown each side; zero means 5.
|
||||
Levels int
|
||||
}
|
||||
|
||||
// level2Mark is a level's last values and size move.
|
||||
type level2Mark struct {
|
||||
price, size float64
|
||||
dir int
|
||||
at time.Time
|
||||
seen bool
|
||||
}
|
||||
|
||||
// Level2Quotes lists bid and ask levels; changes flash and mark.
|
||||
func Level2Quotes(c *ui.Context, data Level2Data, opts Level2QuotesOptions) *ui.Element {
|
||||
dec := priceDecimals("Level2Quotes", opts.Decimals)
|
||||
if opts.SizeDecimals < 0 || opts.Levels < 0 {
|
||||
panic("mujica: Level2Quotes SizeDecimals and Levels cannot be negative")
|
||||
}
|
||||
n := opts.Levels
|
||||
if n == 0 {
|
||||
n = 5
|
||||
}
|
||||
u := Density(c).Unit()
|
||||
e := ui.Row(c).Gap(u * 3).AlignItems(ui.Start).Role(ui.RoleTable).Label(msg(c, "level2_quotes.label"))
|
||||
marks := ui.Local(e, "marks", func() map[string]*level2Mark { return map[string]*level2Mark{} })
|
||||
e.Children(func() {
|
||||
for _, side := range []OrderSide{SideBuy, SideSell} {
|
||||
levels := data.Bids
|
||||
if side == SideSell {
|
||||
levels = data.Asks
|
||||
}
|
||||
ui.Column(c).Grow(1).Basis(0).MinWidth(0).Children(func() {
|
||||
for i := range n {
|
||||
var l *L2Level
|
||||
if i < len(levels) {
|
||||
l = &levels[i]
|
||||
}
|
||||
level2Row(c, *marks, side, i, l, dec, opts.SizeDecimals)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return e
|
||||
}
|
||||
|
||||
// level2Row is one level with its move mark.
|
||||
func level2Row(c *ui.Context, marks map[string]*level2Mark, side OrderSide, i int, l *L2Level, dec, sizeDec int) {
|
||||
k := Tokens(c)
|
||||
d := Density(c)
|
||||
name := fmt.Sprintf(msg(c, fmt.Sprintf("level2_quotes.side%d", side)), i+1)
|
||||
key := fmt.Sprint(side, i)
|
||||
m := marks[key]
|
||||
if m == nil {
|
||||
m = &level2Mark{}
|
||||
marks[key] = m
|
||||
}
|
||||
row := ui.Row(c).Gap(d.Unit()*2).AlignItems(ui.Center).MinHeight(d.ControlHeight()).Padding(0, d.Unit()*2).
|
||||
Radius(theme.ControlRadius).BorderWidth(0, 0, theme.BorderWidth, 0).BorderColor(k.Border)
|
||||
if l == nil {
|
||||
row.Children(func() {
|
||||
ui.Text(c, name).TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).Width(28)
|
||||
ui.Text(c, "—").TextColor(k.TextMuted)
|
||||
})
|
||||
return
|
||||
}
|
||||
if m.seen && (l.Price != m.price || l.Size != m.size) {
|
||||
// A new price is a new level, not a move.
|
||||
m.dir, m.at = 0, c.Now()
|
||||
if l.Price == m.price {
|
||||
m.dir = trendSign(l.Size - m.size)
|
||||
}
|
||||
slog.Debug("mujica: level2_quotes level changed", "level", name)
|
||||
}
|
||||
m.price, m.size, m.seen = l.Price, l.Size, true
|
||||
if dur := Motion(c, priceTextFlash); dur > 0 && !m.at.IsZero() {
|
||||
if t := c.Now().Sub(m.at); t < dur {
|
||||
row.Background(k.Selection.Alpha(1 - float32(float64(t)/float64(dur))))
|
||||
c.AnimationFrame()
|
||||
}
|
||||
}
|
||||
col := trendColor(c, side.sign())
|
||||
sizeText := statisticFormat(l.Size, sizeDec)
|
||||
orders := fmt.Sprintf(msg(c, "level2_quotes.orders"), l.Orders)
|
||||
row.Role(ui.RoleRow).Label(name + " " + statisticFormat(l.Price, dec) + " " + sizeText + " " + orders)
|
||||
row.Children(func() {
|
||||
ui.Text(c, name).TextColor(k.TextMuted).FontSize(FontSize(c, theme.CaptionSize)).Width(28)
|
||||
ui.Text(c, statisticFormat(l.Price, dec)).TextColor(col).FontFeatures("tnum").FontWeight(600).Grow(1).Basis(0).SingleLine()
|
||||
ui.Row(c).Gap(2).AlignItems(ui.Center).Justify(ui.End).Grow(1).Basis(0).Children(func() {
|
||||
if m.dir != 0 {
|
||||
ui.Icon(c, trendIcon(m.dir)).Size(12, 12).TextColor(trendColor(c, m.dir))
|
||||
}
|
||||
ui.Text(c, sizeText).FontFeatures("tnum").SingleLine()
|
||||
})
|
||||
ui.Text(c, fmt.Sprint(l.Orders)).TextColor(k.TextMuted).FontFeatures("tnum").TextAlign(ui.End).Width(36).SingleLine()
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user