mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
fixedpoint for exchange and indicators, some fixes in types
This commit is contained in:
parent
e221f54397
commit
b8bf2af14d
|
@ -70,8 +70,8 @@ func (report AverageCostPnlReport) SlackAttachment() slack.Attachment {
|
|||
Fields: []slack.AttachmentField{
|
||||
{Title: "Profit", Value: types.USD.FormatMoney(report.Profit)},
|
||||
{Title: "Unrealized Profit", Value: types.USD.FormatMoney(report.UnrealizedProfit)},
|
||||
{Title: "Current Price", Value: report.Market.FormatPrice(report.LastPrice.Float64()), Short: true},
|
||||
{Title: "Average Cost", Value: report.Market.FormatPrice(report.AverageCost.Float64()), Short: true},
|
||||
{Title: "Current Price", Value: report.Market.FormatPrice(report.LastPrice), Short: true},
|
||||
{Title: "Average Cost", Value: report.Market.FormatPrice(report.AverageCost), Short: true},
|
||||
|
||||
// FIXME:
|
||||
// {Title: "Fee (USD)", Value: types.USD.FormatMoney(report.FeeInUSD), Short: true},
|
||||
|
|
|
@ -124,7 +124,7 @@ func (m BacktestAccountBalanceMap) BalanceMap() types.BalanceMap {
|
|||
balances[currency] = types.Balance{
|
||||
Currency: currency,
|
||||
Available: value,
|
||||
Locked: 0,
|
||||
Locked: fixedpoint.Zero,
|
||||
}
|
||||
}
|
||||
return balances
|
||||
|
|
|
@ -2,7 +2,6 @@ package binance
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -12,7 +11,6 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
)
|
||||
|
||||
func toGlobalMarket(symbol binance.Symbol) types.Market {
|
||||
|
@ -26,8 +24,8 @@ func toGlobalMarket(symbol binance.Symbol) types.Market {
|
|||
}
|
||||
|
||||
if f := symbol.MinNotionalFilter(); f != nil {
|
||||
market.MinNotional = util.MustParseFloat(f.MinNotional)
|
||||
market.MinAmount = util.MustParseFloat(f.MinNotional)
|
||||
market.MinNotional = fixedpoint.MustNewFromString(f.MinNotional)
|
||||
market.MinAmount = fixedpoint.MustNewFromString(f.MinNotional)
|
||||
}
|
||||
|
||||
// The LOT_SIZE filter defines the quantity (aka "lots" in auction terms) rules for a symbol.
|
||||
|
@ -36,15 +34,15 @@ func toGlobalMarket(symbol binance.Symbol) types.Market {
|
|||
// maxQty defines the maximum quantity/icebergQty allowed.
|
||||
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
|
||||
if f := symbol.LotSizeFilter(); f != nil {
|
||||
market.MinQuantity = util.MustParseFloat(f.MinQuantity)
|
||||
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
|
||||
market.StepSize = util.MustParseFloat(f.StepSize)
|
||||
market.MinQuantity = fixedpoint.MustNewFromString(f.MinQuantity)
|
||||
market.MaxQuantity = fixedpoint.MustNewFromString(f.MaxQuantity)
|
||||
market.StepSize = fixedpoint.MustNewFromString(f.StepSize)
|
||||
}
|
||||
|
||||
if f := symbol.PriceFilter(); f != nil {
|
||||
market.MaxPrice = util.MustParseFloat(f.MaxPrice)
|
||||
market.MinPrice = util.MustParseFloat(f.MinPrice)
|
||||
market.TickSize = util.MustParseFloat(f.TickSize)
|
||||
market.MaxPrice = fixedpoint.MustNewFromString(f.MaxPrice)
|
||||
market.MinPrice = fixedpoint.MustNewFromString(f.MinPrice)
|
||||
market.TickSize = fixedpoint.MustNewFromString(f.TickSize)
|
||||
}
|
||||
|
||||
return market
|
||||
|
@ -62,8 +60,8 @@ func toGlobalFuturesMarket(symbol futures.Symbol) types.Market {
|
|||
}
|
||||
|
||||
if f := symbol.MinNotionalFilter(); f != nil {
|
||||
market.MinNotional = util.MustParseFloat(f.Notional)
|
||||
market.MinAmount = util.MustParseFloat(f.Notional)
|
||||
market.MinNotional = fixedpoint.MustNewFromString(f.Notional)
|
||||
market.MinAmount = fixedpoint.MustNewFromString(f.Notional)
|
||||
}
|
||||
|
||||
// The LOT_SIZE filter defines the quantity (aka "lots" in auction terms) rules for a symbol.
|
||||
|
@ -72,15 +70,15 @@ func toGlobalFuturesMarket(symbol futures.Symbol) types.Market {
|
|||
// maxQty defines the maximum quantity/icebergQty allowed.
|
||||
// stepSize defines the intervals that a quantity/icebergQty can be increased/decreased by.
|
||||
if f := symbol.LotSizeFilter(); f != nil {
|
||||
market.MinQuantity = util.MustParseFloat(f.MinQuantity)
|
||||
market.MaxQuantity = util.MustParseFloat(f.MaxQuantity)
|
||||
market.StepSize = util.MustParseFloat(f.StepSize)
|
||||
market.MinQuantity = fixedpoint.MustNewFromString(f.MinQuantity)
|
||||
market.MaxQuantity = fixedpoint.MustNewFromString(f.MaxQuantity)
|
||||
market.StepSize = fixedpoint.MustNewFromString(f.StepSize)
|
||||
}
|
||||
|
||||
if f := symbol.PriceFilter(); f != nil {
|
||||
market.MaxPrice = util.MustParseFloat(f.MaxPrice)
|
||||
market.MinPrice = util.MustParseFloat(f.MinPrice)
|
||||
market.TickSize = util.MustParseFloat(f.TickSize)
|
||||
market.MaxPrice = fixedpoint.MustNewFromString(f.MaxPrice)
|
||||
market.MinPrice = fixedpoint.MustNewFromString(f.MinPrice)
|
||||
market.TickSize = fixedpoint.MustNewFromString(f.TickSize)
|
||||
}
|
||||
|
||||
return market
|
||||
|
@ -236,13 +234,13 @@ func toGlobalFuturesUserAssets(assets []*futures.AccountAsset) (retAssets types.
|
|||
|
||||
func toGlobalTicker(stats *binance.PriceChangeStats) (*types.Ticker, error) {
|
||||
return &types.Ticker{
|
||||
Volume: util.MustParseFloat(stats.Volume),
|
||||
Last: util.MustParseFloat(stats.LastPrice),
|
||||
Open: util.MustParseFloat(stats.OpenPrice),
|
||||
High: util.MustParseFloat(stats.HighPrice),
|
||||
Low: util.MustParseFloat(stats.LowPrice),
|
||||
Buy: util.MustParseFloat(stats.BidPrice),
|
||||
Sell: util.MustParseFloat(stats.AskPrice),
|
||||
Volume: fixedpoint.MustNewFromString(stats.Volume),
|
||||
Last: fixedpoint.MustNewFromString(stats.LastPrice),
|
||||
Open: fixedpoint.MustNewFromString(stats.OpenPrice),
|
||||
High: fixedpoint.MustNewFromString(stats.HighPrice),
|
||||
Low: fixedpoint.MustNewFromString(stats.LowPrice),
|
||||
Buy: fixedpoint.MustNewFromString(stats.BidPrice),
|
||||
Sell: fixedpoint.MustNewFromString(stats.AskPrice),
|
||||
Time: time.Unix(0, stats.CloseTime*int64(time.Millisecond)),
|
||||
}, nil
|
||||
}
|
||||
|
@ -324,15 +322,15 @@ func toGlobalOrder(binanceOrder *binance.Order, isMargin bool) (*types.Order, er
|
|||
Symbol: binanceOrder.Symbol,
|
||||
Side: toGlobalSideType(binanceOrder.Side),
|
||||
Type: toGlobalOrderType(binanceOrder.Type),
|
||||
Quantity: util.MustParseFloat(binanceOrder.OrigQuantity),
|
||||
Price: util.MustParseFloat(binanceOrder.Price),
|
||||
Quantity: fixedpoint.MustNewFromString(binanceOrder.OrigQuantity),
|
||||
Price: fixedpoint.MustNewFromString(binanceOrder.Price),
|
||||
TimeInForce: string(binanceOrder.TimeInForce),
|
||||
},
|
||||
Exchange: types.ExchangeBinance,
|
||||
IsWorking: binanceOrder.IsWorking,
|
||||
OrderID: uint64(binanceOrder.OrderID),
|
||||
Status: toGlobalOrderStatus(binanceOrder.Status),
|
||||
ExecutedQuantity: util.MustParseFloat(binanceOrder.ExecutedQuantity),
|
||||
ExecutedQuantity: fixedpoint.MustNewFromString(binanceOrder.ExecutedQuantity),
|
||||
CreationTime: types.Time(millisecondTime(binanceOrder.Time)),
|
||||
UpdateTime: types.Time(millisecondTime(binanceOrder.UpdateTime)),
|
||||
IsMargin: isMargin,
|
||||
|
@ -349,14 +347,14 @@ func toGlobalFuturesOrder(futuresOrder *futures.Order, isMargin bool) (*types.Or
|
|||
Type: toGlobalFuturesOrderType(futuresOrder.Type),
|
||||
ReduceOnly: futuresOrder.ReduceOnly,
|
||||
ClosePosition: futuresOrder.ClosePosition,
|
||||
Quantity: util.MustParseFloat(futuresOrder.OrigQuantity),
|
||||
Price: util.MustParseFloat(futuresOrder.Price),
|
||||
Quantity: fixedpoint.MustNewFromString(futuresOrder.OrigQuantity),
|
||||
Price: fixedpoint.MustNewFromString(futuresOrder.Price),
|
||||
TimeInForce: string(futuresOrder.TimeInForce),
|
||||
},
|
||||
Exchange: types.ExchangeBinance,
|
||||
OrderID: uint64(futuresOrder.OrderID),
|
||||
Status: toGlobalFuturesOrderStatus(futuresOrder.Status),
|
||||
ExecutedQuantity: util.MustParseFloat(futuresOrder.ExecutedQuantity),
|
||||
ExecutedQuantity: fixedpoint.MustNewFromString(futuresOrder.ExecutedQuantity),
|
||||
CreationTime: types.Time(millisecondTime(futuresOrder.Time)),
|
||||
UpdateTime: types.Time(millisecondTime(futuresOrder.UpdateTime)),
|
||||
IsMargin: isMargin,
|
||||
|
@ -376,27 +374,27 @@ func toGlobalTrade(t binance.TradeV3, isMargin bool) (*types.Trade, error) {
|
|||
side = types.SideTypeSell
|
||||
}
|
||||
|
||||
price, err := strconv.ParseFloat(t.Price, 64)
|
||||
price, err := fixedpoint.NewFromString(t.Price)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "price parse error, price: %+v", t.Price)
|
||||
}
|
||||
|
||||
quantity, err := strconv.ParseFloat(t.Quantity, 64)
|
||||
quantity, err := fixedpoint.NewFromString(t.Quantity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "quantity parse error, quantity: %+v", t.Quantity)
|
||||
}
|
||||
|
||||
var quoteQuantity = 0.0
|
||||
var quoteQuantity fixedpoint.Value
|
||||
if len(t.QuoteQuantity) > 0 {
|
||||
quoteQuantity, err = strconv.ParseFloat(t.QuoteQuantity, 64)
|
||||
quoteQuantity, err = fixedpoint.NewFromString(t.QuoteQuantity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "quote quantity parse error, quoteQuantity: %+v", t.QuoteQuantity)
|
||||
}
|
||||
} else {
|
||||
quoteQuantity = price * quantity
|
||||
quoteQuantity = price.Mul(quantity)
|
||||
}
|
||||
|
||||
fee, err := strconv.ParseFloat(t.Commission, 64)
|
||||
fee, err := fixedpoint.NewFromString(t.Commission)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "commission parse error, commission: %+v", t.Commission)
|
||||
}
|
||||
|
@ -429,27 +427,27 @@ func toGlobalFuturesTrade(t futures.AccountTrade) (*types.Trade, error) {
|
|||
side = types.SideTypeSell
|
||||
}
|
||||
|
||||
price, err := strconv.ParseFloat(t.Price, 64)
|
||||
price, err := fixedpoint.NewFromString(t.Price)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "price parse error, price: %+v", t.Price)
|
||||
}
|
||||
|
||||
quantity, err := strconv.ParseFloat(t.Quantity, 64)
|
||||
quantity, err := fixedpoint.NewFromString(t.Quantity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "quantity parse error, quantity: %+v", t.Quantity)
|
||||
}
|
||||
|
||||
var quoteQuantity = 0.0
|
||||
var quoteQuantity fixedpoint.Value
|
||||
if len(t.QuoteQuantity) > 0 {
|
||||
quoteQuantity, err = strconv.ParseFloat(t.QuoteQuantity, 64)
|
||||
quoteQuantity, err = fixedpoint.NewFromString(t.QuoteQuantity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "quote quantity parse error, quoteQuantity: %+v", t.QuoteQuantity)
|
||||
}
|
||||
} else {
|
||||
quoteQuantity = price * quantity
|
||||
quoteQuantity = price.Mul(quantity)
|
||||
}
|
||||
|
||||
fee, err := strconv.ParseFloat(t.Commission, 64)
|
||||
fee, err := fixedpoint.NewFromString(t.Commission)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "commission parse error, commission: %+v", t.Commission)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
)
|
||||
|
||||
const BNB = "BNB"
|
||||
|
@ -152,13 +151,13 @@ func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[stri
|
|||
}
|
||||
|
||||
tick := types.Ticker{
|
||||
Volume: util.MustParseFloat(stats.Volume),
|
||||
Last: util.MustParseFloat(stats.LastPrice),
|
||||
Open: util.MustParseFloat(stats.OpenPrice),
|
||||
High: util.MustParseFloat(stats.HighPrice),
|
||||
Low: util.MustParseFloat(stats.LowPrice),
|
||||
Buy: util.MustParseFloat(stats.BidPrice),
|
||||
Sell: util.MustParseFloat(stats.AskPrice),
|
||||
Volume: fixedpoint.MustNewFromString(stats.Volume),
|
||||
Last: fixedpoint.MustNewFromString(stats.LastPrice),
|
||||
Open: fixedpoint.MustNewFromString(stats.OpenPrice),
|
||||
High: fixedpoint.MustNewFromString(stats.HighPrice),
|
||||
Low: fixedpoint.MustNewFromString(stats.LowPrice),
|
||||
Buy: fixedpoint.MustNewFromString(stats.BidPrice),
|
||||
Sell: fixedpoint.MustNewFromString(stats.AskPrice),
|
||||
Time: time.Unix(0, stats.CloseTime*int64(time.Millisecond)),
|
||||
}
|
||||
|
||||
|
@ -197,13 +196,13 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
|
|||
return markets, nil
|
||||
}
|
||||
|
||||
func (e *Exchange) QueryAveragePrice(ctx context.Context, symbol string) (float64, error) {
|
||||
func (e *Exchange) QueryAveragePrice(ctx context.Context, symbol string) (fixedpoint.Value, error) {
|
||||
resp, err := e.Client.NewAveragePriceService().Symbol(symbol).Do(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return fixedpoint.Zero, err
|
||||
}
|
||||
|
||||
return util.MustParseFloat(resp.Price), nil
|
||||
return fixedpoint.MustNewFromString(resp.Price), nil
|
||||
}
|
||||
|
||||
func (e *Exchange) NewStream() types.Stream {
|
||||
|
@ -342,10 +341,10 @@ func (e *Exchange) QueryWithdrawHistory(ctx context.Context, asset string, since
|
|||
Exchange: types.ExchangeBinance,
|
||||
ApplyTime: types.Time(applyTime),
|
||||
Asset: d.Coin,
|
||||
Amount: util.MustParseFloat(d.Amount),
|
||||
Amount: fixedpoint.MustNewFromString(d.Amount),
|
||||
Address: d.Address,
|
||||
TransactionID: d.TxID,
|
||||
TransactionFee: util.MustParseFloat(d.TransactionFee),
|
||||
TransactionFee: fixedpoint.MustNewFromString(d.TransactionFee),
|
||||
WithdrawOrderID: d.WithdrawOrderID,
|
||||
Network: d.Network,
|
||||
Status: status,
|
||||
|
@ -414,7 +413,7 @@ func (e *Exchange) QueryDepositHistory(ctx context.Context, asset string, since,
|
|||
Exchange: types.ExchangeBinance,
|
||||
Time: types.Time(time.Unix(0, d.InsertTime*int64(time.Millisecond))),
|
||||
Asset: d.Coin,
|
||||
Amount: util.MustParseFloat(d.Amount),
|
||||
Amount: fixedpoint.MustNewFromString(d.Amount),
|
||||
Address: d.Address,
|
||||
AddressTag: d.AddressTag,
|
||||
TransactionID: d.TxID,
|
||||
|
@ -754,7 +753,8 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
|
|||
if order.Market.Symbol != "" {
|
||||
req.Quantity(order.Market.FormatQuantity(order.Quantity))
|
||||
} else {
|
||||
req.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.Quantity(order.Quantity.FormatString(8))
|
||||
}
|
||||
|
||||
// set price field for limit orders
|
||||
|
@ -763,7 +763,8 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
|
|||
if order.Market.Symbol != "" {
|
||||
req.Price(order.Market.FormatPrice(order.Price))
|
||||
} else {
|
||||
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.Price(order.Price.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -774,7 +775,8 @@ func (e *Exchange) submitMarginOrder(ctx context.Context, order types.SubmitOrde
|
|||
if order.Market.Symbol != "" {
|
||||
req.StopPrice(order.Market.FormatPrice(order.StopPrice))
|
||||
} else {
|
||||
req.StopPrice(strconv.FormatFloat(order.StopPrice, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.StopPrice(order.StopPrice.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -838,7 +840,8 @@ func (e *Exchange) submitFuturesOrder(ctx context.Context, order types.SubmitOrd
|
|||
if order.Market.Symbol != "" {
|
||||
req.Quantity(order.Market.FormatQuantity(order.Quantity))
|
||||
} else {
|
||||
req.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.Quantity(order.Quantity.FormatString(8))
|
||||
}
|
||||
|
||||
// set price field for limit orders
|
||||
|
@ -847,7 +850,8 @@ func (e *Exchange) submitFuturesOrder(ctx context.Context, order types.SubmitOrd
|
|||
if order.Market.Symbol != "" {
|
||||
req.Price(order.Market.FormatPrice(order.Price))
|
||||
} else {
|
||||
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.Price(order.Price.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -858,7 +862,8 @@ func (e *Exchange) submitFuturesOrder(ctx context.Context, order types.SubmitOrd
|
|||
if order.Market.Symbol != "" {
|
||||
req.StopPrice(order.Market.FormatPrice(order.StopPrice))
|
||||
} else {
|
||||
req.StopPrice(strconv.FormatFloat(order.StopPrice, 'f', 8, 64))
|
||||
// TODO report error
|
||||
req.StopPrice(order.StopPrice.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -975,7 +980,8 @@ func (e *Exchange) submitSpotOrder(ctx context.Context, order types.SubmitOrder)
|
|||
if order.Market.Symbol != "" {
|
||||
req.Quantity(order.Market.FormatQuantity(order.Quantity))
|
||||
} else {
|
||||
req.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
|
||||
// TODO: report error
|
||||
req.Quantity(order.Quantity.FormatString(8))
|
||||
}
|
||||
|
||||
// set price field for limit orders
|
||||
|
@ -984,7 +990,8 @@ func (e *Exchange) submitSpotOrder(ctx context.Context, order types.SubmitOrder)
|
|||
if order.Market.Symbol != "" {
|
||||
req.Price(order.Market.FormatPrice(order.Price))
|
||||
} else {
|
||||
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
||||
// TODO: report error
|
||||
req.Price(order.Price.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -993,7 +1000,8 @@ func (e *Exchange) submitSpotOrder(ctx context.Context, order types.SubmitOrder)
|
|||
if order.Market.Symbol != "" {
|
||||
req.StopPrice(order.Market.FormatPrice(order.StopPrice))
|
||||
} else {
|
||||
req.StopPrice(strconv.FormatFloat(order.StopPrice, 'f', 8, 64))
|
||||
// TODO: report error
|
||||
req.StopPrice(order.StopPrice.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1111,14 +1119,14 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
|
|||
Interval: interval,
|
||||
StartTime: types.NewTimeFromUnix(0, k.OpenTime*int64(time.Millisecond)),
|
||||
EndTime: types.NewTimeFromUnix(0, k.CloseTime*int64(time.Millisecond)),
|
||||
Open: util.MustParseFloat(k.Open),
|
||||
Close: util.MustParseFloat(k.Close),
|
||||
High: util.MustParseFloat(k.High),
|
||||
Low: util.MustParseFloat(k.Low),
|
||||
Volume: util.MustParseFloat(k.Volume),
|
||||
QuoteVolume: util.MustParseFloat(k.QuoteAssetVolume),
|
||||
TakerBuyBaseAssetVolume: util.MustParseFloat(k.TakerBuyBaseAssetVolume),
|
||||
TakerBuyQuoteAssetVolume: util.MustParseFloat(k.TakerBuyQuoteAssetVolume),
|
||||
Open: fixedpoint.MustNewFromString(k.Open),
|
||||
Close: fixedpoint.MustNewFromString(k.Close),
|
||||
High: fixedpoint.MustNewFromString(k.High),
|
||||
Low: fixedpoint.MustNewFromString(k.Low),
|
||||
Volume: fixedpoint.MustNewFromString(k.Volume),
|
||||
QuoteVolume: fixedpoint.MustNewFromString(k.QuoteAssetVolume),
|
||||
TakerBuyBaseAssetVolume: fixedpoint.MustNewFromString(k.TakerBuyBaseAssetVolume),
|
||||
TakerBuyQuoteAssetVolume: fixedpoint.MustNewFromString(k.TakerBuyQuoteAssetVolume),
|
||||
LastTradeID: 0,
|
||||
NumberOfTrades: uint64(k.TradeNum),
|
||||
Closed: true,
|
||||
|
|
|
@ -119,13 +119,13 @@ func (e *ExecutionReportEvent) Order() (*types.Order, error) {
|
|||
ClientOrderID: e.ClientOrderID,
|
||||
Side: toGlobalSideType(binance.SideType(e.Side)),
|
||||
Type: toGlobalOrderType(binance.OrderType(e.OrderType)),
|
||||
Quantity: e.OrderQuantity.Float64(),
|
||||
Price: e.OrderPrice.Float64(),
|
||||
Quantity: e.OrderQuantity,
|
||||
Price: e.OrderPrice,
|
||||
TimeInForce: e.TimeInForce,
|
||||
},
|
||||
OrderID: uint64(e.OrderID),
|
||||
Status: toGlobalOrderStatus(binance.OrderStatusType(e.CurrentOrderStatus)),
|
||||
ExecutedQuantity: e.CumulativeFilledQuantity.Float64(),
|
||||
ExecutedQuantity: e.CumulativeFilledQuantity,
|
||||
CreationTime: types.Time(orderCreationTime),
|
||||
}, nil
|
||||
}
|
||||
|
@ -142,13 +142,13 @@ func (e *ExecutionReportEvent) Trade() (*types.Trade, error) {
|
|||
Symbol: e.Symbol,
|
||||
OrderID: uint64(e.OrderID),
|
||||
Side: toGlobalSideType(binance.SideType(e.Side)),
|
||||
Price: e.LastExecutedPrice.Float64(),
|
||||
Quantity: e.LastExecutedQuantity.Float64(),
|
||||
QuoteQuantity: e.LastQuoteAssetTransactedQuantity.Float64(),
|
||||
Price: e.LastExecutedPrice,
|
||||
Quantity: e.LastExecutedQuantity,
|
||||
QuoteQuantity: e.LastQuoteAssetTransactedQuantity,
|
||||
IsBuyer: e.Side == "BUY",
|
||||
IsMaker: e.IsMaker,
|
||||
Time: types.Time(tt),
|
||||
Fee: e.CommissionAmount.Float64(),
|
||||
Fee: e.CommissionAmount,
|
||||
FeeCurrency: e.CommissionAsset,
|
||||
}, nil
|
||||
}
|
||||
|
@ -528,14 +528,14 @@ func (k *KLine) KLine() types.KLine {
|
|||
Interval: types.Interval(k.Interval),
|
||||
StartTime: types.NewTimeFromUnix(0, k.StartTime*int64(time.Millisecond)),
|
||||
EndTime: types.NewTimeFromUnix(0, k.EndTime*int64(time.Millisecond)),
|
||||
Open: k.Open.Float64(),
|
||||
Close: k.Close.Float64(),
|
||||
High: k.High.Float64(),
|
||||
Low: k.Low.Float64(),
|
||||
Volume: k.Volume.Float64(),
|
||||
QuoteVolume: k.QuoteVolume.Float64(),
|
||||
TakerBuyBaseAssetVolume: k.TakerBuyBaseAssetVolume.Float64(),
|
||||
TakerBuyQuoteAssetVolume: k.TakerBuyQuoteAssetVolume.Float64(),
|
||||
Open: k.Open,
|
||||
Close: k.Close,
|
||||
High: k.High,
|
||||
Low: k.Low,
|
||||
Volume: k.Volume,
|
||||
QuoteVolume: k.QuoteVolume,
|
||||
TakerBuyBaseAssetVolume: k.TakerBuyBaseAssetVolume,
|
||||
TakerBuyQuoteAssetVolume: k.TakerBuyQuoteAssetVolume,
|
||||
LastTradeID: uint64(k.LastTradeID),
|
||||
NumberOfTrades: uint64(k.NumberOfTrades),
|
||||
Closed: k.Closed,
|
||||
|
@ -708,13 +708,13 @@ func (e *OrderTradeUpdateEvent) OrderFutures() (*types.Order, error) {
|
|||
ClientOrderID: e.OrderTrade.ClientOrderID,
|
||||
Side: toGlobalFuturesSideType(futures.SideType(e.OrderTrade.Side)),
|
||||
Type: toGlobalFuturesOrderType(futures.OrderType(e.OrderTrade.OrderType)),
|
||||
Quantity: e.OrderTrade.OriginalQuantity.Float64(),
|
||||
Price: e.OrderTrade.OriginalPrice.Float64(),
|
||||
Quantity: e.OrderTrade.OriginalQuantity,
|
||||
Price: e.OrderTrade.OriginalPrice,
|
||||
TimeInForce: e.OrderTrade.TimeInForce,
|
||||
},
|
||||
OrderID: uint64(e.OrderTrade.OrderId),
|
||||
Status: toGlobalFuturesOrderStatus(futures.OrderStatusType(e.OrderTrade.CurrentOrderStatus)),
|
||||
ExecutedQuantity: e.OrderTrade.OrderFilledAccumulatedQuantity.Float64(),
|
||||
ExecutedQuantity: e.OrderTrade.OrderFilledAccumulatedQuantity,
|
||||
CreationTime: types.Time(orderCreationTime),
|
||||
}, nil
|
||||
}
|
||||
|
@ -731,13 +731,13 @@ func (e *OrderTradeUpdateEvent) TradeFutures() (*types.Trade, error) {
|
|||
Symbol: e.OrderTrade.Symbol,
|
||||
OrderID: uint64(e.OrderTrade.OrderId),
|
||||
Side: toGlobalSideType(binance.SideType(e.OrderTrade.Side)),
|
||||
Price: e.OrderTrade.LastFilledPrice.Float64(),
|
||||
Quantity: e.OrderTrade.OrderLastFilledQuantity.Float64(),
|
||||
QuoteQuantity: e.OrderTrade.OrderFilledAccumulatedQuantity.Float64(),
|
||||
Price: e.OrderTrade.LastFilledPrice,
|
||||
Quantity: e.OrderTrade.OrderLastFilledQuantity,
|
||||
QuoteQuantity: e.OrderTrade.OrderFilledAccumulatedQuantity,
|
||||
IsBuyer: e.OrderTrade.Side == "BUY",
|
||||
IsMaker: e.OrderTrade.IsMaker,
|
||||
Time: types.Time(tt),
|
||||
Fee: e.OrderTrade.CommissionAmount.Float64(),
|
||||
Fee: e.OrderTrade.CommissionAmount,
|
||||
FeeCurrency: e.OrderTrade.CommissionAsset,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -64,14 +63,14 @@ func toGlobalOrder(r order) (types.Order, error) {
|
|||
case "new":
|
||||
o.Status = types.OrderStatusNew
|
||||
case "open":
|
||||
if fixedpoint.NewFromFloat(o.ExecutedQuantity) != fixedpoint.NewFromInt(0) {
|
||||
if !o.ExecutedQuantity.IsZero() {
|
||||
o.Status = types.OrderStatusPartiallyFilled
|
||||
} else {
|
||||
o.Status = types.OrderStatusNew
|
||||
}
|
||||
case "closed":
|
||||
// filled or canceled
|
||||
if fixedpoint.NewFromFloat(o.Quantity) == fixedpoint.NewFromFloat(o.ExecutedQuantity) {
|
||||
if o.Quantity == o.ExecutedQuantity {
|
||||
o.Status = types.OrderStatusFilled
|
||||
} else {
|
||||
// can't distinguish it's canceled or rejected from order response, so always set to canceled
|
||||
|
@ -124,7 +123,7 @@ func toGlobalTrade(f fill) (types.Trade, error) {
|
|||
Exchange: types.ExchangeFTX,
|
||||
Price: f.Price,
|
||||
Quantity: f.Size,
|
||||
QuoteQuantity: f.Price * f.Size,
|
||||
QuoteQuantity: f.Price.Mul(f.Size),
|
||||
Symbol: toGlobalSymbol(f.Market),
|
||||
Side: f.Side,
|
||||
IsBuyer: f.Side == types.SideTypeBuy,
|
||||
|
|
|
@ -38,10 +38,10 @@ type Exchange struct {
|
|||
|
||||
type MarketTicker struct {
|
||||
Market types.Market
|
||||
Price float64
|
||||
Ask float64
|
||||
Bid float64
|
||||
Last float64
|
||||
Price fixedpoint.Value
|
||||
Ask fixedpoint.Value
|
||||
Bid fixedpoint.Value
|
||||
Last fixedpoint.Value
|
||||
}
|
||||
|
||||
type MarketMap map[string]MarketTicker
|
||||
|
@ -138,19 +138,19 @@ func (e *Exchange) _queryMarkets(ctx context.Context) (MarketMap, error) {
|
|||
LocalSymbol: m.Name,
|
||||
// The max precision is length(DefaultPow). For example, currently fixedpoint.DefaultPow
|
||||
// is 1e8, so the max precision will be 8.
|
||||
PricePrecision: fixedpoint.NumFractionalDigits(fixedpoint.NewFromFloat(m.PriceIncrement)),
|
||||
VolumePrecision: fixedpoint.NumFractionalDigits(fixedpoint.NewFromFloat(m.SizeIncrement)),
|
||||
PricePrecision: fixedpoint.NumFractionalDigits(m.PriceIncrement),
|
||||
VolumePrecision: fixedpoint.NumFractionalDigits(m.SizeIncrement),
|
||||
QuoteCurrency: toGlobalCurrency(m.QuoteCurrency),
|
||||
BaseCurrency: toGlobalCurrency(m.BaseCurrency),
|
||||
// FTX only limit your order by `MinProvideSize`, so I assign zero value to unsupported fields:
|
||||
// MinNotional, MinAmount, MaxQuantity, MinPrice and MaxPrice.
|
||||
MinNotional: 0,
|
||||
MinAmount: 0,
|
||||
MinNotional: fixedpoint.Zero,
|
||||
MinAmount: fixedpoint.Zero,
|
||||
MinQuantity: m.MinProvideSize,
|
||||
MaxQuantity: 0,
|
||||
MaxQuantity: fixedpoint.Zero,
|
||||
StepSize: m.SizeIncrement,
|
||||
MinPrice: 0,
|
||||
MaxPrice: 0,
|
||||
MinPrice: fixedpoint.Zero,
|
||||
MaxPrice: fixedpoint.Zero,
|
||||
TickSize: m.PriceIncrement,
|
||||
},
|
||||
Price: m.Price,
|
||||
|
@ -173,9 +173,9 @@ func (e *Exchange) QueryAccount(ctx context.Context) (*types.Account, error) {
|
|||
}
|
||||
|
||||
a := &types.Account{
|
||||
MakerCommission: fixedpoint.NewFromFloat(resp.Result.MakerFee),
|
||||
TakerCommission: fixedpoint.NewFromFloat(resp.Result.TakerFee),
|
||||
TotalAccountValue: fixedpoint.NewFromFloat(resp.Result.TotalAccountValue),
|
||||
MakerCommission: resp.Result.MakerFee,
|
||||
TakerCommission: resp.Result.TakerFee,
|
||||
TotalAccountValue: resp.Result.TotalAccountValue,
|
||||
}
|
||||
|
||||
balances, err := e.QueryAccountBalances(ctx)
|
||||
|
@ -199,8 +199,8 @@ func (e *Exchange) QueryAccountBalances(ctx context.Context) (types.BalanceMap,
|
|||
for _, r := range resp.Result {
|
||||
balances[toGlobalCurrency(r.Coin)] = types.Balance{
|
||||
Currency: toGlobalCurrency(r.Coin),
|
||||
Available: fixedpoint.NewFromFloat(r.Free),
|
||||
Locked: fixedpoint.NewFromFloat(r.Total).Sub(fixedpoint.NewFromFloat(r.Free)),
|
||||
Available: r.Free,
|
||||
Locked: r.Total.Sub(r.Free),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type orderRequest struct {
|
||||
|
@ -28,9 +29,9 @@ type orderRequest struct {
|
|||
type PlaceOrderPayload struct {
|
||||
Market string
|
||||
Side string
|
||||
Price float64
|
||||
Price fixedpoint.Value
|
||||
Type string
|
||||
Size float64
|
||||
Size fixedpoint.Value
|
||||
ReduceOnly bool
|
||||
IOC bool
|
||||
PostOnly bool
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
// ex: 2019-03-05T09:56:55.728933+00:00
|
||||
|
@ -87,9 +88,9 @@ type accountResponse struct {
|
|||
}
|
||||
|
||||
type account struct {
|
||||
MakerFee float64 `json:"makerFee"`
|
||||
TakerFee float64 `json:"takerFee"`
|
||||
TotalAccountValue float64 `json:"totalAccountValue"`
|
||||
MakerFee fixedpoint.Value `json:"makerFee"`
|
||||
TakerFee fixedpoint.Value `json:"takerFee"`
|
||||
TotalAccountValue fixedpoint.Value `json:"totalAccountValue"`
|
||||
}
|
||||
|
||||
type positionsResponse struct {
|
||||
|
@ -117,21 +118,21 @@ type positionsResponse struct {
|
|||
}
|
||||
*/
|
||||
type position struct {
|
||||
Cost float64 `json:"cost"`
|
||||
EntryPrice float64 `json:"entryPrice"`
|
||||
EstimatedLiquidationPrice float64 `json:"estimatedLiquidationPrice"`
|
||||
Cost fixedpoint.Value `json:"cost"`
|
||||
EntryPrice fixedpoint.Value `json:"entryPrice"`
|
||||
EstimatedLiquidationPrice fixedpoint.Value `json:"estimatedLiquidationPrice"`
|
||||
Future string `json:"future"`
|
||||
InitialMarginRequirement float64 `json:"initialMarginRequirement"`
|
||||
LongOrderSize float64 `json:"longOrderSize"`
|
||||
MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"`
|
||||
NetSize float64 `json:"netSize"`
|
||||
OpenSize float64 `json:"openSize"`
|
||||
RealizedPnl float64 `json:"realizedPnl"`
|
||||
ShortOrderSize float64 `json:"shortOrderSize"`
|
||||
InitialMarginRequirement fixedpoint.Value `json:"initialMarginRequirement"`
|
||||
LongOrderSize fixedpoint.Value `json:"longOrderSize"`
|
||||
MaintenanceMarginRequirement fixedpoint.Value `json:"maintenanceMarginRequirement"`
|
||||
NetSize fixedpoint.Value `json:"netSize"`
|
||||
OpenSize fixedpoint.Value `json:"openSize"`
|
||||
RealizedPnl fixedpoint.Value `json:"realizedPnl"`
|
||||
ShortOrderSize fixedpoint.Value `json:"shortOrderSize"`
|
||||
Side string `json:"Side"`
|
||||
Size float64 `json:"size"`
|
||||
UnrealizedPnl float64 `json:"unrealizedPnl"`
|
||||
CollateralUsed float64 `json:"collateralUsed"`
|
||||
Size fixedpoint.Value `json:"size"`
|
||||
UnrealizedPnl fixedpoint.Value `json:"unrealizedPnl"`
|
||||
CollateralUsed fixedpoint.Value `json:"collateralUsed"`
|
||||
}
|
||||
|
||||
type balances struct {
|
||||
|
@ -139,8 +140,8 @@ type balances struct {
|
|||
|
||||
Result []struct {
|
||||
Coin string `json:"coin"`
|
||||
Free float64 `json:"free"`
|
||||
Total float64 `json:"total"`
|
||||
Free fixedpoint.Value `json:"free"`
|
||||
Total fixedpoint.Value `json:"total"`
|
||||
} `json:"result"`
|
||||
}
|
||||
|
||||
|
@ -180,24 +181,24 @@ type market struct {
|
|||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
MinProvideSize float64 `json:"minProvideSize"`
|
||||
Last float64 `json:"last"`
|
||||
Bid float64 `json:"bid"`
|
||||
Ask float64 `json:"ask"`
|
||||
Price float64 `json:"price"`
|
||||
PriceIncrement fixedpoint.Value `json:"priceIncrement"`
|
||||
SizeIncrement fixedpoint.Value `json:"sizeIncrement"`
|
||||
MinProvideSize fixedpoint.Value `json:"minProvideSize"`
|
||||
Last fixedpoint.Value `json:"last"`
|
||||
Bid fixedpoint.Value `json:"bid"`
|
||||
Ask fixedpoint.Value `json:"ask"`
|
||||
Price fixedpoint.Value `json:"price"`
|
||||
Type string `json:"type"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
Underlying string `json:"underlying"`
|
||||
Restricted bool `json:"restricted"`
|
||||
HighLeverageFeeExempt bool `json:"highLeverageFeeExempt"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
QuoteVolume24h float64 `json:"quoteVolume24h"`
|
||||
VolumeUsd24h float64 `json:"volumeUsd24h"`
|
||||
Change1h fixedpoint.Value `json:"change1h"`
|
||||
Change24h fixedpoint.Value `json:"change24h"`
|
||||
ChangeBod fixedpoint.Value `json:"changeBod"`
|
||||
QuoteVolume24h fixedpoint.Value `json:"quoteVolume24h"`
|
||||
VolumeUsd24h fixedpoint.Value `json:"volumeUsd24h"`
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -221,12 +222,12 @@ type HistoricalPricesResponse struct {
|
|||
}
|
||||
|
||||
type Candle struct {
|
||||
Close float64 `json:"close"`
|
||||
High float64 `json:"high"`
|
||||
Low float64 `json:"low"`
|
||||
Open float64 `json:"open"`
|
||||
Close fixedpoint.Value `json:"close"`
|
||||
High fixedpoint.Value `json:"high"`
|
||||
Low fixedpoint.Value `json:"low"`
|
||||
Open fixedpoint.Value `json:"open"`
|
||||
StartTime datetime `json:"startTime"`
|
||||
Volume float64 `json:"volume"`
|
||||
Volume fixedpoint.Value `json:"volume"`
|
||||
}
|
||||
|
||||
type ordersHistoryResponse struct {
|
||||
|
@ -248,16 +249,16 @@ type cancelOrderResponse struct {
|
|||
|
||||
type order struct {
|
||||
CreatedAt datetime `json:"createdAt"`
|
||||
FilledSize float64 `json:"filledSize"`
|
||||
FilledSize fixedpoint.Value `json:"filledSize"`
|
||||
// Future field is not defined in the response format table but in the response example.
|
||||
Future string `json:"future"`
|
||||
ID int64 `json:"id"`
|
||||
Market string `json:"market"`
|
||||
Price float64 `json:"price"`
|
||||
AvgFillPrice float64 `json:"avgFillPrice"`
|
||||
RemainingSize float64 `json:"remainingSize"`
|
||||
Price fixedpoint.Value `json:"price"`
|
||||
AvgFillPrice fixedpoint.Value `json:"avgFillPrice"`
|
||||
RemainingSize fixedpoint.Value `json:"remainingSize"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Size fixedpoint.Value `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Type string `json:"type"`
|
||||
ReduceOnly bool `json:"reduceOnly"`
|
||||
|
@ -304,9 +305,9 @@ type depositHistory struct {
|
|||
Address address `json:"address"`
|
||||
Confirmations int64 `json:"confirmations"`
|
||||
ConfirmedTime datetime `json:"confirmedTime"`
|
||||
Fee float64 `json:"fee"`
|
||||
Fee fixedpoint.Value `json:"fee"`
|
||||
SentTime datetime `json:"sentTime"`
|
||||
Size float64 `json:"size"`
|
||||
Size fixedpoint.Value `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Time datetime `json:"time"`
|
||||
Notes string `json:"notes"`
|
||||
|
@ -360,13 +361,13 @@ type fill struct {
|
|||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
Type string `json:"type"`
|
||||
Side types.SideType `json:"side"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
Price fixedpoint.Value `json:"price"`
|
||||
Size fixedpoint.Value `json:"size"`
|
||||
OrderId uint64 `json:"orderId"`
|
||||
Time datetime `json:"time"`
|
||||
TradeId uint64 `json:"tradeId"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
Fee float64 `json:"fee"`
|
||||
FeeRate fixedpoint.Value `json:"feeRate"`
|
||||
Fee fixedpoint.Value `json:"fee"`
|
||||
FeeCurrency string `json:"feeCurrency"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
}
|
||||
|
@ -379,7 +380,7 @@ type transferResponse struct {
|
|||
type transfer struct {
|
||||
Id uint `json:"id"`
|
||||
Coin string `json:"coin"`
|
||||
Size float64 `json:"size"`
|
||||
Size fixedpoint.Value `json:"size"`
|
||||
Time string `json:"time"`
|
||||
Notes string `json:"notes"`
|
||||
Status string `json:"status"`
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
func toGlobalBalanceMap(accounts []kucoinapi.Account) types.BalanceMap {
|
||||
|
@ -42,28 +43,28 @@ func toGlobalMarket(m kucoinapi.Symbol) types.Market {
|
|||
VolumePrecision: int(math.Log10(m.BaseIncrement.Float64())),
|
||||
QuoteCurrency: m.QuoteCurrency,
|
||||
BaseCurrency: m.BaseCurrency,
|
||||
MinNotional: m.QuoteMinSize.Float64(),
|
||||
MinAmount: m.QuoteMinSize.Float64(),
|
||||
MinQuantity: m.BaseMinSize.Float64(),
|
||||
MaxQuantity: 0, // not used
|
||||
StepSize: m.BaseIncrement.Float64(),
|
||||
MinNotional: m.QuoteMinSize,
|
||||
MinAmount: m.QuoteMinSize,
|
||||
MinQuantity: m.BaseMinSize,
|
||||
MaxQuantity: fixedpoint.Zero, // not used
|
||||
StepSize: m.BaseIncrement,
|
||||
|
||||
MinPrice: 0, // not used
|
||||
MaxPrice: 0, // not used
|
||||
TickSize: m.PriceIncrement.Float64(),
|
||||
MinPrice: fixedpoint.Zero, // not used
|
||||
MaxPrice: fixedpoint.Zero, // not used
|
||||
TickSize: m.PriceIncrement,
|
||||
}
|
||||
}
|
||||
|
||||
func toGlobalTicker(s kucoinapi.Ticker24H) types.Ticker {
|
||||
return types.Ticker{
|
||||
Time: s.Time.Time(),
|
||||
Volume: s.Volume.Float64(),
|
||||
Last: s.Last.Float64(),
|
||||
Open: s.Last.Float64() - s.ChangePrice.Float64(),
|
||||
High: s.High.Float64(),
|
||||
Low: s.Low.Float64(),
|
||||
Buy: s.Buy.Float64(),
|
||||
Sell: s.Sell.Float64(),
|
||||
Volume: s.Volume,
|
||||
Last: s.Last,
|
||||
Open: s.Last.Sub(s.ChangePrice),
|
||||
High: s.High,
|
||||
Low: s.Low,
|
||||
Buy: s.Buy,
|
||||
Sell: s.Sell,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +147,7 @@ func toGlobalOrderStatus(o kucoinapi.Order) types.OrderStatus {
|
|||
var status types.OrderStatus
|
||||
if o.IsActive {
|
||||
status = types.OrderStatusNew
|
||||
if o.DealSize > 0 {
|
||||
if o.DealSize.Sign() > 0 {
|
||||
status = types.OrderStatusPartiallyFilled
|
||||
}
|
||||
} else if o.CancelExist {
|
||||
|
@ -209,16 +210,16 @@ func toGlobalOrder(o kucoinapi.Order) types.Order {
|
|||
Symbol: toGlobalSymbol(o.Symbol),
|
||||
Side: toGlobalSide(o.Side),
|
||||
Type: toGlobalOrderType(o.Type),
|
||||
Quantity: o.Size.Float64(),
|
||||
Price: o.Price.Float64(),
|
||||
StopPrice: o.StopPrice.Float64(),
|
||||
Quantity: o.Size,
|
||||
Price: o.Price,
|
||||
StopPrice: o.StopPrice,
|
||||
TimeInForce: string(o.TimeInForce),
|
||||
},
|
||||
Exchange: types.ExchangeKucoin,
|
||||
OrderID: hashStringID(o.ID),
|
||||
UUID: o.ID,
|
||||
Status: status,
|
||||
ExecutedQuantity: o.DealSize.Float64(),
|
||||
ExecutedQuantity: o.DealSize,
|
||||
IsWorking: o.IsActive,
|
||||
CreationTime: types.Time(o.CreatedAt.Time()),
|
||||
UpdateTime: types.Time(o.CreatedAt.Time()), // kucoin does not response updated time
|
||||
|
@ -231,15 +232,15 @@ func toGlobalTrade(fill kucoinapi.Fill) types.Trade {
|
|||
ID: hashStringID(fill.TradeId),
|
||||
OrderID: hashStringID(fill.OrderId),
|
||||
Exchange: types.ExchangeKucoin,
|
||||
Price: fill.Price.Float64(),
|
||||
Quantity: fill.Size.Float64(),
|
||||
QuoteQuantity: fill.Funds.Float64(),
|
||||
Price: fill.Price,
|
||||
Quantity: fill.Size,
|
||||
QuoteQuantity: fill.Funds,
|
||||
Symbol: toGlobalSymbol(fill.Symbol),
|
||||
Side: toGlobalSide(string(fill.Side)),
|
||||
IsBuyer: fill.Side == kucoinapi.SideTypeBuy,
|
||||
IsMaker: fill.Liquidity == kucoinapi.LiquidityTypeMaker,
|
||||
Time: types.Time(fill.CreatedAt.Time()),
|
||||
Fee: fill.Fee.Float64(),
|
||||
Fee: fill.Fee,
|
||||
FeeCurrency: toGlobalSymbol(fill.FeeCurrency),
|
||||
}
|
||||
return trade
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
var marketDataLimiter = rate.NewLimiter(rate.Every(500*time.Millisecond), 1)
|
||||
|
@ -184,12 +185,12 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
|
|||
StartTime: types.Time(k.StartTime),
|
||||
EndTime: types.Time(k.StartTime.Add(gi.Duration() - time.Millisecond)),
|
||||
Interval: gi,
|
||||
Open: k.Open.Float64(),
|
||||
Close: k.Close.Float64(),
|
||||
High: k.High.Float64(),
|
||||
Low: k.Low.Float64(),
|
||||
Volume: k.Volume.Float64(),
|
||||
QuoteVolume: k.QuoteVolume.Float64(),
|
||||
Open: k.Open,
|
||||
Close: k.Close,
|
||||
High: k.High,
|
||||
Low: k.Low,
|
||||
Volume: k.Volume,
|
||||
QuoteVolume: k.QuoteVolume,
|
||||
Closed: true,
|
||||
})
|
||||
}
|
||||
|
@ -214,7 +215,8 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
if order.Market.Symbol != "" {
|
||||
req.Size(order.Market.FormatQuantity(order.Quantity))
|
||||
} else {
|
||||
req.Size(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
|
||||
// TODO: report error?
|
||||
req.Size(order.Quantity.FormatString(8))
|
||||
}
|
||||
|
||||
// set price field for limit orders
|
||||
|
@ -223,7 +225,8 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
if order.Market.Symbol != "" {
|
||||
req.Price(order.Market.FormatPrice(order.Price))
|
||||
} else {
|
||||
req.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
||||
// TODO: report error?
|
||||
req.Price(order.Price.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +251,7 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
OrderID: hashStringID(orderResponse.OrderID),
|
||||
UUID: orderResponse.OrderID,
|
||||
Status: types.OrderStatusNew,
|
||||
ExecutedQuantity: 0,
|
||||
ExecutedQuantity: fixedpoint.Zero,
|
||||
IsWorking: true,
|
||||
CreationTime: types.Time(time.Now()),
|
||||
UpdateTime: types.Time(time.Now()),
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/exchange/kucoin/kucoinapi"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
const readTimeout = 30 * time.Second
|
||||
|
@ -116,15 +117,15 @@ func (s *Stream) handlePrivateOrderEvent(e *WebSocketPrivateOrderEvent) {
|
|||
OrderID: hashStringID(e.OrderId),
|
||||
ID: hashStringID(e.TradeId),
|
||||
Exchange: types.ExchangeKucoin,
|
||||
Price: e.MatchPrice.Float64(),
|
||||
Quantity: e.MatchSize.Float64(),
|
||||
QuoteQuantity: e.MatchPrice.Float64() * e.MatchSize.Float64(),
|
||||
Price: e.MatchPrice,
|
||||
Quantity: e.MatchSize,
|
||||
QuoteQuantity: e.MatchPrice.Mul(e.MatchSize),
|
||||
Symbol: toGlobalSymbol(e.Symbol),
|
||||
Side: toGlobalSide(e.Side),
|
||||
IsBuyer: e.Side == "buy",
|
||||
IsMaker: e.Liquidity == "maker",
|
||||
Time: types.Time(e.Ts.Time()),
|
||||
Fee: 0, // not supported
|
||||
Fee: fixedpoint.Zero, // not supported
|
||||
FeeCurrency: "", // not supported
|
||||
})
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ func (s *Stream) handlePrivateOrderEvent(e *WebSocketPrivateOrderEvent) {
|
|||
status = types.OrderStatusCanceled
|
||||
}
|
||||
} else if e.Status == "open" {
|
||||
if e.FilledSize > 0 {
|
||||
if e.FilledSize.Sign() > 0 {
|
||||
status = types.OrderStatusPartiallyFilled
|
||||
}
|
||||
}
|
||||
|
@ -150,14 +151,14 @@ func (s *Stream) handlePrivateOrderEvent(e *WebSocketPrivateOrderEvent) {
|
|||
Symbol: toGlobalSymbol(e.Symbol),
|
||||
Side: toGlobalSide(e.Side),
|
||||
Type: toGlobalOrderType(e.OrderType),
|
||||
Quantity: e.Size.Float64(),
|
||||
Price: e.Price.Float64(),
|
||||
Quantity: e.Size,
|
||||
Price: e.Price,
|
||||
},
|
||||
Exchange: types.ExchangeKucoin,
|
||||
OrderID: hashStringID(e.OrderId),
|
||||
UUID: e.OrderId,
|
||||
Status: status,
|
||||
ExecutedQuantity: e.FilledSize.Float64(),
|
||||
ExecutedQuantity: e.FilledSize,
|
||||
IsWorking: e.Status == "open",
|
||||
CreationTime: types.Time(e.OrderTime.Time()),
|
||||
UpdateTime: types.Time(e.Ts.Time()),
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
)
|
||||
|
||||
type WebSocketMessageType string
|
||||
|
@ -97,12 +96,12 @@ type WebSocketCandleEvent struct {
|
|||
|
||||
func (e *WebSocketCandleEvent) KLine() types.KLine {
|
||||
startTime := types.MustParseUnixTimestamp(e.Candles[0])
|
||||
openPrice := util.MustParseFloat(e.Candles[1])
|
||||
closePrice := util.MustParseFloat(e.Candles[2])
|
||||
highPrice := util.MustParseFloat(e.Candles[3])
|
||||
lowPrice := util.MustParseFloat(e.Candles[4])
|
||||
volume := util.MustParseFloat(e.Candles[5])
|
||||
quoteVolume := util.MustParseFloat(e.Candles[6])
|
||||
openPrice := fixedpoint.MustNewFromString(e.Candles[1])
|
||||
closePrice := fixedpoint.MustNewFromString(e.Candles[2])
|
||||
highPrice := fixedpoint.MustNewFromString(e.Candles[3])
|
||||
lowPrice := fixedpoint.MustNewFromString(e.Candles[4])
|
||||
volume := fixedpoint.MustNewFromString(e.Candles[5])
|
||||
quoteVolume := fixedpoint.MustNewFromString(e.Candles[6])
|
||||
kline := types.KLine{
|
||||
Exchange: types.ExchangeKucoin,
|
||||
Symbol: toGlobalSymbol(e.Symbol),
|
||||
|
|
|
@ -2,7 +2,6 @@ package max
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -11,7 +10,6 @@ import (
|
|||
"github.com/c9s/bbgo/pkg/exchange/max/maxapi"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
)
|
||||
|
||||
func toGlobalCurrency(currency string) string {
|
||||
|
@ -77,23 +75,23 @@ func toGlobalOrderStatus(orderState max.OrderState, executedVolume, remainingVol
|
|||
return types.OrderStatusCanceled
|
||||
|
||||
case max.OrderStateFinalizing, max.OrderStateDone:
|
||||
if executedVolume == 0 {
|
||||
if executedVolume.IsZero() {
|
||||
return types.OrderStatusCanceled
|
||||
} else if remainingVolume == 0 {
|
||||
} else if remainingVolume.IsZero() {
|
||||
return types.OrderStatusFilled
|
||||
}
|
||||
|
||||
return types.OrderStatusFilled
|
||||
|
||||
case max.OrderStateWait:
|
||||
if executedVolume > 0 && remainingVolume > 0 {
|
||||
if executedVolume.Sign() > 0 && remainingVolume.Sign() > 0 {
|
||||
return types.OrderStatusPartiallyFilled
|
||||
}
|
||||
|
||||
return types.OrderStatusNew
|
||||
|
||||
case max.OrderStateConvert:
|
||||
if executedVolume > 0 && remainingVolume > 0 {
|
||||
if executedVolume.Sign() > 0 && remainingVolume.Sign() > 0 {
|
||||
return types.OrderStatusPartiallyFilled
|
||||
}
|
||||
|
||||
|
@ -189,8 +187,8 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
|||
Symbol: toGlobalSymbol(maxOrder.Market),
|
||||
Side: toGlobalSideType(maxOrder.Side),
|
||||
Type: toGlobalOrderType(maxOrder.OrderType),
|
||||
Quantity: util.MustParseFloat(maxOrder.Volume),
|
||||
Price: util.MustParseFloat(maxOrder.Price),
|
||||
Quantity: fixedpoint.MustNewFromString(maxOrder.Volume),
|
||||
Price: fixedpoint.MustNewFromString(maxOrder.Price),
|
||||
TimeInForce: "GTC", // MAX only supports GTC
|
||||
GroupID: maxOrder.GroupID,
|
||||
},
|
||||
|
@ -198,7 +196,7 @@ func toGlobalOrder(maxOrder max.Order) (*types.Order, error) {
|
|||
IsWorking: maxOrder.State == "wait",
|
||||
OrderID: maxOrder.ID,
|
||||
Status: toGlobalOrderStatus(maxOrder.State, executedVolume, remainingVolume),
|
||||
ExecutedQuantity: executedVolume.Float64(),
|
||||
ExecutedQuantity: executedVolume,
|
||||
CreationTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
||||
UpdateTime: types.Time(maxOrder.CreatedAtMs.Time()),
|
||||
}, nil
|
||||
|
@ -211,22 +209,22 @@ func toGlobalTrade(t max.Trade) (*types.Trade, error) {
|
|||
// trade time
|
||||
mts := time.Unix(0, t.CreatedAtMilliSeconds*int64(time.Millisecond))
|
||||
|
||||
price, err := strconv.ParseFloat(t.Price, 64)
|
||||
price, err := fixedpoint.NewFromString(t.Price)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quantity, err := strconv.ParseFloat(t.Volume, 64)
|
||||
quantity, err := fixedpoint.NewFromString(t.Volume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quoteQuantity, err := strconv.ParseFloat(t.Funds, 64)
|
||||
quoteQuantity, err := fixedpoint.NewFromString(t.Funds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fee, err := strconv.ParseFloat(t.Fee, 64)
|
||||
fee, err := fixedpoint.NewFromString(t.Fee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -276,19 +274,19 @@ func convertWebSocketTrade(t max.TradeUpdate) (*types.Trade, error) {
|
|||
// trade time
|
||||
mts := time.Unix(0, t.Timestamp*int64(time.Millisecond))
|
||||
|
||||
price, err := strconv.ParseFloat(t.Price, 64)
|
||||
price, err := fixedpoint.NewFromString(t.Price)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quantity, err := strconv.ParseFloat(t.Volume, 64)
|
||||
quantity, err := fixedpoint.NewFromString(t.Volume)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quoteQuantity := price * quantity
|
||||
quoteQuantity := price.Mul(quantity)
|
||||
|
||||
fee, err := strconv.ParseFloat(t.Fee, 64)
|
||||
fee, err := fixedpoint.NewFromString(t.Fee)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -327,16 +325,16 @@ func convertWebSocketOrderUpdate(u max.OrderUpdate) (*types.Order, error) {
|
|||
Symbol: toGlobalSymbol(u.Market),
|
||||
Side: toGlobalSideType(u.Side),
|
||||
Type: toGlobalOrderType(u.OrderType),
|
||||
Quantity: util.MustParseFloat(u.Volume),
|
||||
Price: util.MustParseFloat(u.Price),
|
||||
StopPrice: util.MustParseFloat(u.StopPrice),
|
||||
Quantity: fixedpoint.MustNewFromString(u.Volume),
|
||||
Price: fixedpoint.MustNewFromString(u.Price),
|
||||
StopPrice: fixedpoint.MustNewFromString(u.StopPrice),
|
||||
TimeInForce: "GTC", // MAX only supports GTC
|
||||
GroupID: u.GroupID,
|
||||
},
|
||||
Exchange: types.ExchangeMax,
|
||||
OrderID: u.ID,
|
||||
Status: toGlobalOrderStatus(u.State, executedVolume, remainingVolume),
|
||||
ExecutedQuantity: executedVolume.Float64(),
|
||||
ExecutedQuantity: executedVolume,
|
||||
CreationTime: types.Time(time.Unix(0, u.CreatedAtMs*int64(time.Millisecond))),
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"math"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -60,13 +59,13 @@ func (e *Exchange) QueryTicker(ctx context.Context, symbol string) (*types.Ticke
|
|||
|
||||
return &types.Ticker{
|
||||
Time: ticker.Time,
|
||||
Volume: util.MustParseFloat(ticker.Volume),
|
||||
Last: util.MustParseFloat(ticker.Last),
|
||||
Open: util.MustParseFloat(ticker.Open),
|
||||
High: util.MustParseFloat(ticker.High),
|
||||
Low: util.MustParseFloat(ticker.Low),
|
||||
Buy: util.MustParseFloat(ticker.Buy),
|
||||
Sell: util.MustParseFloat(ticker.Sell),
|
||||
Volume: fixedpoint.MustNewFromString(ticker.Volume),
|
||||
Last: fixedpoint.MustNewFromString(ticker.Last),
|
||||
Open: fixedpoint.MustNewFromString(ticker.Open),
|
||||
High: fixedpoint.MustNewFromString(ticker.High),
|
||||
Low: fixedpoint.MustNewFromString(ticker.Low),
|
||||
Buy: fixedpoint.MustNewFromString(ticker.Buy),
|
||||
Sell: fixedpoint.MustNewFromString(ticker.Sell),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -102,13 +101,13 @@ func (e *Exchange) QueryTickers(ctx context.Context, symbol ...string) (map[stri
|
|||
}
|
||||
tickers[toGlobalSymbol(k)] = types.Ticker{
|
||||
Time: v.Time,
|
||||
Volume: util.MustParseFloat(v.Volume),
|
||||
Last: util.MustParseFloat(v.Last),
|
||||
Open: util.MustParseFloat(v.Open),
|
||||
High: util.MustParseFloat(v.High),
|
||||
Low: util.MustParseFloat(v.Low),
|
||||
Buy: util.MustParseFloat(v.Buy),
|
||||
Sell: util.MustParseFloat(v.Sell),
|
||||
Volume: fixedpoint.MustNewFromString(v.Volume),
|
||||
Last: fixedpoint.MustNewFromString(v.Last),
|
||||
Open: fixedpoint.MustNewFromString(v.Open),
|
||||
High: fixedpoint.MustNewFromString(v.High),
|
||||
Low: fixedpoint.MustNewFromString(v.Low),
|
||||
Buy: fixedpoint.MustNewFromString(v.Buy),
|
||||
Sell: fixedpoint.MustNewFromString(v.Sell),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,12 +138,13 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
|
|||
MinAmount: m.MinQuoteAmount,
|
||||
|
||||
MinQuantity: m.MinBaseAmount,
|
||||
MaxQuantity: 10000.0,
|
||||
StepSize: 1.0 / math.Pow10(m.BaseUnitPrecision), // make it like 0.0001
|
||||
|
||||
MinPrice: 1.0 / math.Pow10(m.QuoteUnitPrecision), // used in the price formatter
|
||||
MaxPrice: 10000.0,
|
||||
TickSize: 1.0 / math.Pow10(m.QuoteUnitPrecision),
|
||||
MaxQuantity: fixedpoint.NewFromInt(10000),
|
||||
// make it like 0.0001
|
||||
StepSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(m.BaseUnitPrecision)),
|
||||
// used in the price formatter
|
||||
MinPrice: fixedpoint.NewFromFloat(1.0 / math.Pow10(m.QuoteUnitPrecision)),
|
||||
MaxPrice: fixedpoint.NewFromInt(10000),
|
||||
TickSize: fixedpoint.NewFromFloat(1.0 / math.Pow10(m.QuoteUnitPrecision)),
|
||||
}
|
||||
|
||||
markets[symbol] = market
|
||||
|
@ -421,7 +421,7 @@ func toMaxSubmitOrder(o types.SubmitOrder) (*maxapi.Order, error) {
|
|||
if o.Market.Symbol != "" {
|
||||
quantityString = o.Market.FormatQuantity(o.Quantity)
|
||||
} else {
|
||||
quantityString = strconv.FormatFloat(o.Quantity, 'f', -1, 64)
|
||||
quantityString = o.Quantity.String()
|
||||
}
|
||||
|
||||
maxOrder := maxapi.Order{
|
||||
|
@ -447,7 +447,7 @@ func toMaxSubmitOrder(o types.SubmitOrder) (*maxapi.Order, error) {
|
|||
if o.Market.Symbol != "" {
|
||||
priceInString = o.Market.FormatPrice(o.Price)
|
||||
} else {
|
||||
priceInString = strconv.FormatFloat(o.Price, 'f', -1, 64)
|
||||
priceInString = o.Price.String()
|
||||
}
|
||||
maxOrder.Price = priceInString
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ func toMaxSubmitOrder(o types.SubmitOrder) (*maxapi.Order, error) {
|
|||
if o.Market.Symbol != "" {
|
||||
priceInString = o.Market.FormatPrice(o.StopPrice)
|
||||
} else {
|
||||
priceInString = strconv.FormatFloat(o.StopPrice, 'f', -1, 64)
|
||||
priceInString = o.StopPrice.String()
|
||||
}
|
||||
maxOrder.StopPrice = priceInString
|
||||
}
|
||||
|
@ -713,11 +713,11 @@ func (e *Exchange) QueryWithdrawHistory(ctx context.Context, asset string, since
|
|||
Exchange: types.ExchangeMax,
|
||||
ApplyTime: types.Time(time.Unix(d.CreatedAt, 0)),
|
||||
Asset: toGlobalCurrency(d.Currency),
|
||||
Amount: util.MustParseFloat(d.Amount),
|
||||
Amount: fixedpoint.MustNewFromString(d.Amount),
|
||||
Address: "",
|
||||
AddressTag: "",
|
||||
TransactionID: d.TxID,
|
||||
TransactionFee: util.MustParseFloat(d.Fee),
|
||||
TransactionFee: fixedpoint.MustNewFromString(d.Fee),
|
||||
TransactionFeeCurrency: d.FeeCurrency,
|
||||
// WithdrawOrderID: d.WithdrawOrderID,
|
||||
// Network: d.Network,
|
||||
|
@ -784,7 +784,7 @@ func (e *Exchange) QueryDepositHistory(ctx context.Context, asset string, since,
|
|||
allDeposits = append(allDeposits, types.Deposit{
|
||||
Exchange: types.ExchangeMax,
|
||||
Time: types.Time(time.Unix(d.CreatedAt, 0)),
|
||||
Amount: util.MustParseFloat(d.Amount),
|
||||
Amount: fixedpoint.MustNewFromString(d.Amount),
|
||||
Asset: toGlobalCurrency(d.Currency),
|
||||
Address: "", // not supported
|
||||
AddressTag: "", // not supported
|
||||
|
@ -965,11 +965,14 @@ func (e *Exchange) QueryKLines(ctx context.Context, symbol string, interval type
|
|||
return kLines, nil
|
||||
}
|
||||
|
||||
func (e *Exchange) QueryAveragePrice(ctx context.Context, symbol string) (float64, error) {
|
||||
var Two = fixedpoint.NewFromInt(2)
|
||||
|
||||
func (e *Exchange) QueryAveragePrice(ctx context.Context, symbol string) (fixedpoint.Value, error) {
|
||||
ticker, err := e.client.PublicService.Ticker(toLocalSymbol(symbol))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return fixedpoint.Zero, err
|
||||
}
|
||||
|
||||
return (util.MustParseFloat(ticker.Sell) + util.MustParseFloat(ticker.Buy)) / 2, nil
|
||||
return fixedpoint.MustNewFromString(ticker.Sell).
|
||||
Add(fixedpoint.MustNewFromString(ticker.Buy)).Div(Two), nil
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/valyala/fastjson"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -25,8 +26,8 @@ type Market struct {
|
|||
BaseUnitPrecision int `json:"base_unit_precision"`
|
||||
QuoteUnit string `json:"quote_unit"`
|
||||
QuoteUnitPrecision int `json:"quote_unit_precision"`
|
||||
MinBaseAmount float64 `json:"min_base_amount"`
|
||||
MinQuoteAmount float64 `json:"min_quote_amount"`
|
||||
MinBaseAmount fixedpoint.Value `json:"min_base_amount"`
|
||||
MinQuoteAmount fixedpoint.Value `json:"min_quote_amount"`
|
||||
}
|
||||
|
||||
type Ticker struct {
|
||||
|
@ -206,8 +207,8 @@ type KLine struct {
|
|||
Symbol string
|
||||
Interval string
|
||||
StartTime, EndTime time.Time
|
||||
Open, High, Low, Close float64
|
||||
Volume float64
|
||||
Open, High, Low, Close fixedpoint.Value
|
||||
Volume fixedpoint.Value
|
||||
Closed bool
|
||||
}
|
||||
|
||||
|
@ -309,11 +310,11 @@ func parseKLines(payload []byte, symbol, resolution string, interval Interval) (
|
|||
Interval: resolution,
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
Open: slice[1].GetFloat64(),
|
||||
High: slice[2].GetFloat64(),
|
||||
Low: slice[3].GetFloat64(),
|
||||
Close: slice[4].GetFloat64(),
|
||||
Volume: slice[5].GetFloat64(),
|
||||
Open: fixedpoint.MustNewFromBytes(slice[1].GetStringBytes()),
|
||||
High: fixedpoint.MustNewFromBytes(slice[2].GetStringBytes()),
|
||||
Low: fixedpoint.MustNewFromBytes(slice[3].GetStringBytes()),
|
||||
Close: fixedpoint.MustNewFromBytes(slice[4].GetStringBytes()),
|
||||
Volume: fixedpoint.MustNewFromBytes(slice[5].GetStringBytes()),
|
||||
Closed: isClosed,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
)
|
||||
|
||||
var ErrIncorrectBookEntryElementLength = errors.New("incorrect book entry element length")
|
||||
|
@ -122,12 +121,12 @@ func (k KLinePayload) KLine() types.KLine {
|
|||
EndTime: types.Time(time.Unix(0, k.EndTime*int64(time.Millisecond))),
|
||||
Symbol: k.Market,
|
||||
Interval: types.Interval(k.Resolution),
|
||||
Open: util.MustParseFloat(k.Open),
|
||||
Close: util.MustParseFloat(k.Close),
|
||||
High: util.MustParseFloat(k.High),
|
||||
Low: util.MustParseFloat(k.Low),
|
||||
Volume: util.MustParseFloat(k.Volume),
|
||||
QuoteVolume: 0, // TODO: add this from kingfisher
|
||||
Open: fixedpoint.MustNewFromString(k.Open),
|
||||
Close: fixedpoint.MustNewFromString(k.Close),
|
||||
High: fixedpoint.MustNewFromString(k.High),
|
||||
Low: fixedpoint.MustNewFromString(k.Low),
|
||||
Volume: fixedpoint.MustNewFromString(k.Volume),
|
||||
QuoteVolume: fixedpoint.Zero, // TODO: add this from kingfisher
|
||||
LastTradeID: uint64(k.LastTradeID),
|
||||
NumberOfTrades: 0, // TODO: add this from kingfisher
|
||||
Closed: k.Closed,
|
||||
|
@ -211,11 +210,11 @@ func parseKLineEvent(val *fastjson.Value) (*KLineEvent, error) {
|
|||
Interval: string(val.GetStringBytes("k", "R")),
|
||||
StartTime: time.Unix(0, val.GetInt64("k", "ST")*int64(time.Millisecond)),
|
||||
EndTime: time.Unix(0, val.GetInt64("k", "ET")*int64(time.Millisecond)),
|
||||
Open: util.MustParseFloat(string(val.GetStringBytes("k", "O"))),
|
||||
High: util.MustParseFloat(string(val.GetStringBytes("k", "H"))),
|
||||
Low: util.MustParseFloat(string(val.GetStringBytes("k", "L"))),
|
||||
Close: util.MustParseFloat(string(val.GetStringBytes("k", "C"))),
|
||||
Volume: util.MustParseFloat(string(val.GetStringBytes("k", "v"))),
|
||||
Open: fixedpoint.MustNewFromBytes(val.GetStringBytes("k", "O")),
|
||||
High: fixedpoint.MustNewFromBytes(val.GetStringBytes("k", "H")),
|
||||
Low: fixedpoint.MustNewFromBytes(val.GetStringBytes("k", "L")),
|
||||
Close: fixedpoint.MustNewFromBytes(val.GetStringBytes("k", "C")),
|
||||
Volume: fixedpoint.MustNewFromBytes(val.GetStringBytes("k", "v")),
|
||||
Closed: val.GetBool("k", "x"),
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/exchange/okex/okexapi"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -28,13 +29,13 @@ func toLocalSymbol(symbol string) string {
|
|||
func toGlobalTicker(marketTicker okexapi.MarketTicker) *types.Ticker {
|
||||
return &types.Ticker{
|
||||
Time: marketTicker.Timestamp.Time(),
|
||||
Volume: marketTicker.Volume24H.Float64(),
|
||||
Last: marketTicker.Last.Float64(),
|
||||
Open: marketTicker.Open24H.Float64(),
|
||||
High: marketTicker.High24H.Float64(),
|
||||
Low: marketTicker.Low24H.Float64(),
|
||||
Buy: marketTicker.BidPrice.Float64(),
|
||||
Sell: marketTicker.AskPrice.Float64(),
|
||||
Volume: marketTicker.Volume24H,
|
||||
Last: marketTicker.Last,
|
||||
Open: marketTicker.Open24H,
|
||||
High: marketTicker.High24H,
|
||||
Low: marketTicker.Low24H,
|
||||
Buy: marketTicker.BidPrice,
|
||||
Sell: marketTicker.AskPrice,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,15 +140,15 @@ func toGlobalTrades(orderDetails []okexapi.OrderDetails) ([]types.Trade, error)
|
|||
ID: uint64(tradeID),
|
||||
OrderID: uint64(orderID),
|
||||
Exchange: types.ExchangeOKEx,
|
||||
Price: orderDetail.LastFilledPrice.Float64(),
|
||||
Quantity: orderDetail.LastFilledQuantity.Float64(),
|
||||
QuoteQuantity: orderDetail.LastFilledPrice.Float64() * orderDetail.LastFilledQuantity.Float64(),
|
||||
Price: orderDetail.LastFilledPrice,
|
||||
Quantity: orderDetail.LastFilledQuantity,
|
||||
QuoteQuantity: orderDetail.LastFilledPrice.Mul(orderDetail.LastFilledQuantity),
|
||||
Symbol: toGlobalSymbol(orderDetail.InstrumentID),
|
||||
Side: side,
|
||||
IsBuyer: side == types.SideTypeBuy,
|
||||
IsMaker: orderDetail.ExecutionType == "M",
|
||||
Time: types.Time(orderDetail.LastFilledTime),
|
||||
Fee: orderDetail.LastFilledFee.Float64(),
|
||||
Fee: orderDetail.LastFilledFee,
|
||||
FeeCurrency: orderDetail.LastFilledFeeCurrency,
|
||||
IsMargin: false,
|
||||
IsIsolated: false,
|
||||
|
@ -199,15 +200,15 @@ func toGlobalOrders(orderDetails []okexapi.OrderDetails) ([]types.Order, error)
|
|||
Symbol: toGlobalSymbol(orderDetail.InstrumentID),
|
||||
Side: side,
|
||||
Type: orderType,
|
||||
Price: orderDetail.Price.Float64(),
|
||||
Quantity: orderDetail.Quantity.Float64(),
|
||||
StopPrice: 0, // not supported yet
|
||||
Price: orderDetail.Price,
|
||||
Quantity: orderDetail.Quantity,
|
||||
StopPrice: fixedpoint.Zero, // not supported yet
|
||||
TimeInForce: timeInForce,
|
||||
},
|
||||
Exchange: types.ExchangeOKEx,
|
||||
OrderID: uint64(orderID),
|
||||
Status: orderStatus,
|
||||
ExecutedQuantity: orderDetail.FilledQuantity.Float64(),
|
||||
ExecutedQuantity: orderDetail.FilledQuantity,
|
||||
IsWorking: isWorking,
|
||||
CreationTime: types.Time(orderDetail.CreationTime),
|
||||
UpdateTime: types.Time(orderDetail.UpdateTime),
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"github.com/c9s/bbgo/pkg/exchange/okex/okexapi"
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
// OKB is the platform currency of OKEx, pre-allocate static string here
|
||||
|
@ -69,17 +70,17 @@ func (e *Exchange) QueryMarkets(ctx context.Context) (types.MarketMap, error) {
|
|||
VolumePrecision: int(-math.Log10(instrument.LotSize.Float64())),
|
||||
|
||||
// TickSize: OKEx's price tick, for BTC-USDT it's "0.1"
|
||||
TickSize: instrument.TickSize.Float64(),
|
||||
TickSize: instrument.TickSize,
|
||||
|
||||
// Quantity step size, for BTC-USDT, it's "0.00000001"
|
||||
StepSize: instrument.LotSize.Float64(),
|
||||
StepSize: instrument.LotSize,
|
||||
|
||||
// for BTC-USDT, it's "0.00001"
|
||||
MinQuantity: instrument.MinSize.Float64(),
|
||||
MinQuantity: instrument.MinSize,
|
||||
|
||||
// OKEx does not offer minimal notional, use 1 USD here.
|
||||
MinNotional: 1.0,
|
||||
MinAmount: 1.0,
|
||||
MinNotional: fixedpoint.One,
|
||||
MinAmount: fixedpoint.One,
|
||||
}
|
||||
markets[symbol] = market
|
||||
}
|
||||
|
@ -170,7 +171,8 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
if order.Market.Symbol != "" {
|
||||
orderReq.Quantity(order.Market.FormatQuantity(order.Quantity))
|
||||
} else {
|
||||
orderReq.Quantity(strconv.FormatFloat(order.Quantity, 'f', 8, 64))
|
||||
// TODO report error
|
||||
orderReq.Quantity(order.Quantity.FormatString(8))
|
||||
}
|
||||
|
||||
// set price field for limit orders
|
||||
|
@ -179,7 +181,8 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
if order.Market.Symbol != "" {
|
||||
orderReq.Price(order.Market.FormatPrice(order.Price))
|
||||
} else {
|
||||
orderReq.Price(strconv.FormatFloat(order.Price, 'f', 8, 64))
|
||||
// TODO report error
|
||||
orderReq.Price(order.Price.FormatString(8))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,7 +217,7 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
|
|||
Exchange: types.ExchangeOKEx,
|
||||
OrderID: uint64(orderID),
|
||||
Status: types.OrderStatusNew,
|
||||
ExecutedQuantity: 0,
|
||||
ExecutedQuantity: fixedpoint.Zero,
|
||||
IsWorking: true,
|
||||
CreationTime: types.Time(time.Now()),
|
||||
UpdateTime: types.Time(time.Now()),
|
||||
|
|
|
@ -108,6 +108,13 @@ var halfpow10 = [...]uint64{
|
|||
500000000000000000,
|
||||
5000000000000000000}
|
||||
|
||||
func min(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (v Value) Value() (driver.Value, error) {
|
||||
return v.Float64(), nil
|
||||
}
|
||||
|
@ -233,6 +240,41 @@ func Inf(sign int8) Value {
|
|||
}
|
||||
}
|
||||
|
||||
func (dn Value) FormatString(prec int) string {
|
||||
if dn.sign == 0 {
|
||||
return "0"
|
||||
}
|
||||
const maxLeadingZeros = 7
|
||||
sign := ""
|
||||
if dn.sign < 0 {
|
||||
sign = "-"
|
||||
}
|
||||
if dn.IsInf() {
|
||||
return sign + "inf"
|
||||
}
|
||||
digits := getDigits(dn.coef)
|
||||
nd := len(digits)
|
||||
e := int(dn.exp) - nd
|
||||
if -maxLeadingZeros <= dn.exp && dn.exp <= 0 {
|
||||
// decimal to the left
|
||||
return sign + "." + strings.Repeat("0", -e-nd) + digits[:min(prec, nd)]
|
||||
} else if -nd < e && e <= -1 {
|
||||
// decimal within
|
||||
dec := nd + e
|
||||
return sign + digits[:dec] + "." + digits[dec:min(dec+prec, nd)]
|
||||
} else if 0 < dn.exp && dn.exp <= digitsMax {
|
||||
// decimal to the right
|
||||
return sign + digits + strings.Repeat("0", e)
|
||||
} else {
|
||||
// scientific notation
|
||||
after := ""
|
||||
if nd > 1 {
|
||||
after = "." + digits[1:min(1+prec, nd)]
|
||||
}
|
||||
return sign + digits[:1] + after + "e" + strconv.Itoa(int(dn.exp-1))
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the Value
|
||||
func (dn Value) String() string {
|
||||
if dn.sign == 0 {
|
||||
|
@ -360,12 +402,12 @@ func NewFromString(s string) (Value, error) {
|
|||
s = s[:length-1]
|
||||
}
|
||||
r := &reader{s, 0}
|
||||
sign := getSign(r)
|
||||
sign := r.getSign()
|
||||
if r.matchStr("inf") {
|
||||
return Inf(sign), nil
|
||||
}
|
||||
coef, exp := getCoef(r)
|
||||
exp += getExp(r)
|
||||
coef, exp := r.getCoef()
|
||||
exp += r.getExp()
|
||||
if r.len() != 0 { // didn't consume entire string
|
||||
return Zero, errors.New("invalid number")
|
||||
} else if coef == 0 || exp < math.MinInt8 {
|
||||
|
@ -388,6 +430,158 @@ func MustNewFromString(input string) Value {
|
|||
return v
|
||||
}
|
||||
|
||||
func NewFromBytes(s []byte) (Value, error) {
|
||||
length := len(s)
|
||||
isPercentage := s[length - 1] == '%'
|
||||
if isPercentage {
|
||||
s = s[:length-1]
|
||||
}
|
||||
r := &readerBytes{s, 0}
|
||||
sign := r.getSign()
|
||||
if r.matchStr("inf") {
|
||||
return Inf(sign), nil
|
||||
}
|
||||
coef, exp := r.getCoef()
|
||||
exp += r.getExp()
|
||||
if r.len() != 0 { // didn't consume entire string
|
||||
return Zero, errors.New("invalid number")
|
||||
} else if coef == 0 || exp < math.MinInt8 {
|
||||
return Zero, nil
|
||||
} else if exp > math.MaxInt8 {
|
||||
return Inf(sign), nil
|
||||
}
|
||||
if isPercentage {
|
||||
exp -= 2
|
||||
}
|
||||
//check(coefMin <= coef && coef <= coefMax)
|
||||
return Value{coef, sign, exp}, nil
|
||||
}
|
||||
|
||||
func MustNewFromBytes(input []byte) Value {
|
||||
v, err := NewFromBytes(input)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot parse %s into fixedpoint, error: %s", input, err.Error()))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
// TODO: refactor by interface
|
||||
|
||||
type readerBytes struct {
|
||||
s []byte
|
||||
i int
|
||||
}
|
||||
|
||||
func (r *readerBytes) cur() byte {
|
||||
if r.i >= len(r.s) {
|
||||
return 0
|
||||
}
|
||||
return byte(r.s[r.i])
|
||||
}
|
||||
|
||||
func (r *readerBytes) prev() byte {
|
||||
if r.i == 0 {
|
||||
return 0
|
||||
}
|
||||
return byte(r.s[r.i-1])
|
||||
}
|
||||
|
||||
func (r *readerBytes) len() int {
|
||||
return len(r.s) - r.i
|
||||
}
|
||||
|
||||
func (r *readerBytes) match(c byte) bool {
|
||||
if r.cur() == c {
|
||||
r.i++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *readerBytes) matchDigit() bool {
|
||||
c := r.cur()
|
||||
if '0' <= c && c <= '9' {
|
||||
r.i++
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *readerBytes) matchStr(pre string) bool {
|
||||
for i, c := range r.s[r.i:] {
|
||||
if pre[i] != c {
|
||||
return false
|
||||
}
|
||||
}
|
||||
r.i += len(pre)
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *readerBytes) getSign() int8 {
|
||||
if r.match('-') {
|
||||
return int8(signNeg)
|
||||
}
|
||||
r.match('+')
|
||||
return int8(signPos)
|
||||
}
|
||||
|
||||
func (r *readerBytes) getCoef() (uint64, int) {
|
||||
digits := false
|
||||
beforeDecimal := true
|
||||
for r.match('0') {
|
||||
digits = true
|
||||
}
|
||||
if r.cur() == '.' && r.len() > 1 {
|
||||
digits = false
|
||||
}
|
||||
n := uint64(0)
|
||||
exp := 0
|
||||
p := shiftMax
|
||||
for {
|
||||
c := r.cur()
|
||||
if r.matchDigit() {
|
||||
digits = true
|
||||
// ignore extra decimal places
|
||||
if c != '0' && p >= 0 {
|
||||
n += uint64(c-'0') * pow10[p]
|
||||
}
|
||||
p--
|
||||
} else if beforeDecimal {
|
||||
// decimal point or end
|
||||
exp = shiftMax - p
|
||||
if !r.match('.') {
|
||||
break
|
||||
}
|
||||
beforeDecimal = false
|
||||
if !digits {
|
||||
for r.match('0') {
|
||||
digits = true
|
||||
exp--
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !digits {
|
||||
panic("numbers require at least one digit")
|
||||
}
|
||||
return n, exp
|
||||
}
|
||||
|
||||
func (r *readerBytes) getExp() int {
|
||||
e := 0
|
||||
if r.match('e') || r.match('E') {
|
||||
esign := r.getSign()
|
||||
for r.matchDigit() {
|
||||
e = e*10 + int(r.prev()-'0')
|
||||
}
|
||||
e *= int(esign)
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
s string
|
||||
i int
|
||||
|
@ -436,7 +630,7 @@ func (r *reader) matchStr(pre string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func getSign(r *reader) int8 {
|
||||
func (r *reader) getSign() int8 {
|
||||
if r.match('-') {
|
||||
return int8(signNeg)
|
||||
}
|
||||
|
@ -444,7 +638,7 @@ func getSign(r *reader) int8 {
|
|||
return int8(signPos)
|
||||
}
|
||||
|
||||
func getCoef(r *reader) (uint64, int) {
|
||||
func (r *reader) getCoef() (uint64, int) {
|
||||
digits := false
|
||||
beforeDecimal := true
|
||||
for r.match('0') {
|
||||
|
@ -488,10 +682,10 @@ func getCoef(r *reader) (uint64, int) {
|
|||
return n, exp
|
||||
}
|
||||
|
||||
func getExp(r *reader) int {
|
||||
func (r *reader) getExp() int {
|
||||
e := 0
|
||||
if r.match('e') || r.match('E') {
|
||||
esign := getSign(r)
|
||||
esign := r.getSign()
|
||||
for r.matchDigit() {
|
||||
e = e*10 + int(r.prev()-'0')
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ func TestMulString(t *testing.T) {
|
|||
y := NewFromFloat(10.55)
|
||||
x = x.Mul(y)
|
||||
assert.Equal(t, "111.3025", x.String())
|
||||
assert.Equal(t, "111.30", x.FormatString(2))
|
||||
}
|
||||
|
||||
// Not used
|
||||
|
|
|
@ -23,10 +23,10 @@ type AD struct {
|
|||
}
|
||||
|
||||
func (inc *AD) update(kLine types.KLine) {
|
||||
close := kLine.Close
|
||||
high := kLine.High
|
||||
low := kLine.Low
|
||||
volume := kLine.Volume
|
||||
close := kLine.Close.Float64()
|
||||
high := kLine.High.Float64()
|
||||
low := kLine.Low.Float64()
|
||||
volume := kLine.Volume.Float64()
|
||||
|
||||
moneyFlowVolume := ((2*close - high - low) / (high - low)) * volume
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ func (inc *BOLL) calculateAndUpdate(kLines []types.KLine) {
|
|||
|
||||
var prices []float64
|
||||
for _, k := range recentK {
|
||||
prices = append(prices, k.Close)
|
||||
prices = append(prices, k.Close.Float64())
|
||||
}
|
||||
|
||||
var std = stat.StdDev(prices, nil)
|
||||
|
|
|
@ -115,15 +115,15 @@ func ewma(prices []float64, multiplier float64) float64 {
|
|||
type KLinePriceMapper func(k types.KLine) float64
|
||||
|
||||
func KLineOpenPriceMapper(k types.KLine) float64 {
|
||||
return k.Open
|
||||
return k.Open.Float64()
|
||||
}
|
||||
|
||||
func KLineClosePriceMapper(k types.KLine) float64 {
|
||||
return k.Close
|
||||
return k.Close.Float64()
|
||||
}
|
||||
|
||||
func KLineTypicalPriceMapper(k types.KLine) float64 {
|
||||
return (k.High + k.Low + k.Close) / float64(3)
|
||||
return (k.High.Float64() + k.Low.Float64() + k.Close.Float64()) / 3.
|
||||
}
|
||||
|
||||
func MapKLinePrice(kLines []types.KLine, f KLinePriceMapper) (prices []float64) {
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/types"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -17,15 +16,15 @@ On-Balance Volume (OBV) Definition
|
|||
type OBV struct {
|
||||
types.IntervalWindow
|
||||
Values types.Float64Slice
|
||||
PrePrice fixedpoint.Value
|
||||
PrePrice float64
|
||||
|
||||
EndTime time.Time
|
||||
UpdateCallbacks []func(value fixedpoint.Value)
|
||||
UpdateCallbacks []func(value float64)
|
||||
}
|
||||
|
||||
func (inc *OBV) update(kLine types.KLine, priceF KLinePriceMapper) {
|
||||
price := priceF(kLine)
|
||||
volume := kLine.Volume
|
||||
volume := kLine.Volume.Float64()
|
||||
|
||||
if len(inc.Values) == 0 {
|
||||
inc.PrePrice = price
|
||||
|
|
|
@ -30,10 +30,11 @@ func (inc *STOCH) update(kLine types.KLine) {
|
|||
inc.KLineWindow.Add(kLine)
|
||||
inc.KLineWindow.Truncate(inc.Window)
|
||||
|
||||
lowest := inc.KLineWindow.GetLow()
|
||||
highest := inc.KLineWindow.GetHigh()
|
||||
lowest := inc.KLineWindow.GetLow().Float64()
|
||||
highest := inc.KLineWindow.GetHigh().Float64()
|
||||
clos := kLine.Close.Float64()
|
||||
|
||||
k := 100.0 * (kLine.Close - lowest) / (highest - lowest)
|
||||
k := 100.0 * (clos - lowest) / (highest - lowest)
|
||||
inc.K.Push(k)
|
||||
|
||||
d := inc.K.Tail(DPeriod).Mean()
|
||||
|
|
|
@ -44,7 +44,7 @@ func (inc *VWAP) calculateVWAP(kLines []types.KLine, priceF KLinePriceMapper) (v
|
|||
func (inc *VWAP) update(kLine types.KLine, priceF KLinePriceMapper, multiplier float64) {
|
||||
// multiplier = 1 or -1
|
||||
price := priceF(kLine)
|
||||
volume := kLine.Volume
|
||||
volume := kLine.Volume.Float64()
|
||||
|
||||
inc.WeightedSum += multiplier * price * volume
|
||||
inc.VolumeSum += multiplier * volume
|
||||
|
@ -63,7 +63,7 @@ func (inc *VWAP) calculateAndUpdate(kLines []types.KLine) {
|
|||
if len(inc.Values) == 0 {
|
||||
// for the first value, we should use the close price
|
||||
price := priceF(kLines[0])
|
||||
volume := kLines[0].Volume
|
||||
volume := kLines[0].Volume.Float64()
|
||||
|
||||
inc.Values = []float64{price}
|
||||
inc.WeightedSum = price * volume
|
||||
|
|
|
@ -35,11 +35,11 @@ func (inc *VWMA) Last() float64 {
|
|||
}
|
||||
|
||||
func KLinePriceVolumeMapper(k types.KLine) float64 {
|
||||
return k.Close * k.Volume
|
||||
return k.Close.Mul(k.Volume).Float64()
|
||||
}
|
||||
|
||||
func KLineVolumeMapper(k types.KLine) float64 {
|
||||
return k.Volume
|
||||
return k.Volume.Float64()
|
||||
}
|
||||
|
||||
func (inc *VWMA) calculateAndUpdate(kLines []types.KLine) {
|
||||
|
|
|
@ -81,7 +81,7 @@ func (s *OrderService) Sync(ctx context.Context, exchange types.Exchange, symbol
|
|||
}
|
||||
|
||||
// skip canceled and not filled orders
|
||||
if order.Status == types.OrderStatusCanceled && order.ExecutedQuantity == 0.0 {
|
||||
if order.Status == types.OrderStatusCanceled && order.ExecutedQuantity.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package types
|
|||
|
||||
import (
|
||||
"time"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type DepositStatus string
|
||||
|
@ -25,7 +26,7 @@ type Deposit struct {
|
|||
GID int64 `json:"gid" db:"gid"`
|
||||
Exchange ExchangeName `json:"exchange" db:"exchange"`
|
||||
Time Time `json:"time" db:"time"`
|
||||
Amount float64 `json:"amount" db:"amount"`
|
||||
Amount fixedpoint.Value `json:"amount" db:"amount"`
|
||||
Asset string `json:"asset" db:"asset"`
|
||||
Address string `json:"address" db:"address"`
|
||||
AddressTag string `json:"addressTag"`
|
||||
|
|
|
@ -74,30 +74,30 @@ type Market struct {
|
|||
|
||||
// The MIN_NOTIONAL filter defines the minimum notional value allowed for an order on a symbol.
|
||||
// An order's notional value is the price * quantity
|
||||
MinNotional float64 `json:"minNotional,omitempty"`
|
||||
MinAmount float64 `json:"minAmount,omitempty"`
|
||||
MinNotional fixedpoint.Value `json:"minNotional,omitempty"`
|
||||
MinAmount fixedpoint.Value `json:"minAmount,omitempty"`
|
||||
|
||||
// The LOT_SIZE filter defines the quantity
|
||||
MinQuantity float64 `json:"minQuantity,omitempty"`
|
||||
MinQuantity fixedpoint.Value `json:"minQuantity,omitempty"`
|
||||
|
||||
// MaxQuantity is currently not used in the code
|
||||
MaxQuantity float64 `json:"maxQuantity,omitempty"`
|
||||
MaxQuantity fixedpoint.Value `json:"maxQuantity,omitempty"`
|
||||
|
||||
// StepSize is the step size of quantity
|
||||
// can be converted from precision, e.g.
|
||||
// 1.0 / math.Pow10(m.BaseUnitPrecision)
|
||||
StepSize float64 `json:"stepSize,omitempty"`
|
||||
StepSize fixedpoint.Value `json:"stepSize,omitempty"`
|
||||
|
||||
MinPrice float64 `json:"minPrice,omitempty"`
|
||||
MaxPrice float64 `json:"maxPrice,omitempty"`
|
||||
MinPrice fixedpoint.Value `json:"minPrice,omitempty"`
|
||||
MaxPrice fixedpoint.Value `json:"maxPrice,omitempty"`
|
||||
|
||||
// TickSize is the step size of price
|
||||
TickSize float64 `json:"tickSize,omitempty"`
|
||||
TickSize fixedpoint.Value `json:"tickSize,omitempty"`
|
||||
}
|
||||
|
||||
// TruncateQuantity uses the step size to truncate floating number, in order to avoid the rounding issue
|
||||
func (m Market) TruncateQuantity(quantity fixedpoint.Value) fixedpoint.Value {
|
||||
stepRound := math.Pow10(-int(math.Log10(m.StepSize)))
|
||||
stepRound := math.Pow10(-int(math.Log10(m.StepSize.Float64())))
|
||||
return fixedpoint.NewFromFloat(math.Trunc(quantity.Float64()*stepRound) / stepRound)
|
||||
}
|
||||
|
||||
|
@ -125,55 +125,59 @@ func (m Market) QuoteCurrencyFormatter() *accounting.Accounting {
|
|||
return a
|
||||
}
|
||||
|
||||
func (m Market) FormatPriceCurrency(val float64) string {
|
||||
func (m Market) FormatPriceCurrency(val fixedpoint.Value) string {
|
||||
switch m.QuoteCurrency {
|
||||
|
||||
case "USD", "USDT":
|
||||
return USD.FormatMoneyFloat64(val)
|
||||
return USD.FormatMoney(val)
|
||||
|
||||
case "BTC":
|
||||
return BTC.FormatMoneyFloat64(val)
|
||||
return BTC.FormatMoney(val)
|
||||
|
||||
case "BNB":
|
||||
return BNB.FormatMoneyFloat64(val)
|
||||
return BNB.FormatMoney(val)
|
||||
|
||||
}
|
||||
|
||||
return m.FormatPrice(val)
|
||||
}
|
||||
|
||||
func (m Market) FormatPrice(val float64) string {
|
||||
func (m Market) FormatPrice(val fixedpoint.Value) string {
|
||||
// p := math.Pow10(m.PricePrecision)
|
||||
return formatPrice(val, m.TickSize)
|
||||
}
|
||||
|
||||
func formatPrice(price float64, tickSize float64) string {
|
||||
prec := int(math.Round(math.Abs(math.Log10(tickSize))))
|
||||
func formatPrice(price fixedpoint.Value, tickSize fixedpoint.Value) string {
|
||||
// TODO Round
|
||||
prec := int(math.Round(math.Abs(math.Log10(tickSize.Float64()))))
|
||||
p := math.Pow10(prec)
|
||||
price = math.Trunc(price*p) / p
|
||||
return strconv.FormatFloat(price, 'f', prec, 64)
|
||||
pp := math.Trunc(price.Float64()*p) / p
|
||||
return strconv.FormatFloat(pp, 'f', prec, 64)
|
||||
}
|
||||
|
||||
func (m Market) FormatQuantity(val float64) string {
|
||||
func (m Market) FormatQuantity(val fixedpoint.Value) string {
|
||||
return formatQuantity(val, m.StepSize)
|
||||
}
|
||||
|
||||
func formatQuantity(quantity float64, lot float64) string {
|
||||
prec := int(math.Round(math.Abs(math.Log10(lot))))
|
||||
func formatQuantity(quantity fixedpoint.Value, lot fixedpoint.Value) string {
|
||||
// TODO Round
|
||||
prec := int(math.Round(math.Abs(math.Log10(lot.Float64()))))
|
||||
p := math.Pow10(prec)
|
||||
quantity = math.Trunc(quantity*p) / p
|
||||
return strconv.FormatFloat(quantity, 'f', prec, 64)
|
||||
q := math.Trunc(quantity.Float64() * p) / p
|
||||
return strconv.FormatFloat(q, 'f', prec, 64)
|
||||
}
|
||||
|
||||
func (m Market) FormatVolume(val float64) string {
|
||||
func (m Market) FormatVolume(val fixedpoint.Value) string {
|
||||
// TODO Round
|
||||
p := math.Pow10(m.VolumePrecision)
|
||||
val = math.Trunc(val*p) / p
|
||||
return strconv.FormatFloat(val, 'f', m.VolumePrecision, 64)
|
||||
v := math.Trunc(val.Float64()*p) / p
|
||||
return strconv.FormatFloat(v, 'f', m.VolumePrecision, 64)
|
||||
}
|
||||
|
||||
func (m Market) CanonicalizeVolume(val float64) float64 {
|
||||
func (m Market) CanonicalizeVolume(val fixedpoint.Value) float64 {
|
||||
// TODO Round
|
||||
p := math.Pow10(m.VolumePrecision)
|
||||
return math.Trunc(p*val) / p
|
||||
return math.Trunc(p*val.Float64()) / p
|
||||
}
|
||||
|
||||
type MarketMap map[string]Market
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/slack-go/slack"
|
||||
|
||||
"github.com/c9s/bbgo/pkg/util"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -108,9 +109,9 @@ type SubmitOrder struct {
|
|||
Side SideType `json:"side" db:"side"`
|
||||
Type OrderType `json:"orderType" db:"order_type"`
|
||||
|
||||
Quantity float64 `json:"quantity" db:"quantity"`
|
||||
Price float64 `json:"price" db:"price"`
|
||||
StopPrice float64 `json:"stopPrice,omitempty" db:"stop_price"`
|
||||
Quantity fixedpoint.Value `json:"quantity" db:"quantity"`
|
||||
Price fixedpoint.Value `json:"price" db:"price"`
|
||||
StopPrice fixedpoint.Value `json:"stopPrice,omitempty" db:"stop_price"`
|
||||
|
||||
Market Market `json:"-" db:"-"`
|
||||
|
||||
|
@ -129,40 +130,40 @@ type SubmitOrder struct {
|
|||
func (o SubmitOrder) String() string {
|
||||
switch o.Type {
|
||||
case OrderTypeMarket:
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %f", o.Symbol, o.Type, o.Side, o.Quantity)
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %s", o.Symbol, o.Type, o.Side, o.Quantity.String())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %f @ %f", o.Symbol, o.Type, o.Side, o.Quantity, o.Price)
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %s @ %s", o.Symbol, o.Type, o.Side, o.Quantity.String(), o.Price.String())
|
||||
}
|
||||
|
||||
func (o SubmitOrder) PlainText() string {
|
||||
switch o.Type {
|
||||
case OrderTypeMarket:
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %f", o.Symbol, o.Type, o.Side, o.Quantity)
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %s", o.Symbol, o.Type, o.Side, o.Quantity.String())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %f @ %f", o.Symbol, o.Type, o.Side, o.Quantity, o.Price)
|
||||
return fmt.Sprintf("SubmitOrder %s %s %s %s @ %s", o.Symbol, o.Type, o.Side, o.Quantity.String(), o.Price.String())
|
||||
}
|
||||
|
||||
func (o SubmitOrder) SlackAttachment() slack.Attachment {
|
||||
var fields = []slack.AttachmentField{
|
||||
{Title: "Symbol", Value: o.Symbol, Short: true},
|
||||
{Title: "Side", Value: string(o.Side), Short: true},
|
||||
{Title: "Price", Value: trimTrailingZeroFloat(o.Price), Short: true},
|
||||
{Title: "Quantity", Value: trimTrailingZeroFloat(o.Quantity), Short: true},
|
||||
{Title: "Price", Value: o.Price.String(), Short: true},
|
||||
{Title: "Quantity", Value: o.Quantity.String(), Short: true},
|
||||
}
|
||||
|
||||
if o.Price > 0 && o.Quantity > 0 && len(o.Market.QuoteCurrency) > 0 {
|
||||
if o.Price.Sign() > 0 && o.Quantity.Sign() > 0 && len(o.Market.QuoteCurrency) > 0 {
|
||||
if IsFiatCurrency(o.Market.QuoteCurrency) {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Amount",
|
||||
Value: USD.FormatMoneyFloat64(o.Price * o.Quantity),
|
||||
Value: USD.FormatMoney(o.Price.Mul(o.Quantity)),
|
||||
Short: true,
|
||||
})
|
||||
} else {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: "Amount",
|
||||
Value: fmt.Sprintf("%f %s", o.Price*o.Quantity, o.Market.QuoteCurrency),
|
||||
Value: fmt.Sprintf("%s %s", o.Price.Mul(o.Quantity).String(), o.Market.QuoteCurrency),
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
@ -201,7 +202,7 @@ type Order struct {
|
|||
UUID string `json:"uuid,omitempty"`
|
||||
|
||||
Status OrderStatus `json:"status" db:"status"`
|
||||
ExecutedQuantity float64 `json:"executedQuantity" db:"executed_quantity"`
|
||||
ExecutedQuantity fixedpoint.Value `json:"executedQuantity" db:"executed_quantity"`
|
||||
IsWorking bool `json:"isWorking" db:"is_working"`
|
||||
CreationTime Time `json:"creationTime" db:"created_at"`
|
||||
UpdateTime Time `json:"updateTime" db:"updated_at"`
|
||||
|
@ -214,7 +215,7 @@ type Order struct {
|
|||
// so that we can post the order later when we want to restore the orders.
|
||||
func (o Order) Backup() SubmitOrder {
|
||||
so := o.SubmitOrder
|
||||
so.Quantity = o.Quantity - o.ExecutedQuantity
|
||||
so.Quantity = o.Quantity.Sub(o.ExecutedQuantity)
|
||||
|
||||
// ClientOrderID can not be reused
|
||||
so.ClientOrderID = ""
|
||||
|
@ -229,14 +230,14 @@ func (o Order) String() string {
|
|||
orderID = strconv.FormatUint(o.OrderID, 10)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("ORDER %s %s %s %s %f/%f @ %f -> %s",
|
||||
return fmt.Sprintf("ORDER %s %s %s %s %s/%s @ %s -> %s",
|
||||
o.Exchange.String(),
|
||||
orderID,
|
||||
o.Symbol,
|
||||
o.Side,
|
||||
o.ExecutedQuantity,
|
||||
o.Quantity,
|
||||
o.Price,
|
||||
o.ExecutedQuantity.String(),
|
||||
o.Quantity.String(),
|
||||
o.Price.String(),
|
||||
o.Status)
|
||||
}
|
||||
|
||||
|
@ -247,9 +248,9 @@ func (o Order) PlainText() string {
|
|||
o.Symbol,
|
||||
o.Type,
|
||||
o.Side,
|
||||
util.FormatFloat(o.Price, 2),
|
||||
util.FormatFloat(o.ExecutedQuantity, 2),
|
||||
util.FormatFloat(o.Quantity, 4),
|
||||
o.Price.FormatString(2),
|
||||
o.ExecutedQuantity.FormatString(2),
|
||||
o.Quantity.FormatString(4),
|
||||
o.Status)
|
||||
}
|
||||
|
||||
|
@ -257,10 +258,10 @@ func (o Order) SlackAttachment() slack.Attachment {
|
|||
var fields = []slack.AttachmentField{
|
||||
{Title: "Symbol", Value: o.Symbol, Short: true},
|
||||
{Title: "Side", Value: string(o.Side), Short: true},
|
||||
{Title: "Price", Value: trimTrailingZeroFloat(o.Price), Short: true},
|
||||
{Title: "Price", Value: o.Price.String(), Short: true},
|
||||
{
|
||||
Title: "Executed Quantity",
|
||||
Value: trimTrailingZeroFloat(o.ExecutedQuantity) + "/" + trimTrailingZeroFloat(o.Quantity),
|
||||
Value: o.ExecutedQuantity.String() + "/" + o.Quantity.String(),
|
||||
Short: true,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -53,11 +53,10 @@ type Position struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
func (p *Position) NewClosePositionOrder(percentage float64) *SubmitOrder {
|
||||
func (p *Position) NewClosePositionOrder(percentage fixedpoint.Value) *SubmitOrder {
|
||||
base := p.GetBase()
|
||||
quantity := base.Float64()
|
||||
quantity = quantity * percentage
|
||||
if quantity < p.Market.MinQuantity {
|
||||
quantity := base.Mul(percentage)
|
||||
if quantity.Compare(p.Market.MinQuantity) < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -67,8 +66,6 @@ func (p *Position) NewClosePositionOrder(percentage float64) *SubmitOrder {
|
|||
return nil
|
||||
} else if sign < 0 {
|
||||
side = SideTypeBuy
|
||||
} else if sign > 0 {
|
||||
side = SideTypeSell
|
||||
}
|
||||
|
||||
return &SubmitOrder{
|
||||
|
@ -189,9 +186,9 @@ func (p *Position) SlackAttachment() slack.Attachment {
|
|||
title := util.Render(string(posType)+` Position {{ .Symbol }} `, p)
|
||||
|
||||
fields := []slack.AttachmentField{
|
||||
{Title: "Average Cost", Value: trimTrailingZeroFloat(averageCost.Float64()) + " " + p.QuoteCurrency, Short: true},
|
||||
{Title: p.BaseCurrency, Value: trimTrailingZeroFloat(base.Float64()), Short: true},
|
||||
{Title: p.QuoteCurrency, Value: trimTrailingZeroFloat(quote.Float64())},
|
||||
{Title: "Average Cost", Value: averageCost.String() + " " + p.QuoteCurrency, Short: true},
|
||||
{Title: p.BaseCurrency, Value: base.String(), Short: true},
|
||||
{Title: p.QuoteCurrency, Value: quote.String()},
|
||||
}
|
||||
|
||||
if p.TotalFee != nil {
|
||||
|
@ -199,7 +196,7 @@ func (p *Position) SlackAttachment() slack.Attachment {
|
|||
if fee.Sign() > 0 {
|
||||
fields = append(fields, slack.AttachmentField{
|
||||
Title: fmt.Sprintf("Fee (%s)", feeCurrency),
|
||||
Value: trimTrailingZeroFloat(fee.Float64()),
|
||||
Value: fee.String(),
|
||||
Short: true,
|
||||
})
|
||||
}
|
||||
|
@ -222,14 +219,14 @@ func (p *Position) PlainText() (msg string) {
|
|||
msg = fmt.Sprintf("%s Position %s: average cost = %s, base = %s, quote = %s",
|
||||
posType,
|
||||
p.Symbol,
|
||||
trimTrailingZeroFloat(p.AverageCost.Float64()),
|
||||
trimTrailingZeroFloat(p.Base.Float64()),
|
||||
trimTrailingZeroFloat(p.Quote.Float64()),
|
||||
p.AverageCost.String(),
|
||||
p.Base.String(),
|
||||
p.Quote.String(),
|
||||
)
|
||||
|
||||
if p.TotalFee != nil {
|
||||
for feeCurrency, fee := range p.TotalFee {
|
||||
msg += fmt.Sprintf("\nfee (%s) = %s", feeCurrency, trimTrailingZeroFloat(fee.Float64()))
|
||||
msg += fmt.Sprintf("\nfee (%s) = %s", feeCurrency, fee.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,16 @@ package types
|
|||
|
||||
import (
|
||||
"time"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type Ticker struct {
|
||||
Time time.Time
|
||||
Volume float64 // `volume` from Max & binance
|
||||
Last float64 // `last` from Max, `lastPrice` from binance
|
||||
Open float64 // `open` from Max, `openPrice` from binance
|
||||
High float64 // `high` from Max, `highPrice` from binance
|
||||
Low float64 // `low` from Max, `lowPrice` from binance
|
||||
Buy float64 // `buy` from Max, `bidPrice` from binance
|
||||
Sell float64 // `sell` from Max, `askPrice` from binance
|
||||
Volume fixedpoint.Value // `volume` from Max & binance
|
||||
Last fixedpoint.Value // `last` from Max, `lastPrice` from binance
|
||||
Open fixedpoint.Value // `open` from Max, `openPrice` from binance
|
||||
High fixedpoint.Value // `high` from Max, `highPrice` from binance
|
||||
Low fixedpoint.Value // `low` from Max, `lowPrice` from binance
|
||||
Buy fixedpoint.Value // `buy` from Max, `bidPrice` from binance
|
||||
Sell fixedpoint.Value // `sell` from Max, `askPrice` from binance
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ func (trade Trade) PositionChange() fixedpoint.Value {
|
|||
return fixedpoint.Zero
|
||||
}
|
||||
|
||||
func trimTrailingZero(a string) string {
|
||||
/*func trimTrailingZero(a string) string {
|
||||
index := strings.Index(a, ".")
|
||||
if index == -1 {
|
||||
return a
|
||||
|
@ -111,9 +111,9 @@ func trimTrailingZero(a string) string {
|
|||
return a
|
||||
}
|
||||
|
||||
func trimTrailingZeroFloat(a float64) string {
|
||||
func trimTrailingZero(a float64) string {
|
||||
return trimTrailingZero(fmt.Sprintf("%f", a))
|
||||
}
|
||||
}*/
|
||||
|
||||
// String is for console output
|
||||
func (trade Trade) String() string {
|
||||
|
@ -121,10 +121,10 @@ func (trade Trade) String() string {
|
|||
trade.Exchange.String(),
|
||||
trade.Symbol,
|
||||
trade.Side,
|
||||
trimTrailingZeroFloat(trade.Quantity.Float64()),
|
||||
trimTrailingZeroFloat(trade.Price.Float64()),
|
||||
trimTrailingZeroFloat(trade.QuoteQuantity.Float64()),
|
||||
trimTrailingZeroFloat(trade.Fee.Float64()),
|
||||
trade.Quantity.String(),
|
||||
trade.Price.String(),
|
||||
trade.QuoteQuantity.String(),
|
||||
trade.Fee.String(),
|
||||
trade.FeeCurrency,
|
||||
trade.OrderID,
|
||||
trade.Time.Time().Format(time.StampMilli),
|
||||
|
@ -137,10 +137,10 @@ func (trade Trade) PlainText() string {
|
|||
trade.Exchange.String(),
|
||||
trade.Symbol,
|
||||
trade.Side,
|
||||
trimTrailingZeroFloat(trade.Quantity.Float64()),
|
||||
trimTrailingZeroFloat(trade.Price.Float64()),
|
||||
trimTrailingZeroFloat(trade.QuoteQuantity.Float64()),
|
||||
trimTrailingZeroFloat(trade.Fee.Float64()),
|
||||
trade.Quantity.String(),
|
||||
trade.Price.String(),
|
||||
trade.QuoteQuantity.String(),
|
||||
trade.Fee.String(),
|
||||
trade.FeeCurrency)
|
||||
}
|
||||
|
||||
|
@ -184,10 +184,10 @@ func (trade Trade) SlackAttachment() slack.Attachment {
|
|||
Color: color,
|
||||
Fields: []slack.AttachmentField{
|
||||
{Title: "Exchange", Value: trade.Exchange.String(), Short: true},
|
||||
{Title: "Price", Value: trimTrailingZeroFloat(trade.Price.Float64()), Short: true},
|
||||
{Title: "Quantity", Value: trimTrailingZeroFloat(trade.Quantity.Float64()), Short: true},
|
||||
{Title: "QuoteQuantity", Value: trimTrailingZeroFloat(trade.QuoteQuantity.Float64()), Short: true},
|
||||
{Title: "Fee", Value: trimTrailingZeroFloat(trade.Fee.Float64()), Short: true},
|
||||
{Title: "Price", Value: trade.Price.String(), Short: true},
|
||||
{Title: "Quantity", Value: trade.Quantity.String(), Short: true},
|
||||
{Title: "QuoteQuantity", Value: trade.QuoteQuantity.String(), Short: true},
|
||||
{Title: "Fee", Value: trade.Fee.String(), Short: true},
|
||||
{Title: "FeeCurrency", Value: trade.FeeCurrency, Short: true},
|
||||
{Title: "Liquidity", Value: liquidity, Short: true},
|
||||
{Title: "Order ID", Value: strconv.FormatUint(trade.OrderID, 10), Short: true},
|
||||
|
|
|
@ -3,19 +3,20 @@ package types
|
|||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||
)
|
||||
|
||||
type Withdraw struct {
|
||||
GID int64 `json:"gid" db:"gid"`
|
||||
Exchange ExchangeName `json:"exchange" db:"exchange"`
|
||||
Asset string `json:"asset" db:"asset"`
|
||||
Amount float64 `json:"amount" db:"amount"`
|
||||
Amount fixedpoint.Value `json:"amount" db:"amount"`
|
||||
Address string `json:"address" db:"address"`
|
||||
AddressTag string `json:"addressTag"`
|
||||
Status string `json:"status"`
|
||||
|
||||
TransactionID string `json:"transactionID" db:"txn_id"`
|
||||
TransactionFee float64 `json:"transactionFee" db:"txn_fee"`
|
||||
TransactionFee fixedpoint.Value `json:"transactionFee" db:"txn_fee"`
|
||||
TransactionFeeCurrency string `json:"transactionFeeCurrency" db:"txn_fee_currency"`
|
||||
WithdrawOrderID string `json:"withdrawOrderId"`
|
||||
ApplyTime Time `json:"applyTime" db:"time"`
|
||||
|
|
|
@ -20,7 +20,7 @@ func Pow10(n int64) int64 {
|
|||
}
|
||||
|
||||
func FormatValue(val fixedpoint.Value, prec int) string {
|
||||
return strconv.FormatFloat(val.Float64(), 'f', prec, 64)
|
||||
return val.FormatString(prec)
|
||||
}
|
||||
|
||||
func FormatFloat(val float64, prec int) string {
|
||||
|
|
Loading…
Reference in New Issue
Block a user