mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-22 14:55:16 +00:00
move files
This commit is contained in:
parent
1c99ca52cb
commit
a3fa292dc5
|
@ -1,7 +1,7 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"github.com/adshao/go-binance"
|
||||
types2 "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
"math"
|
||||
)
|
||||
|
||||
|
@ -22,10 +22,10 @@ func SellVolumeModifier(price float64) float64 {
|
|||
return math.Min(2, math.Exp((price-targetPrice)/flatness))
|
||||
}
|
||||
|
||||
func VolumeByPriceChange(market Market, currentPrice float64, change float64, side binance.SideType) float64 {
|
||||
func VolumeByPriceChange(market Market, currentPrice float64, change float64, side types2.SideType) float64 {
|
||||
volume := BaseVolumeByPriceChange(change)
|
||||
|
||||
if side == binance.SideTypeSell {
|
||||
if side == types2.SideTypeSell {
|
||||
volume *= SellVolumeModifier(currentPrice)
|
||||
} else {
|
||||
volume *= BuyVolumeModifier(currentPrice)
|
||||
|
|
25
bbgo/config/config.go
Normal file
25
bbgo/config/config.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func LoadConfigFile(filename string, v interface{}) error {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func SaveConfigFile(filename string, v interface{}) error {
|
||||
out, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, out, 0644)
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package bbgo
|
||||
|
||||
import "github.com/c9s/bbgo/pkg/types"
|
||||
import "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
|
||||
type TradingContext struct {
|
||||
KLineWindowSize int
|
||||
|
|
|
@ -3,7 +3,8 @@ package binance
|
|||
import (
|
||||
"context"
|
||||
"github.com/adshao/go-binance"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
types2 "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/sirupsen/logrus"
|
||||
"strconv"
|
||||
|
@ -44,7 +45,7 @@ func (e *Exchange) NewPrivateStream(ctx context.Context) (*PrivateStream, error)
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (e *Exchange) SubmitOrder(ctx context.Context, order *types.Order) error {
|
||||
func (e *Exchange) SubmitOrder(ctx context.Context, order *types2.Order) error {
|
||||
/*
|
||||
limit order example
|
||||
|
||||
|
@ -60,7 +61,7 @@ func (e *Exchange) SubmitOrder(ctx context.Context, order *types.Order) error {
|
|||
|
||||
req := e.Client.NewCreateOrderService().
|
||||
Symbol(order.Symbol).
|
||||
Side(order.Side).
|
||||
Side(binance.SideType(order.Side)).
|
||||
Type(order.Type).
|
||||
Quantity(order.VolumeStr)
|
||||
|
||||
|
@ -103,7 +104,7 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol, interval string, lim
|
|||
return kLines, nil
|
||||
}
|
||||
|
||||
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, startTime time.Time) (trades []types.Trade, err error) {
|
||||
func (e *Exchange) QueryTrades(ctx context.Context, symbol string, startTime time.Time) (trades []types2.Trade, err error) {
|
||||
logrus.Infof("[binance] querying %s trades from %s", symbol, startTime)
|
||||
|
||||
var lastTradeID int64 = 0
|
||||
|
@ -159,7 +160,7 @@ func (e *Exchange) QueryTrades(ctx context.Context, symbol string, startTime tim
|
|||
return nil, err
|
||||
}
|
||||
|
||||
trades = append(trades, types.Trade{
|
||||
trades = append(trades, types2.Trade{
|
||||
ID: t.ID,
|
||||
Price: price,
|
||||
Volume: quantity,
|
|
@ -4,7 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
types2 "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
"github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/valyala/fastjson"
|
||||
"time"
|
||||
|
@ -83,13 +84,13 @@ type ExecutionReportEvent struct {
|
|||
OrderCreationTime int `json:"O"`
|
||||
}
|
||||
|
||||
func (e *ExecutionReportEvent) Trade() (*types.Trade, error) {
|
||||
func (e *ExecutionReportEvent) Trade() (*types2.Trade, error) {
|
||||
if e.CurrentExecutionType != "TRADE" {
|
||||
return nil, errors.New("execution report is not a trade")
|
||||
}
|
||||
|
||||
tt := time.Unix(0, e.TransactionTime/1000000)
|
||||
return &types.Trade{
|
||||
return &types2.Trade{
|
||||
ID: e.TradeID,
|
||||
Symbol: e.Symbol,
|
||||
Price: util.MustParseFloat(e.LastExecutedPrice),
|
|
@ -1,235 +0,0 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/adshao/go-binance"
|
||||
"github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
binance2 "github.com/c9s/bbgo/pkg/exchange/binance"
|
||||
types2 "github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/slack-go/slack"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const epsilon = 0.0000001
|
||||
|
||||
func NotZero(v float64) bool {
|
||||
return math.Abs(v) > epsilon
|
||||
}
|
||||
|
||||
type KLineDetector struct {
|
||||
Name string `json:"name"`
|
||||
Interval string `json:"interval"`
|
||||
|
||||
// MinMaxPriceChange is the minimal max price change trigger
|
||||
MinMaxPriceChange float64 `json:"minMaxPriceChange"`
|
||||
|
||||
// MaxMaxPriceChange is the max - max price change trigger
|
||||
MaxMaxPriceChange float64 `json:"maxMaxPriceChange"`
|
||||
|
||||
EnableMinThickness bool `json:"enableMinThickness"`
|
||||
MinThickness float64 `json:"minThickness"`
|
||||
|
||||
EnableMaxShadowRatio bool `json:"enableMaxShadowRatio"`
|
||||
MaxShadowRatio float64 `json:"maxShadowRatio"`
|
||||
|
||||
EnableLookBack bool `json:"enableLookBack"`
|
||||
LookBackFrames int `json:"lookBackFrames"`
|
||||
|
||||
MinProfitPriceTick float64 `json:"minProfitPriceTick"`
|
||||
|
||||
DelayMilliseconds int `json:"delayMsec"`
|
||||
Stop bool `json:"stop"`
|
||||
}
|
||||
|
||||
func (d *KLineDetector) SlackAttachment() slack.Attachment {
|
||||
var name = "Detector "
|
||||
|
||||
if len(d.Name) > 0 {
|
||||
name += " " + d.Name
|
||||
}
|
||||
|
||||
name += fmt.Sprintf(" %s", d.Interval)
|
||||
|
||||
if d.EnableLookBack {
|
||||
name += fmt.Sprintf(" x %d", d.LookBackFrames)
|
||||
}
|
||||
|
||||
var maxPriceChangeRange = fmt.Sprintf("%.2f ~ NO LIMIT", d.MinMaxPriceChange)
|
||||
if NotZero(d.MaxMaxPriceChange) {
|
||||
maxPriceChangeRange = fmt.Sprintf("%.2f ~ %.2f", d.MinMaxPriceChange, d.MaxMaxPriceChange)
|
||||
}
|
||||
name += " MaxMaxPriceChange " + maxPriceChangeRange
|
||||
|
||||
var fields = []slack.AttachmentField{
|
||||
{
|
||||
Title: "Interval",
|
||||
Value: d.Interval,
|
||||
Short: true,
|
||||
},
|
||||
}
|
||||
|
||||
if d.EnableMinThickness && NotZero(d.MinThickness) {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "MinThickness",
|
||||
Value: util.FormatFloat(d.MinThickness, 4),
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if d.EnableMaxShadowRatio && NotZero(d.MaxShadowRatio) {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "MaxShadowRatio",
|
||||
Value: util.FormatFloat(d.MaxShadowRatio, 4),
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
if d.EnableLookBack {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "LookBackFrames",
|
||||
Value: strconv.Itoa(d.LookBackFrames),
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
||||
return slack.Attachment{
|
||||
Color: "",
|
||||
Fallback: "",
|
||||
ID: 0,
|
||||
Title: name,
|
||||
Pretext: "",
|
||||
Text: "",
|
||||
Fields: fields,
|
||||
Footer: "",
|
||||
FooterIcon: "",
|
||||
Ts: "",
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (d *KLineDetector) String() string {
|
||||
var name = fmt.Sprintf("Detector %s (%f < x < %f)", d.Interval, d.MinMaxPriceChange, d.MaxMaxPriceChange)
|
||||
|
||||
if d.EnableMinThickness {
|
||||
name += fmt.Sprintf(" [MinThickness: %f]", d.MinThickness)
|
||||
}
|
||||
|
||||
if d.EnableLookBack {
|
||||
name += fmt.Sprintf(" [LookBack: %d]", d.LookBackFrames)
|
||||
}
|
||||
if d.EnableMaxShadowRatio {
|
||||
name += fmt.Sprintf(" [MaxShadowRatio: %f]", d.MaxShadowRatio)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func (d *KLineDetector) NewOrder(e *binance2.KLineEvent, tradingCtx *TradingContext) *types2.Order {
|
||||
var kline types.KLineOrWindow = e.KLine
|
||||
if d.EnableLookBack {
|
||||
klineWindow := tradingCtx.KLineWindows[e.KLine.Interval]
|
||||
if len(klineWindow) >= d.LookBackFrames {
|
||||
kline = klineWindow.Tail(d.LookBackFrames)
|
||||
}
|
||||
}
|
||||
|
||||
var trend = kline.GetTrend()
|
||||
|
||||
var side binance.SideType
|
||||
if trend < 0 {
|
||||
side = binance.SideTypeBuy
|
||||
} else if trend > 0 {
|
||||
side = binance.SideTypeSell
|
||||
}
|
||||
|
||||
var volume = tradingCtx.Market.FormatVolume(VolumeByPriceChange(tradingCtx.Market, kline.GetClose(), kline.GetChange(), side))
|
||||
return &types2.Order{
|
||||
Symbol: e.KLine.Symbol,
|
||||
Type: binance.OrderTypeMarket,
|
||||
Side: side,
|
||||
VolumeStr: volume,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *KLineDetector) Detect(e *binance2.KLineEvent, tradingCtx *TradingContext) (reason string, kline types.KLineOrWindow, ok bool) {
|
||||
kline = e.KLine
|
||||
|
||||
// if the 3m trend is drop, do not buy, let 5m window handle it.
|
||||
if d.EnableLookBack {
|
||||
klineWindow := tradingCtx.KLineWindows[e.KLine.Interval]
|
||||
if len(klineWindow) >= d.LookBackFrames {
|
||||
kline = klineWindow.Tail(d.LookBackFrames)
|
||||
}
|
||||
/*
|
||||
if lookbackKline.AllDrop() {
|
||||
trader.Infof("1m window all drop down (%d frames), do not buy: %+v", d.LookBackFrames, klineWindow)
|
||||
} else if lookbackKline.AllRise() {
|
||||
trader.Infof("1m window all rise up (%d frames), do not sell: %+v", d.LookBackFrames, klineWindow)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
var maxChange = math.Abs(kline.GetMaxChange())
|
||||
|
||||
if maxChange < d.MinMaxPriceChange {
|
||||
return "", kline, false
|
||||
}
|
||||
|
||||
if NotZero(d.MaxMaxPriceChange) && maxChange > d.MaxMaxPriceChange {
|
||||
return fmt.Sprintf("exceeded max price change %.4f > %.4f", maxChange, d.MaxMaxPriceChange), kline, false
|
||||
}
|
||||
|
||||
if d.EnableMinThickness {
|
||||
if kline.GetThickness() < d.MinThickness {
|
||||
return fmt.Sprintf("kline too thin. %.4f < min kline thickness %.4f", kline.GetThickness(), d.MinThickness), kline, false
|
||||
}
|
||||
}
|
||||
|
||||
var trend = kline.GetTrend()
|
||||
if d.EnableMaxShadowRatio {
|
||||
if trend > 0 {
|
||||
if kline.GetUpperShadowRatio() > d.MaxShadowRatio {
|
||||
return fmt.Sprintf("kline upper shadow ratio too high. %.4f > %.4f (MaxShadowRatio)", kline.GetUpperShadowRatio(), d.MaxShadowRatio), kline, false
|
||||
}
|
||||
} else if trend < 0 {
|
||||
if kline.GetLowerShadowRatio() > d.MaxShadowRatio {
|
||||
return fmt.Sprintf("kline lower shadow ratio too high. %.4f > %.4f (MaxShadowRatio)", kline.GetLowerShadowRatio(), d.MaxShadowRatio), kline, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if trend > 0 && kline.BounceUp() { // trend up, ignore bounce up
|
||||
|
||||
return fmt.Sprintf("bounce up, do not sell, kline mid: %.4f", kline.Mid()), kline, false
|
||||
|
||||
} else if trend < 0 && kline.BounceDown() { // trend down, ignore bounce down
|
||||
|
||||
return fmt.Sprintf("bounce down, do not buy, kline mid: %.4f", kline.Mid()), kline, false
|
||||
|
||||
}
|
||||
|
||||
if NotZero(d.MinProfitPriceTick) {
|
||||
|
||||
// do not buy too early if it's greater than the average bid price + min profit tick
|
||||
if trend < 0 && kline.GetClose() > (tradingCtx.AverageBidPrice-d.MinProfitPriceTick) {
|
||||
return fmt.Sprintf("price %f is greater than the average price + min profit tick %f", kline.GetClose(), tradingCtx.AverageBidPrice - d.MinProfitPriceTick), kline, false
|
||||
}
|
||||
|
||||
// do not sell too early if it's less than the average bid price + min profit tick
|
||||
if trend > 0 && kline.GetClose() < (tradingCtx.AverageBidPrice+d.MinProfitPriceTick) {
|
||||
return fmt.Sprintf("price %f is less than the average price + min profit tick %f", kline.GetClose(), tradingCtx.AverageBidPrice + d.MinProfitPriceTick), kline, false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
if toPrice(kline.GetClose()) == toPrice(kline.GetLow()) {
|
||||
return fmt.Sprintf("close near the lowest price, the price might continue to drop."), false
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return "", kline, true
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
package bbgo
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
types2 "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -11,12 +11,12 @@ type ProfitAndLossCalculator struct {
|
|||
Symbol string
|
||||
StartTime time.Time
|
||||
CurrentPrice float64
|
||||
Trades []types.Trade
|
||||
Trades []types2.Trade
|
||||
|
||||
CurrencyPrice map[string]float64
|
||||
}
|
||||
|
||||
func (c *ProfitAndLossCalculator) AddTrade(trade types.Trade) {
|
||||
func (c *ProfitAndLossCalculator) AddTrade(trade types2.Trade) {
|
||||
c.Trades = append(c.Trades, trade)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,9 @@ package bbgo
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/exchange/binance"
|
||||
"github.com/c9s/bbgo/pkg/bbgo/exchange/binance"
|
||||
types2 "github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
"github.com/c9s/bbgo/pkg/slack/slackstyle"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/leekchan/accounting"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -76,7 +76,7 @@ func (t *Trader) Errorf(err error, format string, args ...interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Trader) ReportTrade(e *binance.ExecutionReportEvent, trade *types.Trade) {
|
||||
func (t *Trader) ReportTrade(e *binance.ExecutionReportEvent, trade *types2.Trade) {
|
||||
var color = ""
|
||||
if trade.IsBuyer {
|
||||
color = "#228B22"
|
||||
|
@ -171,7 +171,7 @@ func (t *Trader) ReportPnL() {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Trader) SubmitOrder(ctx context.Context, order *types.Order) {
|
||||
func (t *Trader) SubmitOrder(ctx context.Context, order *types2.Order) {
|
||||
t.Infof(":memo: Submitting %s order on side %s with volume: %s", order.Type, order.Side, order.VolumeStr, order.SlackAttachment())
|
||||
|
||||
err := t.Exchange.SubmitOrder(ctx, order)
|
||||
|
|
|
@ -2,13 +2,6 @@ package types
|
|||
|
||||
import "time"
|
||||
|
||||
type Trade interface {
|
||||
GetPrice() float64
|
||||
GetVolume() float64
|
||||
GetFeeCurrency() string
|
||||
GetFeeAmount() float64
|
||||
}
|
||||
|
||||
type Exchange interface {
|
||||
QueryKLines(interval string, startFrom time.Time, endTo time.Time) []KLineOrWindow
|
||||
QueryTrades(symbol string, startFrom time.Time) []Trade
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
package types
|
||||
|
||||
import "github.com/slack-go/slack"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/slack-go/slack"
|
||||
"math"
|
||||
)
|
||||
|
||||
type KLineOrWindow interface {
|
||||
GetTrend() int
|
||||
|
@ -22,3 +27,408 @@ type KLineOrWindow interface {
|
|||
SlackAttachment() slack.Attachment
|
||||
}
|
||||
|
||||
// KLine uses binance's kline as the standard structure
|
||||
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 util.MustParseFloat(k.High)
|
||||
}
|
||||
|
||||
func (k KLine) GetLow() float64 {
|
||||
return util.MustParseFloat(k.Low)
|
||||
}
|
||||
|
||||
func (k KLine) GetOpen() float64 {
|
||||
return util.MustParseFloat(k.Open)
|
||||
}
|
||||
|
||||
func (k KLine) GetClose() float64 {
|
||||
return util.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 Green
|
||||
} else if k.GetTrend() < 0 {
|
||||
return Red
|
||||
}
|
||||
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: util.FormatFloat(k.Mid(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Change",
|
||||
Value: util.FormatFloat(k.GetChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Max Change",
|
||||
Value: util.FormatFloat(k.GetMaxChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Thickness",
|
||||
Value: util.FormatFloat(k.GetThickness(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "UpperShadowRatio",
|
||||
Value: util.FormatFloat(k.GetUpperShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "LowerShadowRatio",
|
||||
Value: util.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) Color() string {
|
||||
if k.GetTrend() > 0 {
|
||||
return Green
|
||||
} else if k.GetTrend() < 0 {
|
||||
return Red
|
||||
}
|
||||
return "#f0f0f0"
|
||||
}
|
||||
|
||||
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 (k KLineWindow) SlackAttachment() slack.Attachment {
|
||||
return slack.Attachment{
|
||||
Text: "KLine",
|
||||
Color: k.Color(),
|
||||
Fields: []slack.AttachmentField{
|
||||
{
|
||||
Title: "Open",
|
||||
Value: util.FormatFloat(k.GetOpen(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Close",
|
||||
Value: util.FormatFloat(k.GetClose(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "High",
|
||||
Value: util.FormatFloat(k.GetHigh(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Low",
|
||||
Value: util.FormatFloat(k.GetLow(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Mid",
|
||||
Value: util.FormatFloat(k.Mid(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Change",
|
||||
Value: util.FormatFloat(k.GetChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Max Change",
|
||||
Value: util.FormatFloat(k.GetMaxChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Thickness",
|
||||
Value: util.FormatFloat(k.GetThickness(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "UpperShadowRatio",
|
||||
Value: util.FormatFloat(k.GetUpperShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "LowerShadowRatio",
|
||||
Value: util.FormatFloat(k.GetLowerShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
},
|
||||
Footer: "",
|
||||
FooterIcon: "",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
type Order struct {
|
||||
Symbol string
|
||||
Side binance.SideType
|
||||
Side SideType
|
||||
Type binance.OrderType
|
||||
VolumeStr string
|
||||
PriceStr string
|
||||
|
@ -27,7 +27,7 @@ func (o *Order) SlackAttachment() slack.Attachment {
|
|||
}
|
||||
|
||||
return slack.Attachment{
|
||||
Color: SideToColorName(SideType(o.Side)),
|
||||
Color: SideToColorName(o.Side),
|
||||
Title: string(o.Type) + " Order " + string(o.Side),
|
||||
// Text: "",
|
||||
Fields: fields,
|
414
types/kline.go
414
types/kline.go
|
@ -1,414 +0,0 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/slack-go/slack"
|
||||
"math"
|
||||
)
|
||||
|
||||
// KLine uses binance's kline as the standard structure
|
||||
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 util.MustParseFloat(k.High)
|
||||
}
|
||||
|
||||
func (k KLine) GetLow() float64 {
|
||||
return util.MustParseFloat(k.Low)
|
||||
}
|
||||
|
||||
func (k KLine) GetOpen() float64 {
|
||||
return util.MustParseFloat(k.Open)
|
||||
}
|
||||
|
||||
func (k KLine) GetClose() float64 {
|
||||
return util.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 Green
|
||||
} else if k.GetTrend() < 0 {
|
||||
return Red
|
||||
}
|
||||
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: util.FormatFloat(k.Mid(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Change",
|
||||
Value: util.FormatFloat(k.GetChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Max Change",
|
||||
Value: util.FormatFloat(k.GetMaxChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Thickness",
|
||||
Value: util.FormatFloat(k.GetThickness(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "UpperShadowRatio",
|
||||
Value: util.FormatFloat(k.GetUpperShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "LowerShadowRatio",
|
||||
Value: util.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) Color() string {
|
||||
if k.GetTrend() > 0 {
|
||||
return Green
|
||||
} else if k.GetTrend() < 0 {
|
||||
return Red
|
||||
}
|
||||
return "#f0f0f0"
|
||||
}
|
||||
|
||||
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 (k KLineWindow) SlackAttachment() slack.Attachment {
|
||||
return slack.Attachment{
|
||||
Text: "KLine",
|
||||
Color: k.Color(),
|
||||
Fields: []slack.AttachmentField{
|
||||
{
|
||||
Title: "Open",
|
||||
Value: util.FormatFloat(k.GetOpen(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Close",
|
||||
Value: util.FormatFloat(k.GetClose(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "High",
|
||||
Value: util.FormatFloat(k.GetHigh(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Low",
|
||||
Value: util.FormatFloat(k.GetLow(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Mid",
|
||||
Value: util.FormatFloat(k.Mid(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Change",
|
||||
Value: util.FormatFloat(k.GetChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Max Change",
|
||||
Value: util.FormatFloat(k.GetMaxChange(), 2),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Thickness",
|
||||
Value: util.FormatFloat(k.GetThickness(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "UpperShadowRatio",
|
||||
Value: util.FormatFloat(k.GetUpperShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "LowerShadowRatio",
|
||||
Value: util.FormatFloat(k.GetLowerShadowRatio(), 4),
|
||||
Short: true,
|
||||
},
|
||||
},
|
||||
Footer: "",
|
||||
FooterIcon: "",
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -29,3 +30,9 @@ func MustParseFloat(s string) float64 {
|
|||
return v
|
||||
}
|
||||
|
||||
const epsilon = 0.0000001
|
||||
|
||||
func NotZero(v float64) bool {
|
||||
return math.Abs(v) > epsilon
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package bbgo
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
Loading…
Reference in New Issue
Block a user