2021-01-20 08:08:14 +00:00
|
|
|
package bbgo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
2021-05-16 09:50:08 +00:00
|
|
|
func TestPosition_ExchangeFeeRate_Short(t *testing.T) {
|
|
|
|
pos := &Position{
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
BaseCurrency: "BTC",
|
|
|
|
QuoteCurrency: "USDT",
|
|
|
|
}
|
|
|
|
|
|
|
|
feeRate := 0.075 * 0.01
|
|
|
|
pos.SetExchangeFeeRate(types.ExchangeBinance, ExchangeFee{
|
|
|
|
MakerFeeRate: fixedpoint.NewFromFloat(feeRate),
|
|
|
|
TakerFeeRate: fixedpoint.NewFromFloat(feeRate),
|
|
|
|
})
|
|
|
|
|
|
|
|
quantity := 10.0
|
|
|
|
quoteQuantity := 3000.0 * quantity
|
|
|
|
fee := quoteQuantity * feeRate
|
|
|
|
averageCost := (quoteQuantity - fee) / quantity
|
|
|
|
bnbPrice := 570.0
|
|
|
|
pos.AddTrade(types.Trade{
|
|
|
|
Exchange: types.ExchangeBinance,
|
|
|
|
Price: 3000.0,
|
|
|
|
Quantity: quantity,
|
|
|
|
QuoteQuantity: quoteQuantity,
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Fee: fee / bnbPrice,
|
|
|
|
FeeCurrency: "BNB",
|
|
|
|
})
|
|
|
|
|
|
|
|
profit, madeProfit := pos.AddTrade(types.Trade{
|
|
|
|
Exchange: types.ExchangeBinance,
|
|
|
|
Price: 2000.0,
|
|
|
|
Quantity: 10.0,
|
|
|
|
QuoteQuantity: 2000.0 * 10.0,
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Fee: 2000.0 * 10.0 * feeRate / bnbPrice,
|
|
|
|
FeeCurrency: "BNB",
|
|
|
|
})
|
|
|
|
|
|
|
|
expectedProfit := (averageCost-2000.0)*10.0 - (2000.0 * 10.0 * feeRate)
|
|
|
|
assert.True(t, madeProfit)
|
|
|
|
assert.Equal(t, fixedpoint.NewFromFloat(expectedProfit), profit)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPosition_ExchangeFeeRate_Long(t *testing.T) {
|
|
|
|
pos := &Position{
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
BaseCurrency: "BTC",
|
|
|
|
QuoteCurrency: "USDT",
|
|
|
|
}
|
|
|
|
|
|
|
|
feeRate := 0.075 * 0.01
|
|
|
|
pos.SetExchangeFeeRate(types.ExchangeBinance, ExchangeFee{
|
|
|
|
MakerFeeRate: fixedpoint.NewFromFloat(feeRate),
|
|
|
|
TakerFeeRate: fixedpoint.NewFromFloat(feeRate),
|
|
|
|
})
|
|
|
|
|
|
|
|
quantity := 10.0
|
|
|
|
quoteQuantity := 3000.0 * quantity
|
|
|
|
fee := quoteQuantity * feeRate
|
|
|
|
averageCost := (quoteQuantity + fee) / quantity
|
|
|
|
bnbPrice := 570.0
|
|
|
|
pos.AddTrade(types.Trade{
|
|
|
|
Exchange: types.ExchangeBinance,
|
|
|
|
Price: 3000.0,
|
|
|
|
Quantity: quantity,
|
|
|
|
QuoteQuantity: quoteQuantity,
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Fee: fee / bnbPrice,
|
|
|
|
FeeCurrency: "BNB",
|
|
|
|
})
|
|
|
|
|
|
|
|
profit, madeProfit := pos.AddTrade(types.Trade{
|
|
|
|
Exchange: types.ExchangeBinance,
|
|
|
|
Price: 4000.0,
|
|
|
|
Quantity: 10.0,
|
|
|
|
QuoteQuantity: 4000.0 * 10.0,
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Fee: 4000.0 * 10.0 * feeRate / bnbPrice,
|
|
|
|
FeeCurrency: "BNB",
|
|
|
|
})
|
|
|
|
|
|
|
|
expectedProfit := (4000.0-averageCost)*10.0 - (4000.0 * 10.0 * feeRate)
|
|
|
|
assert.True(t, madeProfit)
|
|
|
|
assert.Equal(t, fixedpoint.NewFromFloat(expectedProfit), profit)
|
|
|
|
}
|
|
|
|
|
2021-01-20 08:08:14 +00:00
|
|
|
func TestPosition(t *testing.T) {
|
2021-01-20 16:49:01 +00:00
|
|
|
var feeRate = 0.05 * 0.01
|
2021-01-20 09:35:58 +00:00
|
|
|
var testcases = []struct {
|
|
|
|
name string
|
|
|
|
trades []types.Trade
|
|
|
|
expectedAverageCost fixedpoint.Value
|
|
|
|
expectedBase fixedpoint.Value
|
|
|
|
expectedQuote fixedpoint.Value
|
|
|
|
expectedProfit fixedpoint.Value
|
|
|
|
}{
|
2021-01-20 16:49:01 +00:00
|
|
|
{
|
|
|
|
name: "base fee",
|
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 1000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 1000.0 * 0.01,
|
|
|
|
Fee: 0.01 * 0.05 * 0.01, // 0.05%
|
|
|
|
FeeCurrency: "BTC",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat((1000.0 * 0.01) / (0.01 * (1.0 - feeRate))),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(0.01 - (0.01 * feeRate)),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(0 - 1000.0*0.01),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat(0.0),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "quote fee",
|
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Price: 1000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 1000.0 * 0.01,
|
|
|
|
Fee: (1000.0 * 0.01) * feeRate, // 0.05%
|
|
|
|
FeeCurrency: "USDT",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat((1000.0 * 0.01 * (1.0 - feeRate)) / 0.01),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(-0.01),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(0 + 1000.0*0.01*(1.0-feeRate)),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat(0.0),
|
|
|
|
},
|
2021-01-20 08:08:14 +00:00
|
|
|
{
|
2021-01-20 09:35:58 +00:00
|
|
|
name: "long",
|
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 1000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 1000.0 * 0.01,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 2000.0,
|
|
|
|
Quantity: 0.03,
|
|
|
|
QuoteQuantity: 2000.0 * 0.03,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat((1000.0*0.01 + 2000.0*0.03) / 0.04),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(0.01 + 0.03),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(0 - 1000.0*0.01 - 2000.0*0.03),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat(0.0),
|
2021-01-20 08:08:14 +00:00
|
|
|
},
|
2021-01-20 09:35:58 +00:00
|
|
|
|
2021-01-20 08:08:14 +00:00
|
|
|
{
|
2021-01-20 09:35:58 +00:00
|
|
|
name: "long and sell",
|
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 1000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 1000.0 * 0.01,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 2000.0,
|
|
|
|
Quantity: 0.03,
|
|
|
|
QuoteQuantity: 2000.0 * 0.03,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Price: 3000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 3000.0 * 0.01,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat((1000.0*0.01 + 2000.0*0.03) / 0.04),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(0.03),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(0 - 1000.0*0.01 - 2000.0*0.03 + 3000.0*0.01),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat((3000.0 - (1000.0*0.01+2000.0*0.03)/0.04) * 0.01),
|
2021-01-20 08:08:14 +00:00
|
|
|
},
|
|
|
|
|
2021-01-20 09:35:58 +00:00
|
|
|
{
|
2021-01-20 16:54:59 +00:00
|
|
|
name: "long and sell to short",
|
2021-01-20 09:35:58 +00:00
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 1000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 1000.0 * 0.01,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeBuy,
|
|
|
|
Price: 2000.0,
|
|
|
|
Quantity: 0.03,
|
|
|
|
QuoteQuantity: 2000.0 * 0.03,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Price: 3000.0,
|
|
|
|
Quantity: 0.10,
|
|
|
|
QuoteQuantity: 3000.0 * 0.10,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat(3000.0),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(-0.06),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(-1000.0*0.01 - 2000.0*0.03 + 3000.0*0.1),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat((3000.0 - (1000.0*0.01+2000.0*0.03)/0.04) * 0.04),
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
name: "short",
|
|
|
|
trades: []types.Trade{
|
|
|
|
{
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Price: 2000.0,
|
|
|
|
Quantity: 0.01,
|
|
|
|
QuoteQuantity: 2000.0 * 0.01,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Side: types.SideTypeSell,
|
|
|
|
Price: 3000.0,
|
|
|
|
Quantity: 0.03,
|
|
|
|
QuoteQuantity: 3000.0 * 0.03,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
expectedAverageCost: fixedpoint.NewFromFloat((2000.0*0.01 + 3000.0*0.03) / (0.01 + 0.03)),
|
|
|
|
expectedBase: fixedpoint.NewFromFloat(0 - 0.01 - 0.03),
|
|
|
|
expectedQuote: fixedpoint.NewFromFloat(2000.0*0.01 + 3000.0*0.03),
|
|
|
|
expectedProfit: fixedpoint.NewFromFloat(0.0),
|
|
|
|
},
|
2021-01-20 08:08:14 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 09:35:58 +00:00
|
|
|
for _, testcase := range testcases {
|
|
|
|
t.Run(testcase.name, func(t *testing.T) {
|
2021-01-20 16:49:01 +00:00
|
|
|
pos := Position{
|
|
|
|
Symbol: "BTCUSDT",
|
|
|
|
BaseCurrency: "BTC",
|
|
|
|
QuoteCurrency: "USDT",
|
|
|
|
}
|
2021-01-20 09:35:58 +00:00
|
|
|
profitAmount, profit := pos.AddTrades(testcase.trades)
|
|
|
|
|
|
|
|
assert.Equal(t, testcase.expectedQuote, pos.Quote, "expectedQuote")
|
|
|
|
assert.Equal(t, testcase.expectedBase, pos.Base, "expectedBase")
|
|
|
|
assert.Equal(t, testcase.expectedAverageCost, pos.AverageCost, "expectedAverageCost")
|
|
|
|
if profit {
|
|
|
|
assert.Equal(t, testcase.expectedProfit, profitAmount, "expectedProfit")
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-01-20 08:08:14 +00:00
|
|
|
}
|