mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-10 09:11:55 +00:00
Compare commits
13 Commits
4b1d88df54
...
24bf561026
Author | SHA1 | Date | |
---|---|---|---|
|
24bf561026 | ||
|
2bf1072977 | ||
|
4d1c357c3d | ||
|
a4833524cf | ||
|
ed073264f1 | ||
|
ad6056834e | ||
|
8b1306a6a6 | ||
|
d85da78e17 | ||
|
5e3b0238d8 | ||
|
bc24e8dcaa | ||
|
4fefbaf0e7 | ||
|
b683550f44 | ||
|
26149103f0 |
153
pkg/core/convert_manager.go
Normal file
153
pkg/core/convert_manager.go
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConverterSetting struct {
|
||||||
|
SymbolConverter *SymbolConverter `json:"symbolConverter" yaml:"symbolConverter"`
|
||||||
|
CurrencyConverter *CurrencyConverter `json:"currencyConverter" yaml:"currencyConverter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConverterSetting) getConverter() Converter {
|
||||||
|
if s.SymbolConverter != nil {
|
||||||
|
return s.SymbolConverter
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.CurrencyConverter != nil {
|
||||||
|
return s.CurrencyConverter
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ConverterSetting) InitializeConverter() (Converter, error) {
|
||||||
|
converter := s.getConverter()
|
||||||
|
if converter == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Infof("initializing converter %T ...", converter)
|
||||||
|
err := converter.Initialize()
|
||||||
|
return converter, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConverterManager manages the converters for trade conversion
|
||||||
|
// It can be used to convert the trade symbol into the target symbol, or convert the price, volume into different units.
|
||||||
|
type ConverterManager struct {
|
||||||
|
ConverterSettings []ConverterSetting `json:"converters,omitempty" yaml:"converters,omitempty"`
|
||||||
|
|
||||||
|
converters []Converter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) Initialize() error {
|
||||||
|
for _, setting := range c.ConverterSettings {
|
||||||
|
converter, err := setting.InitializeConverter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if converter != nil {
|
||||||
|
c.AddConverter(converter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
numConverters := len(c.converters)
|
||||||
|
logrus.Infof("%d converters loaded", numConverters)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) AddConverter(converter Converter) {
|
||||||
|
c.converters = append(c.converters, converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) ConvertOrder(order types.Order) types.Order {
|
||||||
|
if len(c.converters) == 0 {
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.converters {
|
||||||
|
convOrder, err := converter.ConvertOrder(order)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("converter %+v error, order: %s", converter, order.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
order = convOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
return order
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) ConvertTrade(trade types.Trade) types.Trade {
|
||||||
|
if len(c.converters) == 0 {
|
||||||
|
return trade
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.converters {
|
||||||
|
convTrade, err := converter.ConvertTrade(trade)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("converter %+v error, trade: %s", converter, trade.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
trade = convTrade
|
||||||
|
}
|
||||||
|
|
||||||
|
return trade
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) ConvertKLine(kline types.KLine) types.KLine {
|
||||||
|
if len(c.converters) == 0 {
|
||||||
|
return kline
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.converters {
|
||||||
|
convKline, err := converter.ConvertKLine(kline)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("converter %+v error, kline: %s", converter, kline.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
kline = convKline
|
||||||
|
}
|
||||||
|
|
||||||
|
return kline
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) ConvertMarket(market types.Market) types.Market {
|
||||||
|
if len(c.converters) == 0 {
|
||||||
|
return market
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.converters {
|
||||||
|
convMarket, err := converter.ConvertMarket(market)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("converter %+v error, market: %+v", converter, market)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
market = convMarket
|
||||||
|
}
|
||||||
|
|
||||||
|
return market
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConverterManager) ConvertBalance(balance types.Balance) types.Balance {
|
||||||
|
if len(c.converters) == 0 {
|
||||||
|
return balance
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, converter := range c.converters {
|
||||||
|
convBal, err := converter.ConvertBalance(balance)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("converter %+v error, balance: %s", converter, balance.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
balance = convBal
|
||||||
|
}
|
||||||
|
|
||||||
|
return balance
|
||||||
|
}
|
212
pkg/core/convert_manager_test.go
Normal file
212
pkg/core/convert_manager_test.go
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInitializeConverter_ValidSymbolConverter(t *testing.T) {
|
||||||
|
setting := ConverterSetting{
|
||||||
|
SymbolConverter: &SymbolConverter{
|
||||||
|
FromSymbol: "MAXEXCHANGEUSDT",
|
||||||
|
ToSymbol: "MAXUSDT",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
converter, err := setting.InitializeConverter()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitializeConverter_ValidCurrencyConverter(t *testing.T) {
|
||||||
|
setting := ConverterSetting{
|
||||||
|
CurrencyConverter: &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
converter, err := setting.InitializeConverter()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitializeConverter_NoConverter(t *testing.T) {
|
||||||
|
setting := ConverterSetting{}
|
||||||
|
converter, err := setting.InitializeConverter()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, converter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialize_ValidConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{
|
||||||
|
ConverterSettings: []ConverterSetting{
|
||||||
|
{SymbolConverter: &SymbolConverter{
|
||||||
|
FromSymbol: "MAXEXCHANGEUSDT",
|
||||||
|
ToSymbol: "MAXUSDT",
|
||||||
|
}},
|
||||||
|
{CurrencyConverter: &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 2, len(manager.converters))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialize_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 0, len(manager.converters))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertOrder_WithConverters(t *testing.T) {
|
||||||
|
jsonStr := `
|
||||||
|
{
|
||||||
|
"converters": [
|
||||||
|
{
|
||||||
|
"symbolConverter": {
|
||||||
|
"from": "MAXEXCHANGEUSDT",
|
||||||
|
"to": "MAXUSDT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"currencyConverter": {
|
||||||
|
"from": "MAXEXCHANGE",
|
||||||
|
"to": "MAX"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
manager := ConverterManager{}
|
||||||
|
err := json.Unmarshal([]byte(jsonStr), &manager)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
order := types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
Market: types.Market{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
QuoteCurrency: "USDT",
|
||||||
|
BaseCurrency: "MAXEXCHANGE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedOrder := manager.ConvertOrder(order)
|
||||||
|
assert.Equal(t, "MAXUSDT", convertedOrder.Symbol)
|
||||||
|
assert.Equal(t, "MAX", convertedOrder.Market.BaseCurrency)
|
||||||
|
assert.Equal(t, "USDT", convertedOrder.Market.QuoteCurrency)
|
||||||
|
assert.Equal(t, "MAXUSDT", convertedOrder.Market.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertOrder_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
order := types.Order{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedOrder := manager.ConvertOrder(order)
|
||||||
|
assert.Equal(t, order, convertedOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertTrade_WithConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
converter := &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
manager.AddConverter(converter)
|
||||||
|
|
||||||
|
trade := types.Trade{}
|
||||||
|
convertedTrade := manager.ConvertTrade(trade)
|
||||||
|
assert.Equal(t, trade, convertedTrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertTrade_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
trade := types.Trade{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedTrade := manager.ConvertTrade(trade)
|
||||||
|
assert.Equal(t, trade, convertedTrade)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertKLine_WithConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
converter := &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
manager.AddConverter(converter)
|
||||||
|
|
||||||
|
kline := types.KLine{}
|
||||||
|
convertedKline := manager.ConvertKLine(kline)
|
||||||
|
assert.Equal(t, kline, convertedKline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertKLine_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
kline := types.KLine{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedKline := manager.ConvertKLine(kline)
|
||||||
|
assert.Equal(t, kline, convertedKline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMarket_WithConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
converter := &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
manager.AddConverter(converter)
|
||||||
|
|
||||||
|
market := types.Market{}
|
||||||
|
convertedMarket := manager.ConvertMarket(market)
|
||||||
|
assert.Equal(t, market, convertedMarket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMarket_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
market := types.Market{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedMarket := manager.ConvertMarket(market)
|
||||||
|
assert.Equal(t, market, convertedMarket)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertBalance_WithConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
converter := &CurrencyConverter{
|
||||||
|
FromCurrency: "MAXEXCHANGE",
|
||||||
|
ToCurrency: "MAX",
|
||||||
|
}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
manager.AddConverter(converter)
|
||||||
|
|
||||||
|
balance := types.Balance{}
|
||||||
|
convertedBalance := manager.ConvertBalance(balance)
|
||||||
|
assert.Equal(t, balance, convertedBalance)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertBalance_NoConverters(t *testing.T) {
|
||||||
|
manager := ConverterManager{}
|
||||||
|
balance := types.Balance{}
|
||||||
|
err := manager.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
convertedBalance := manager.ConvertBalance(balance)
|
||||||
|
assert.Equal(t, balance, convertedBalance)
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSymbolConverter(t *testing.T) {
|
|
||||||
converter := NewSymbolConverter("MAXEXCHANGEUSDT", "MAXUSDT")
|
|
||||||
trade, err := converter.ConvertTrade(types.Trade{
|
|
||||||
Symbol: "MAXEXCHANGEUSDT",
|
|
||||||
})
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, "MAXUSDT", trade.Symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
order, err := converter.ConvertOrder(types.Order{
|
|
||||||
SubmitOrder: types.SubmitOrder{
|
|
||||||
Symbol: "MAXEXCHANGEUSDT",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if assert.NoError(t, err) {
|
|
||||||
assert.Equal(t, "MAXUSDT", order.Symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
68
pkg/core/currency_converter.go
Normal file
68
pkg/core/currency_converter.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CurrencyConverter struct {
|
||||||
|
FromCurrency string `json:"from"`
|
||||||
|
ToCurrency string `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCurrencyConverter(fromSymbol, toSymbol string) *CurrencyConverter {
|
||||||
|
return &CurrencyConverter{FromCurrency: fromSymbol, ToCurrency: toSymbol}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) Initialize() error {
|
||||||
|
if c.FromCurrency == "" {
|
||||||
|
return errors.New("FromCurrency can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ToCurrency == "" {
|
||||||
|
return errors.New("ToCurrency can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) ConvertOrder(order types.Order) (types.Order, error) {
|
||||||
|
if order.SubmitOrder.Market.QuoteCurrency == c.FromCurrency {
|
||||||
|
order.SubmitOrder.Market.QuoteCurrency = c.ToCurrency
|
||||||
|
}
|
||||||
|
if order.SubmitOrder.Market.BaseCurrency == c.FromCurrency {
|
||||||
|
order.SubmitOrder.Market.BaseCurrency = c.ToCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
return order, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) ConvertTrade(trade types.Trade) (types.Trade, error) {
|
||||||
|
if trade.FeeCurrency == c.FromCurrency {
|
||||||
|
trade.FeeCurrency = c.ToCurrency
|
||||||
|
}
|
||||||
|
return trade, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) ConvertKLine(kline types.KLine) (types.KLine, error) {
|
||||||
|
return kline, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) ConvertMarket(mkt types.Market) (types.Market, error) {
|
||||||
|
if mkt.QuoteCurrency == c.FromCurrency {
|
||||||
|
mkt.QuoteCurrency = c.ToCurrency
|
||||||
|
}
|
||||||
|
if mkt.BaseCurrency == c.FromCurrency {
|
||||||
|
mkt.BaseCurrency = c.ToCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
return mkt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CurrencyConverter) ConvertBalance(balance types.Balance) (types.Balance, error) {
|
||||||
|
if balance.Currency == c.FromCurrency {
|
||||||
|
balance.Currency = c.ToCurrency
|
||||||
|
}
|
||||||
|
|
||||||
|
return balance, nil
|
||||||
|
}
|
124
pkg/core/currency_converter_test.go
Normal file
124
pkg/core/currency_converter_test.go
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// pkg/core/tradecollector_test.go
|
||||||
|
func TestInitialize_ValidCurrencies(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
err := converter.Initialize()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialize_EmptyFromCurrency(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("", "MAX")
|
||||||
|
err := converter.Initialize()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "FromCurrency can not be empty", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitialize_EmptyToCurrency(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "")
|
||||||
|
err := converter.Initialize()
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, "ToCurrency can not be empty", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertOrder_ValidConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
order := types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
Market: types.Market{
|
||||||
|
QuoteCurrency: "MAXEXCHANGE",
|
||||||
|
BaseCurrency: "MAXEXCHANGE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
convertedOrder, err := converter.ConvertOrder(order)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "MAX", convertedOrder.SubmitOrder.Market.QuoteCurrency)
|
||||||
|
assert.Equal(t, "MAX", convertedOrder.SubmitOrder.Market.BaseCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertOrder_NoConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
order := types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
Market: types.Market{
|
||||||
|
QuoteCurrency: "JPY",
|
||||||
|
BaseCurrency: "JPY",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
convertedOrder, err := converter.ConvertOrder(order)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "JPY", convertedOrder.SubmitOrder.Market.QuoteCurrency)
|
||||||
|
assert.Equal(t, "JPY", convertedOrder.SubmitOrder.Market.BaseCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertTrade_ValidConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
trade := types.Trade{
|
||||||
|
FeeCurrency: "MAXEXCHANGE",
|
||||||
|
}
|
||||||
|
convertedTrade, err := converter.ConvertTrade(trade)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "MAX", convertedTrade.FeeCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertTrade_NoConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
trade := types.Trade{
|
||||||
|
FeeCurrency: "JPY",
|
||||||
|
}
|
||||||
|
convertedTrade, err := converter.ConvertTrade(trade)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "JPY", convertedTrade.FeeCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMarket_ValidConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
market := types.Market{
|
||||||
|
QuoteCurrency: "MAXEXCHANGE",
|
||||||
|
BaseCurrency: "MAXEXCHANGE",
|
||||||
|
}
|
||||||
|
convertedMarket, err := converter.ConvertMarket(market)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "MAX", convertedMarket.QuoteCurrency)
|
||||||
|
assert.Equal(t, "MAX", convertedMarket.BaseCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertMarket_NoConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
market := types.Market{
|
||||||
|
QuoteCurrency: "JPY",
|
||||||
|
BaseCurrency: "JPY",
|
||||||
|
}
|
||||||
|
convertedMarket, err := converter.ConvertMarket(market)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "JPY", convertedMarket.QuoteCurrency)
|
||||||
|
assert.Equal(t, "JPY", convertedMarket.BaseCurrency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertBalance_ValidConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
balance := types.Balance{
|
||||||
|
Currency: "MAXEXCHANGE",
|
||||||
|
}
|
||||||
|
convertedBalance, err := converter.ConvertBalance(balance)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "MAX", convertedBalance.Currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertBalance_NoConversion(t *testing.T) {
|
||||||
|
converter := NewCurrencyConverter("MAXEXCHANGE", "MAX")
|
||||||
|
balance := types.Balance{
|
||||||
|
Currency: "JPY",
|
||||||
|
}
|
||||||
|
convertedBalance, err := converter.ConvertBalance(balance)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "JPY", convertedBalance.Currency)
|
||||||
|
}
|
|
@ -9,6 +9,9 @@ import (
|
||||||
type Converter interface {
|
type Converter interface {
|
||||||
OrderConverter
|
OrderConverter
|
||||||
TradeConverter
|
TradeConverter
|
||||||
|
KLineConverter
|
||||||
|
MarketConverter
|
||||||
|
BalanceConverter
|
||||||
Initialize() error
|
Initialize() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,12 +25,33 @@ type TradeConverter interface {
|
||||||
ConvertTrade(trade types.Trade) (types.Trade, error)
|
ConvertTrade(trade types.Trade) (types.Trade, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KLineConverter converts the kline to another kline
|
||||||
|
type KLineConverter interface {
|
||||||
|
ConvertKLine(kline types.KLine) (types.KLine, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarketConverter converts the market to another market
|
||||||
|
type MarketConverter interface {
|
||||||
|
ConvertMarket(market types.Market) (types.Market, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BalanceConverter converts the balance to another balance
|
||||||
|
type BalanceConverter interface {
|
||||||
|
ConvertBalance(balance types.Balance) (types.Balance, error)
|
||||||
|
}
|
||||||
|
|
||||||
type OrderConvertFunc func(order types.Order) (types.Order, error)
|
type OrderConvertFunc func(order types.Order) (types.Order, error)
|
||||||
type TradeConvertFunc func(trade types.Trade) (types.Trade, error)
|
type TradeConvertFunc func(trade types.Trade) (types.Trade, error)
|
||||||
|
type KLineConvertFunc func(kline types.KLine) (types.KLine, error)
|
||||||
|
type MarketConvertFunc func(market types.Market) (types.Market, error)
|
||||||
|
type BalanceConvertFunc func(balance types.Balance) (types.Balance, error)
|
||||||
|
|
||||||
type DynamicConverter struct {
|
type DynamicConverter struct {
|
||||||
orderConverter OrderConvertFunc
|
orderConverter OrderConvertFunc
|
||||||
tradeConverter TradeConvertFunc
|
tradeConverter TradeConvertFunc
|
||||||
|
klineConverter KLineConvertFunc
|
||||||
|
marketConverter MarketConvertFunc
|
||||||
|
balanceConverter BalanceConvertFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDynamicConverter(orderConverter OrderConvertFunc, tradeConverter TradeConvertFunc) *DynamicConverter {
|
func NewDynamicConverter(orderConverter OrderConvertFunc, tradeConverter TradeConvertFunc) *DynamicConverter {
|
||||||
|
@ -46,6 +70,18 @@ func (c *DynamicConverter) ConvertTrade(trade types.Trade) (types.Trade, error)
|
||||||
return c.tradeConverter(trade)
|
return c.tradeConverter(trade)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *DynamicConverter) ConvertKLine(kline types.KLine) (types.KLine, error) {
|
||||||
|
return c.klineConverter(kline)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DynamicConverter) ConvertMarket(market types.Market) (types.Market, error) {
|
||||||
|
return c.marketConverter(market)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *DynamicConverter) ConvertBalance(balance types.Balance) (types.Balance, error) {
|
||||||
|
return c.balanceConverter(balance)
|
||||||
|
}
|
||||||
|
|
||||||
// SymbolConverter converts the symbol to another symbol
|
// SymbolConverter converts the symbol to another symbol
|
||||||
type SymbolConverter struct {
|
type SymbolConverter struct {
|
||||||
FromSymbol string `json:"from"`
|
FromSymbol string `json:"from"`
|
||||||
|
@ -73,6 +109,10 @@ func (c *SymbolConverter) ConvertOrder(order types.Order) (types.Order, error) {
|
||||||
order.Symbol = c.ToSymbol
|
order.Symbol = c.ToSymbol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if order.SubmitOrder.Market.Symbol == c.FromSymbol {
|
||||||
|
order.SubmitOrder.Market.Symbol = c.ToSymbol
|
||||||
|
}
|
||||||
|
|
||||||
return order, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,3 +123,22 @@ func (c *SymbolConverter) ConvertTrade(trade types.Trade) (types.Trade, error) {
|
||||||
|
|
||||||
return trade, nil
|
return trade, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *SymbolConverter) ConvertKLine(kline types.KLine) (types.KLine, error) {
|
||||||
|
if kline.Symbol == c.FromSymbol {
|
||||||
|
kline.Symbol = c.ToSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
return kline, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SymbolConverter) ConvertMarket(mkt types.Market) (types.Market, error) {
|
||||||
|
if mkt.Symbol == s.FromSymbol {
|
||||||
|
mkt.Symbol = s.ToSymbol
|
||||||
|
}
|
||||||
|
return mkt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SymbolConverter) ConvertBalance(balance types.Balance) (types.Balance, error) {
|
||||||
|
return balance, nil
|
||||||
|
}
|
60
pkg/core/symbol_converter_test.go
Normal file
60
pkg/core/symbol_converter_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSymbolConverter(t *testing.T) {
|
||||||
|
converter := NewSymbolConverter("MAXEXCHANGEUSDT", "MAXUSDT")
|
||||||
|
trade, err := converter.ConvertTrade(types.Trade{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
})
|
||||||
|
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "MAXUSDT", trade.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
order, err := converter.ConvertOrder(types.Order{
|
||||||
|
SubmitOrder: types.SubmitOrder{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
Market: types.Market{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "MAXUSDT", order.Symbol)
|
||||||
|
assert.Equal(t, "MAXUSDT", order.SubmitOrder.Symbol)
|
||||||
|
assert.Equal(t, "MAXUSDT", order.SubmitOrder.Market.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
kline, err := converter.ConvertKLine(types.KLine{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
})
|
||||||
|
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "MAXUSDT", kline.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
market, err := converter.ConvertMarket(types.Market{
|
||||||
|
Symbol: "MAXEXCHANGEUSDT",
|
||||||
|
})
|
||||||
|
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "MAXUSDT", market.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
balance, err := converter.ConvertBalance(types.Balance{
|
||||||
|
Currency: "MAXEXCHANGE",
|
||||||
|
})
|
||||||
|
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
assert.Equal(t, "MAXEXCHANGE", balance.Currency)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,94 +12,6 @@ import (
|
||||||
"github.com/c9s/bbgo/pkg/types"
|
"github.com/c9s/bbgo/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ConverterSetting struct {
|
|
||||||
SymbolConverter *SymbolConverter `json:"symbolConverter" yaml:"symbolConverter"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ConverterSetting) getConverter() Converter {
|
|
||||||
if s.SymbolConverter != nil {
|
|
||||||
return s.SymbolConverter
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ConverterSetting) InitializeConverter() (Converter, error) {
|
|
||||||
converter := s.getConverter()
|
|
||||||
if converter == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Infof("initializing converter %T ...", converter)
|
|
||||||
err := converter.Initialize()
|
|
||||||
return converter, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConverterManager manages the converters for trade conversion
|
|
||||||
// It can be used to convert the trade symbol into the target symbol, or convert the price, volume into different units.
|
|
||||||
type ConverterManager struct {
|
|
||||||
ConverterSettings []ConverterSetting `json:"converters,omitempty" yaml:"converters,omitempty"`
|
|
||||||
|
|
||||||
converters []Converter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConverterManager) Initialize() error {
|
|
||||||
for _, setting := range c.ConverterSettings {
|
|
||||||
converter, err := setting.InitializeConverter()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if converter != nil {
|
|
||||||
c.AddConverter(converter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
numConverters := len(c.converters)
|
|
||||||
logrus.Infof("%d converters loaded", numConverters)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConverterManager) AddConverter(converter Converter) {
|
|
||||||
c.converters = append(c.converters, converter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConverterManager) ConvertOrder(order types.Order) types.Order {
|
|
||||||
if len(c.converters) == 0 {
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, converter := range c.converters {
|
|
||||||
convOrder, err := converter.ConvertOrder(order)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Errorf("converter %+v error, order: %s", converter, order.String())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
order = convOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConverterManager) ConvertTrade(trade types.Trade) types.Trade {
|
|
||||||
if len(c.converters) == 0 {
|
|
||||||
return trade
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, converter := range c.converters {
|
|
||||||
convTrade, err := converter.ConvertTrade(trade)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Errorf("converter %+v error, trade: %s", converter, trade.String())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
trade = convTrade
|
|
||||||
}
|
|
||||||
|
|
||||||
return trade
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:generate callbackgen -type TradeCollector
|
//go:generate callbackgen -type TradeCollector
|
||||||
type TradeCollector struct {
|
type TradeCollector struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
|
|
|
@ -119,6 +119,8 @@ type Strategy struct {
|
||||||
// MaxExposurePosition defines the unhedged quantity of stop
|
// MaxExposurePosition defines the unhedged quantity of stop
|
||||||
MaxExposurePosition fixedpoint.Value `json:"maxExposurePosition"`
|
MaxExposurePosition fixedpoint.Value `json:"maxExposurePosition"`
|
||||||
|
|
||||||
|
MaxHedgeAccountLeverage fixedpoint.Value `json:"maxHedgeAccountLeverage"`
|
||||||
|
|
||||||
DisableHedge bool `json:"disableHedge"`
|
DisableHedge bool `json:"disableHedge"`
|
||||||
|
|
||||||
NotifyTrade bool `json:"notifyTrade"`
|
NotifyTrade bool `json:"notifyTrade"`
|
||||||
|
@ -479,6 +481,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
makerQuota.BaseAsset.Add(b.Available)
|
makerQuota.BaseAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
disableMakerAsk = true
|
disableMakerAsk = true
|
||||||
|
s.logger.Infof("%s maker ask disabled: insufficient base balance %s", s.Symbol, b.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,6 +490,7 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
makerQuota.QuoteAsset.Add(b.Available)
|
makerQuota.QuoteAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
disableMakerBid = true
|
disableMakerBid = true
|
||||||
|
s.logger.Infof("%s maker bid disabled: insufficient quote balance %s", s.Symbol, b.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,6 +507,10 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
!hedgeAccount.MarginLevel.IsZero() {
|
!hedgeAccount.MarginLevel.IsZero() {
|
||||||
|
|
||||||
if hedgeAccount.MarginLevel.Compare(s.MinMarginLevel) < 0 {
|
if hedgeAccount.MarginLevel.Compare(s.MinMarginLevel) < 0 {
|
||||||
|
s.logger.Infof("hedge account margin level %s is less then the min margin level %s, calculating the borrowed positions",
|
||||||
|
hedgeAccount.MarginLevel.String(),
|
||||||
|
s.MinMarginLevel.String())
|
||||||
|
|
||||||
if quote, ok := hedgeAccount.Balance(s.sourceMarket.QuoteCurrency); ok {
|
if quote, ok := hedgeAccount.Balance(s.sourceMarket.QuoteCurrency); ok {
|
||||||
quoteDebt := quote.Debt()
|
quoteDebt := quote.Debt()
|
||||||
if quoteDebt.Sign() > 0 {
|
if quoteDebt.Sign() > 0 {
|
||||||
|
@ -517,24 +525,46 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// credit buffer
|
s.logger.Infof("hedge account margin level %s is greater than the min margin level %s, calculating the net value",
|
||||||
creditBufferRatio := fixedpoint.NewFromFloat(1.2)
|
hedgeAccount.MarginLevel.String(),
|
||||||
|
s.MinMarginLevel.String())
|
||||||
|
|
||||||
|
netValueInUsd, calcErr := s.accountValueCalculator.NetValue(ctx)
|
||||||
|
if calcErr != nil {
|
||||||
|
s.logger.WithError(calcErr).Errorf("unable to calculate the net value")
|
||||||
|
} else {
|
||||||
|
// calculate credit buffer
|
||||||
|
s.logger.Infof("hedge account net value in usd: %f", netValueInUsd.Float64())
|
||||||
|
|
||||||
|
maximumValueInUsd := netValueInUsd.Mul(s.MaxHedgeAccountLeverage)
|
||||||
|
|
||||||
|
s.logger.Infof("hedge account maximum leveraged value in usd: %f (%f x)", maximumValueInUsd.Float64(), s.MaxHedgeAccountLeverage.Float64())
|
||||||
|
|
||||||
if quote, ok := hedgeAccount.Balance(s.sourceMarket.QuoteCurrency); ok {
|
if quote, ok := hedgeAccount.Balance(s.sourceMarket.QuoteCurrency); ok {
|
||||||
netQuote := quote.Net()
|
debt := quote.Debt()
|
||||||
if netQuote.Sign() > 0 {
|
quota := maximumValueInUsd.Sub(debt)
|
||||||
hedgeQuota.QuoteAsset.Add(netQuote.Mul(creditBufferRatio))
|
|
||||||
}
|
s.logger.Infof("hedge account quote balance: %s, debt: %s, quota: %s",
|
||||||
|
quote.String(),
|
||||||
|
debt.String(),
|
||||||
|
quota.String())
|
||||||
|
|
||||||
|
hedgeQuota.QuoteAsset.Add(quota)
|
||||||
}
|
}
|
||||||
|
|
||||||
if base, ok := hedgeAccount.Balance(s.sourceMarket.BaseCurrency); ok {
|
if base, ok := hedgeAccount.Balance(s.sourceMarket.BaseCurrency); ok {
|
||||||
netBase := base.Net()
|
debt := base.Debt()
|
||||||
if netBase.Sign() > 0 {
|
quota := maximumValueInUsd.Div(bestAsk.Price).Sub(debt)
|
||||||
hedgeQuota.BaseAsset.Add(netBase.Mul(creditBufferRatio))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// netValueInUsd, err := s.accountValueCalculator.NetValue(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
s.logger.Infof("hedge account base balance: %s, debt: %s, quota: %s",
|
||||||
|
base.String(),
|
||||||
|
debt.String(),
|
||||||
|
quota.String())
|
||||||
|
|
||||||
|
hedgeQuota.BaseAsset.Add(quota)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if b, ok := hedgeBalances[s.sourceMarket.BaseCurrency]; ok {
|
if b, ok := hedgeBalances[s.sourceMarket.BaseCurrency]; ok {
|
||||||
// to make bid orders, we need enough base asset in the foreign exchange,
|
// to make bid orders, we need enough base asset in the foreign exchange,
|
||||||
|
@ -544,13 +574,13 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if b.Available.Compare(minAvailable) > 0 {
|
if b.Available.Compare(minAvailable) > 0 {
|
||||||
hedgeQuota.BaseAsset.Add(b.Available.Sub(minAvailable))
|
hedgeQuota.BaseAsset.Add(b.Available.Sub(minAvailable))
|
||||||
} else {
|
} else {
|
||||||
s.logger.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker bid disabled: insufficient hedge base balance %s", s.Symbol, b.String())
|
||||||
disableMakerBid = true
|
disableMakerBid = true
|
||||||
}
|
}
|
||||||
} else if b.Available.Compare(s.sourceMarket.MinQuantity) > 0 {
|
} else if b.Available.Compare(s.sourceMarket.MinQuantity) > 0 {
|
||||||
hedgeQuota.BaseAsset.Add(b.Available)
|
hedgeQuota.BaseAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
s.logger.Warnf("%s maker bid disabled: insufficient base balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker bid disabled: insufficient hedge base balance %s", s.Symbol, b.String())
|
||||||
disableMakerBid = true
|
disableMakerBid = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,17 +593,16 @@ func (s *Strategy) updateQuote(ctx context.Context) {
|
||||||
if b.Available.Compare(minAvailable) > 0 {
|
if b.Available.Compare(minAvailable) > 0 {
|
||||||
hedgeQuota.QuoteAsset.Add(b.Available.Sub(minAvailable))
|
hedgeQuota.QuoteAsset.Add(b.Available.Sub(minAvailable))
|
||||||
} else {
|
} else {
|
||||||
s.logger.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker ask disabled: insufficient hedge quote balance %s", s.Symbol, b.String())
|
||||||
disableMakerAsk = true
|
disableMakerAsk = true
|
||||||
}
|
}
|
||||||
} else if b.Available.Compare(s.sourceMarket.MinNotional) > 0 {
|
} else if b.Available.Compare(s.sourceMarket.MinNotional) > 0 {
|
||||||
hedgeQuota.QuoteAsset.Add(b.Available)
|
hedgeQuota.QuoteAsset.Add(b.Available)
|
||||||
} else {
|
} else {
|
||||||
s.logger.Warnf("%s maker ask disabled: insufficient quote balance %s", s.Symbol, b.String())
|
s.logger.Warnf("%s maker ask disabled: insufficient hedge quote balance %s", s.Symbol, b.String())
|
||||||
disableMakerAsk = true
|
disableMakerAsk = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if max exposure position is configured, we should not:
|
// if max exposure position is configured, we should not:
|
||||||
|
@ -1009,6 +1038,10 @@ func (s *Strategy) Defaults() error {
|
||||||
s.MinMarginLevel = fixedpoint.NewFromFloat(3.0)
|
s.MinMarginLevel = fixedpoint.NewFromFloat(3.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.MaxHedgeAccountLeverage.IsZero() {
|
||||||
|
s.MaxHedgeAccountLeverage = fixedpoint.NewFromFloat(1.2)
|
||||||
|
}
|
||||||
|
|
||||||
if s.BidMargin.IsZero() {
|
if s.BidMargin.IsZero() {
|
||||||
if !s.Margin.IsZero() {
|
if !s.Margin.IsZero() {
|
||||||
s.BidMargin = s.Margin
|
s.BidMargin = s.Margin
|
||||||
|
@ -1267,9 +1300,8 @@ func (s *Strategy) CrossRun(
|
||||||
return errors.New("tradesSince time can not be zero")
|
return errors.New("tradesSince time can not be zero")
|
||||||
}
|
}
|
||||||
|
|
||||||
makerMarket, _ := makerSession.Market(s.Symbol)
|
position := types.NewPositionFromMarket(s.makerMarket)
|
||||||
position := types.NewPositionFromMarket(makerMarket)
|
profitStats := types.NewProfitStats(s.makerMarket)
|
||||||
profitStats := types.NewProfitStats(makerMarket)
|
|
||||||
|
|
||||||
fixer := common.NewProfitFixer()
|
fixer := common.NewProfitFixer()
|
||||||
// fixer.ConverterManager = s.ConverterManager
|
// fixer.ConverterManager = s.ConverterManager
|
||||||
|
@ -1284,7 +1316,7 @@ func (s *Strategy) CrossRun(
|
||||||
fixer.AddExchange(sourceSession.Name, ss)
|
fixer.AddExchange(sourceSession.Name, ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err2 := fixer.Fix(ctx, makerMarket.Symbol,
|
if err2 := fixer.Fix(ctx, s.makerMarket.Symbol,
|
||||||
s.ProfitFixerConfig.TradesSince.Time(),
|
s.ProfitFixerConfig.TradesSince.Time(),
|
||||||
time.Now(),
|
time.Now(),
|
||||||
profitStats,
|
profitStats,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user