mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
362 lines
6.8 KiB
Go
362 lines
6.8 KiB
Go
package bbgo
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/slack-go/slack"
|
|
"math"
|
|
"strconv"
|
|
)
|
|
|
|
type KLineEvent struct {
|
|
EventBase
|
|
Symbol string `json:"s"`
|
|
KLine *KLine `json:"k,omitempty"`
|
|
}
|
|
|
|
type KLine struct {
|
|
StartTime int64 `json:"t"`
|
|
EndTime int64 `json:"T"`
|
|
|
|
Symbol string `json:"s"`
|
|
Interval string `json:"i"`
|
|
|
|
Open string `json:"o"`
|
|
Close string `json:"c"`
|
|
High string `json:"h"`
|
|
Low string `json:"l"`
|
|
Volume string `json:"V"` // taker buy base asset volume (like 10 BTC)
|
|
QuoteVolume string `json:"Q"` // taker buy quote asset volume (like 1000USDT)
|
|
|
|
LastTradeID int `json:"L"`
|
|
NumberOfTrades int64 `json:"n"`
|
|
Closed bool `json:"x"`
|
|
}
|
|
|
|
func (k KLine) Mid() float64 {
|
|
return (k.GetHigh() + k.GetLow()) / 2
|
|
}
|
|
|
|
// green candle with open and close near high price
|
|
func (k KLine) BounceUp() bool {
|
|
mid := k.Mid()
|
|
trend := k.GetTrend()
|
|
return trend > 0 && k.GetOpen() > mid && k.GetClose() > mid
|
|
}
|
|
|
|
// red candle with open and close near low price
|
|
func (k KLine) BounceDown() bool {
|
|
mid := k.Mid()
|
|
trend := k.GetTrend()
|
|
return trend > 0 && k.GetOpen() < mid && k.GetClose() < mid
|
|
}
|
|
|
|
func (k KLine) GetTrend() int {
|
|
o := k.GetOpen()
|
|
c := k.GetClose()
|
|
|
|
if c > o {
|
|
return 1
|
|
} else if c < o {
|
|
return -1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (k KLine) GetHigh() float64 {
|
|
return MustParseFloat(k.High)
|
|
}
|
|
|
|
func (k KLine) GetLow() float64 {
|
|
return MustParseFloat(k.Low)
|
|
}
|
|
|
|
func (k KLine) GetOpen() float64 {
|
|
return MustParseFloat(k.Open)
|
|
}
|
|
|
|
func (k KLine) GetClose() float64 {
|
|
return MustParseFloat(k.Close)
|
|
}
|
|
|
|
func (k KLine) GetMaxChange() float64 {
|
|
return k.GetHigh() - k.GetLow()
|
|
}
|
|
|
|
// GetThickness returns the thickness of the kline. 1 => thick, 0.1 => thin
|
|
func (k KLine) GetThickness() float64 {
|
|
return math.Abs(k.GetChange()) / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLine) GetUpperShadowRatio() float64 {
|
|
return k.GetUpperShadowHeight() / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLine) GetUpperShadowHeight() float64 {
|
|
high := k.GetHigh()
|
|
if k.GetOpen() > k.GetClose() {
|
|
return high - k.GetOpen()
|
|
}
|
|
return high - k.GetClose()
|
|
}
|
|
|
|
func (k KLine) GetLowerShadowRatio() float64 {
|
|
return k.GetLowerShadowHeight() / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLine) GetLowerShadowHeight() float64 {
|
|
low := k.GetLow()
|
|
if k.GetOpen() < k.GetClose() {
|
|
return k.GetOpen() - low
|
|
}
|
|
return k.GetClose() - low
|
|
}
|
|
|
|
// GetBody returns the height of the candle real body
|
|
func (k KLine) GetBody() float64 {
|
|
return k.GetChange()
|
|
}
|
|
|
|
func (k KLine) GetChange() float64 {
|
|
return k.GetClose() - k.GetOpen()
|
|
}
|
|
|
|
func (k KLine) String() string {
|
|
return fmt.Sprintf("%s %s Open: % 14s Close: % 14s High: % 14s Low: % 14s Volume: % 15s Change: % 11f Max Change: % 11f", k.Symbol, k.Interval, k.Open, k.Close, k.High, k.Low, k.Volume, k.GetChange(), k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLine) Color() string {
|
|
if k.GetTrend() > 0 {
|
|
return "#228B22"
|
|
} else if k.GetTrend() < 0 {
|
|
return "#DC143C"
|
|
}
|
|
return "#f0f0f0"
|
|
}
|
|
|
|
func (k KLine) SlackAttachment() slack.Attachment {
|
|
return slack.Attachment{
|
|
Text: "KLine",
|
|
Color: k.Color(),
|
|
Fields: []slack.AttachmentField{
|
|
{
|
|
Title: "Open",
|
|
Value: k.Open,
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Close",
|
|
Value: k.Close,
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "High",
|
|
Value: k.High,
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Low",
|
|
Value: k.Low,
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Mid",
|
|
Value: formatFloat(k.Mid(), 2),
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Change",
|
|
Value: formatFloat(k.GetChange(), 2),
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Max Change",
|
|
Value: formatFloat(k.GetMaxChange(), 2),
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "Thickness",
|
|
Value: formatFloat(k.GetThickness(), 4),
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "UpperShadowRatio",
|
|
Value: formatFloat(k.GetUpperShadowRatio(), 4),
|
|
Short: true,
|
|
},
|
|
{
|
|
Title: "LowerShadowRatio",
|
|
Value: formatFloat(k.GetLowerShadowRatio(), 4),
|
|
Short: true,
|
|
},
|
|
},
|
|
Footer: "",
|
|
FooterIcon: "",
|
|
}
|
|
}
|
|
|
|
|
|
|
|
type KLineWindow []KLine
|
|
|
|
func (k KLineWindow) Len() int {
|
|
return len(k)
|
|
}
|
|
|
|
func (k KLineWindow) GetOpen() float64 {
|
|
return k[0].GetOpen()
|
|
}
|
|
|
|
func (k KLineWindow) GetClose() float64 {
|
|
end := len(k) - 1
|
|
return k[end].GetClose()
|
|
}
|
|
|
|
func (k KLineWindow) GetHigh() float64 {
|
|
high := k.GetOpen()
|
|
for _, line := range k {
|
|
val := line.GetHigh()
|
|
if val > high {
|
|
high = val
|
|
}
|
|
}
|
|
return high
|
|
}
|
|
|
|
func (k KLineWindow) GetLow() float64 {
|
|
low := k.GetOpen()
|
|
for _, line := range k {
|
|
val := line.GetLow()
|
|
if val < low {
|
|
low = val
|
|
}
|
|
}
|
|
return low
|
|
}
|
|
|
|
func (k KLineWindow) GetChange() float64 {
|
|
return k.GetClose() - k.GetOpen()
|
|
}
|
|
|
|
func (k KLineWindow) GetMaxChange() float64 {
|
|
return k.GetHigh() - k.GetLow()
|
|
}
|
|
|
|
func (k KLineWindow) AllDrop() bool {
|
|
for _, n := range k {
|
|
if n.GetTrend() >= 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (k KLineWindow) AllRise() bool {
|
|
for _, n := range k {
|
|
if n.GetTrend() <= 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
|
|
func (k KLineWindow) GetTrend() int {
|
|
o := k.GetOpen()
|
|
c := k.GetClose()
|
|
|
|
if c > o {
|
|
return 1
|
|
} else if c < o {
|
|
return -1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (k KLineWindow) Mid() float64 {
|
|
return k.GetHigh() - k.GetLow()/2
|
|
}
|
|
|
|
// green candle with open and close near high price
|
|
func (k KLineWindow) BounceUp() bool {
|
|
mid := k.Mid()
|
|
trend := k.GetTrend()
|
|
return trend > 0 && k.GetOpen() > mid && k.GetClose() > mid
|
|
}
|
|
|
|
// red candle with open and close near low price
|
|
func (k KLineWindow) BounceDown() bool {
|
|
mid := k.Mid()
|
|
trend := k.GetTrend()
|
|
return trend > 0 && k.GetOpen() < mid && k.GetClose() < mid
|
|
}
|
|
|
|
func (k *KLineWindow) Add(line KLine) {
|
|
*k = append(*k, line)
|
|
}
|
|
|
|
func (k KLineWindow) Take(size int) KLineWindow {
|
|
return k[:size]
|
|
}
|
|
|
|
func (k KLineWindow) Tail(size int) KLineWindow {
|
|
if len(k) <= size {
|
|
return k[:]
|
|
}
|
|
return k[len(k) - size:]
|
|
}
|
|
|
|
func (k *KLineWindow) Truncate(size int) {
|
|
if len(*k) <= size {
|
|
return
|
|
}
|
|
|
|
end := len(*k) - 1
|
|
start := end - size
|
|
if start < 0 {
|
|
start = 0
|
|
}
|
|
*k = (*k)[end-5 : end]
|
|
}
|
|
|
|
func (k KLineWindow) GetBody() float64 {
|
|
return k.GetChange()
|
|
}
|
|
|
|
func (k KLineWindow) GetThickness() float64 {
|
|
return math.Abs(k.GetChange()) / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLineWindow) GetUpperShadowRatio() float64 {
|
|
return k.GetUpperShadowHeight() / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLineWindow) GetUpperShadowHeight() float64 {
|
|
high := k.GetHigh()
|
|
if k.GetOpen() > k.GetClose() {
|
|
return high - k.GetOpen()
|
|
}
|
|
return high - k.GetClose()
|
|
}
|
|
|
|
func (k KLineWindow) GetLowerShadowRatio() float64 {
|
|
return k.GetLowerShadowHeight() / math.Abs(k.GetMaxChange())
|
|
}
|
|
|
|
func (k KLineWindow) GetLowerShadowHeight() float64 {
|
|
low := k.GetLow()
|
|
if k.GetOpen() < k.GetClose() {
|
|
return k.GetOpen() - low
|
|
}
|
|
return k.GetClose() - low
|
|
}
|
|
|
|
|
|
func formatVolume(val float64) string {
|
|
return strconv.FormatFloat(val, 'f', 6, 64)
|
|
}
|
|
|
|
|
|
func formatFloat(val float64, prec int) string {
|
|
return strconv.FormatFloat(val, 'f', prec, 64)
|
|
}
|