111 lines
4.2 KiB
Go
111 lines
4.2 KiB
Go
package mujica
|
|
|
|
import (
|
|
"cmp"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/egoist/mygo/ui"
|
|
|
|
"mujicaui/icons"
|
|
)
|
|
|
|
// FillRow is one of the account's fills; ID is stable.
|
|
type FillRow struct {
|
|
ID string
|
|
Time time.Time
|
|
Symbol string
|
|
Side OrderSide
|
|
Price float64
|
|
Quantity float64
|
|
Fee float64
|
|
}
|
|
|
|
// TradeHistoryTableOptions configure a TradeHistoryTable.
|
|
type TradeHistoryTableOptions struct {
|
|
// Decimals of prices and quantities; zero means 2 and 0.
|
|
Decimals, QuantityDecimals int
|
|
// Version must change when rows change in place.
|
|
Version int
|
|
Status DataStatus
|
|
Error string
|
|
Disabled bool
|
|
}
|
|
|
|
// TradeHistoryTableState is the date range and table state.
|
|
type TradeHistoryTableState struct {
|
|
// Range keeps fills on its days; zero keeps all.
|
|
Range DateRange
|
|
Table DataTableState[string]
|
|
cache tradingRows[FillRow]
|
|
}
|
|
|
|
// TradeHistoryResult is a built table and its export request.
|
|
type TradeHistoryResult struct {
|
|
Element *ui.Element
|
|
Table DataTableView
|
|
exported []FillRow
|
|
export bool
|
|
}
|
|
|
|
// Exported returns the range's fills when export was asked.
|
|
func (r TradeHistoryResult) Exported() ([]FillRow, bool) { return r.exported, r.export }
|
|
|
|
// TradeHistoryTable lists fills by date range, with export.
|
|
func TradeHistoryTable(c *ui.Context, s *TradeHistoryTableState, rows []FillRow, opts TradeHistoryTableOptions) TradeHistoryResult {
|
|
dec := priceDecimals("TradeHistoryTable", opts.Decimals)
|
|
if opts.QuantityDecimals < 0 {
|
|
panic("mujica: TradeHistoryTable with negative QuantityDecimals")
|
|
}
|
|
rng := s.Range
|
|
shown := s.cache.view(rows, opts.Version, rng, func(f FillRow) bool {
|
|
if rng.IsZero() {
|
|
return true
|
|
}
|
|
d := DateOf(f.Time)
|
|
return d.Compare(rng.Start) >= 0 && d.Compare(rng.End) <= 0
|
|
})
|
|
num := func(id, key string, dp int, v func(FillRow) float64) DataColumn[FillRow] {
|
|
return DataColumn[FillRow]{ID: id, Title: msg(c, key), Align: ui.End, Sortable: true,
|
|
Compare: func(a, b FillRow) int { return cmp.Compare(v(a), v(b)) },
|
|
Text: func(f FillRow) string { return statisticFormat(v(f), dp) }}
|
|
}
|
|
cols := []DataColumn[FillRow]{
|
|
{ID: "time", Title: msg(c, "trade_history_table.time"), Sortable: true, Width: 180,
|
|
Compare: func(a, b FillRow) int { return a.Time.Compare(b.Time) },
|
|
Text: func(f FillRow) string { return f.Time.Format("2006-01-02 15:04:05") }},
|
|
{ID: "symbol", Title: msg(c, "position_table.symbol"), Sortable: true,
|
|
Compare: func(a, b FillRow) int { return cmp.Compare(a.Symbol, b.Symbol) },
|
|
Text: func(f FillRow) string { return f.Symbol }},
|
|
{ID: "side", Title: msg(c, "order_table.side"), Text: func(f FillRow) string { return msg(c, fmt.Sprintf("order_entry.side%d", f.Side)) },
|
|
Cell: func(f FillRow) { tradingSide(c, f.Side) }},
|
|
num("price", "order_book.price", dec, func(f FillRow) float64 { return f.Price }),
|
|
num("qty", "order_entry.quantity", opts.QuantityDecimals, func(f FillRow) float64 { return f.Quantity }),
|
|
num("fee", "trade_history_table.fee", dec, func(f FillRow) float64 { return f.Fee }),
|
|
}
|
|
d := Density(c)
|
|
var r TradeHistoryResult
|
|
r.Element = ui.Column(c).Gap(d.Unit() * 2).Disabled(opts.Disabled).Grow(1).FillWidth().MinHeight(0)
|
|
disabled := r.Element.IsDisabled()
|
|
r.Element.Children(func() {
|
|
ui.Row(c).Gap(d.Unit() * 2).AlignItems(ui.Center).Children(func() {
|
|
if DateRangePicker(c, &s.Range, DateRangePickerOptions{Label: msg(c, "trade_history_table.range"), Disabled: disabled}).Changed() {
|
|
slog.Debug("mujica: trade_history_table range", "days", s.Range.Days())
|
|
}
|
|
if !s.Range.IsZero() && Button(c, msg(c, "trade_history_table.all"), ButtonOptions{Variant: Ghost, Disabled: disabled}).Clicked() && !disabled {
|
|
s.Range = DateRange{}
|
|
}
|
|
ui.Box(c).Grow(1)
|
|
if Button(c, msg(c, "trade_history_table.export"), ButtonOptions{Variant: Secondary, Icon: icons.Must("upload"), Disabled: disabled || len(shown) == 0}).Clicked() && !disabled {
|
|
r.exported, r.export = shown, true
|
|
slog.Debug("mujica: trade_history_table export", "rows", len(shown))
|
|
}
|
|
})
|
|
r.Table = DataTable(c, &s.Table, shown, DataTableOptions[FillRow, string]{Columns: cols, Key: func(f FillRow) string { return f.ID },
|
|
Status: opts.Status, Error: opts.Error, Empty: msg(c, "trade_history_table.empty")})
|
|
r.Table.Element.Grow(1)
|
|
})
|
|
return r
|
|
}
|