mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
add stock calculation
This commit is contained in:
parent
aef57a5ae4
commit
a46462a5e4
44
bbgo/pnl.go
44
bbgo/pnl.go
|
@ -44,23 +44,24 @@ func (c *ProfitAndLossCalculator) Calculate() *ProfitAndLossReport {
|
|||
|
||||
var askVolume = 0.0
|
||||
var askFee = 0.0
|
||||
var feeRate = 0.001
|
||||
var feeRate = 0.0015
|
||||
|
||||
for _, trade := range trades {
|
||||
if trade.Symbol == c.Symbol {
|
||||
if trade.IsBuyer {
|
||||
bidVolume += trade.Quantity
|
||||
bidAmount += trade.Price * trade.Quantity
|
||||
|
||||
// since we use USDT as the quote currency, we simply check if it matches the currency symbol
|
||||
if strings.HasPrefix(trade.Symbol, trade.FeeCurrency) {
|
||||
bidVolume -= trade.Fee
|
||||
bidFee += trade.Price * trade.Fee
|
||||
} else if trade.FeeCurrency == "USDT" {
|
||||
bidFee += trade.Fee
|
||||
}
|
||||
}
|
||||
} else if strings.HasPrefix(c.Symbol, c.TradingFeeCurrency) && trade.FeeCurrency == c.TradingFeeCurrency {
|
||||
|
||||
// since we use USDT as the quote currency, we simply check if it matches the currency symbol
|
||||
if strings.HasPrefix(trade.Symbol, trade.FeeCurrency) {
|
||||
bidVolume -= trade.Fee
|
||||
bidFee += trade.Price * trade.Fee
|
||||
} else if trade.FeeCurrency == "USDT" {
|
||||
bidFee += trade.Fee
|
||||
}
|
||||
|
||||
} else if trade.FeeCurrency == c.TradingFeeCurrency {
|
||||
bidVolume -= trade.Fee
|
||||
}
|
||||
}
|
||||
|
@ -74,16 +75,18 @@ func (c *ProfitAndLossCalculator) Calculate() *ProfitAndLossReport {
|
|||
continue
|
||||
}
|
||||
|
||||
if !t.IsBuyer {
|
||||
profit += (t.Price - averageBidPrice) * t.Quantity
|
||||
askVolume += t.Quantity
|
||||
if t.IsBuyer {
|
||||
continue
|
||||
}
|
||||
|
||||
// since we use USDT as the quote currency, we simply check if it matches the currency symbol
|
||||
if strings.HasPrefix(t.Symbol, t.FeeCurrency) {
|
||||
askFee += t.Price * t.Fee
|
||||
} else if t.FeeCurrency == "USDT" {
|
||||
askFee += t.Fee
|
||||
}
|
||||
profit += (t.Price - averageBidPrice) * t.Quantity
|
||||
askVolume += t.Quantity
|
||||
|
||||
// since we use USDT as the quote currency, we simply check if it matches the currency symbol
|
||||
if strings.HasPrefix(t.Symbol, t.FeeCurrency) {
|
||||
askFee += t.Price * t.Fee
|
||||
} else if t.FeeCurrency == "USDT" {
|
||||
askFee += t.Fee
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,8 +138,9 @@ func (report ProfitAndLossReport) Print() {
|
|||
log.Infof("average bid price: %s", USD.FormatMoneyFloat64(report.AverageBidPrice))
|
||||
log.Infof("total bid volume: %f", report.BidVolume)
|
||||
log.Infof("total ask volume: %f", report.AskVolume)
|
||||
log.Infof("stock: %f", report.Stock)
|
||||
log.Infof("current price: %s", USD.FormatMoneyFloat64(report.CurrentPrice))
|
||||
log.Infof("overall profit: %s", USD.FormatMoneyFloat64(report.Profit))
|
||||
log.Infof("profit: %s", USD.FormatMoneyFloat64(report.Profit))
|
||||
}
|
||||
|
||||
func (report ProfitAndLossReport) SlackAttachment() slack.Attachment {
|
||||
|
|
119
bbgo/stock.go
Normal file
119
bbgo/stock.go
Normal file
|
@ -0,0 +1,119 @@
|
|||
package bbgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/c9s/bbgo/pkg/bbgo/types"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Stock types.Trade
|
||||
|
||||
func (stock *Stock) Consume(quantity float64) float64 {
|
||||
delta := math.Min(stock.Quantity, quantity)
|
||||
stock.Quantity -= delta
|
||||
return delta
|
||||
}
|
||||
|
||||
type StockManager struct {
|
||||
Symbol string
|
||||
TradingFeeCurrency string
|
||||
Stocks []Stock
|
||||
}
|
||||
|
||||
func (m *StockManager) Consume(sell Stock) error {
|
||||
if len(m.Stocks) == 0 {
|
||||
return fmt.Errorf("empty stock")
|
||||
}
|
||||
|
||||
idx := len(m.Stocks) - 1
|
||||
for ; idx >= 0; idx-- {
|
||||
stock := m.Stocks[idx]
|
||||
|
||||
// find any stock price is lower than the sell trade
|
||||
if stock.Price >= sell.Price {
|
||||
continue
|
||||
}
|
||||
|
||||
sell.Quantity -= stock.Consume(sell.Quantity)
|
||||
|
||||
if math.Round(stock.Quantity*1e8) < 0.0 {
|
||||
return fmt.Errorf("over sell")
|
||||
}
|
||||
|
||||
if math.Round(stock.Quantity*1e8) == 0.0 {
|
||||
m.Stocks = m.Stocks[:idx]
|
||||
}
|
||||
|
||||
if math.Round(sell.Quantity*1e8) == 0.0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
idx = len(m.Stocks) - 1
|
||||
for ; idx >= 0; idx-- {
|
||||
stock := m.Stocks[idx]
|
||||
|
||||
sell.Quantity -= stock.Consume(sell.Quantity)
|
||||
if math.Round(stock.Quantity*1e8) == 0.0 {
|
||||
m.Stocks = m.Stocks[:idx]
|
||||
}
|
||||
|
||||
if math.Round(sell.Quantity*1e8) == 0.0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if math.Round(sell.Quantity*1e8) > 0.0 {
|
||||
return fmt.Errorf("over sell quantity %f at %s", sell.Quantity, sell.Time)
|
||||
}
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StockManager) LoadTrades(trades []types.Trade) (checkpoints []int, err error) {
|
||||
feeSymbol := strings.HasPrefix(m.Symbol, m.TradingFeeCurrency)
|
||||
for idx, trade := range trades {
|
||||
log.Infof("%s %5s %f %f at %s", trade.Symbol, trade.Side, trade.Price, trade.Quantity, trade.Time)
|
||||
// for other market trades
|
||||
// convert trading fee trades to sell trade
|
||||
if trade.Symbol != m.Symbol {
|
||||
if feeSymbol && trade.FeeCurrency == m.TradingFeeCurrency {
|
||||
trade.Symbol = m.Symbol
|
||||
trade.IsBuyer = false
|
||||
trade.Quantity = trade.Fee
|
||||
trade.Fee = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
if trade.Symbol == m.Symbol {
|
||||
if trade.IsBuyer {
|
||||
m.Stocks = append(m.Stocks, toStock(trade))
|
||||
} else {
|
||||
if err := m.Consume(toStock(trade)) ; err != nil {
|
||||
return checkpoints, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(m.Stocks) == 0 {
|
||||
checkpoints = append(checkpoints, idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkpoints, nil
|
||||
}
|
||||
|
||||
func toStock(trade types.Trade) Stock {
|
||||
if strings.HasPrefix(trade.Symbol, trade.FeeCurrency) {
|
||||
if trade.IsBuyer {
|
||||
trade.Quantity -= trade.Fee
|
||||
} else {
|
||||
trade.Quantity += trade.Fee
|
||||
}
|
||||
trade.Fee = 0
|
||||
}
|
||||
return Stock(trade)
|
||||
}
|
|
@ -81,9 +81,10 @@ func (s *TradeService) QueryLast(symbol string) (*types.Trade, error) {
|
|||
return nil, rows.Err()
|
||||
}
|
||||
|
||||
func (s *TradeService) QueryForTradingFeeCurrency(symbol string) ([]types.Trade, error) {
|
||||
rows, err := s.DB.NamedQuery(`SELECT * FROM trades WHERE symbol = :symbol OR fee_currency = :symbol ORDER BY traded_at ASC`, map[string]interface{}{
|
||||
func (s *TradeService) QueryForTradingFeeCurrency(symbol string, feeCurrency string) ([]types.Trade, error) {
|
||||
rows, err := s.DB.NamedQuery(`SELECT * FROM trades WHERE symbol = :symbol OR fee_currency = :fee_currency ORDER BY traded_at ASC`, map[string]interface{}{
|
||||
"symbol": symbol,
|
||||
"fee_currency": feeCurrency,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
Loading…
Reference in New Issue
Block a user