pkg/core: move convert manager to independent file

This commit is contained in:
edwin 2024-08-23 17:21:36 +08:00
parent bc24e8dcaa
commit 5e3b0238d8
4 changed files with 365 additions and 341 deletions

153
pkg/core/convert_manager.go Normal file
View 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
}

View 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)
}

View File

@ -12,153 +12,6 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
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
}
//go:generate callbackgen -type TradeCollector
type TradeCollector struct {
Symbol string

View File

@ -9,200 +9,6 @@ import (
"github.com/c9s/bbgo/pkg/types"
)
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) {
manager := ConverterManager{
ConverterSettings: []ConverterSetting{
{SymbolConverter: &SymbolConverter{
FromSymbol: "MAXEXCHANGEUSDT",
ToSymbol: "MAXUSDT",
}},
{CurrencyConverter: &CurrencyConverter{
FromCurrency: "MAXEXCHANGE",
ToCurrency: "MAX",
}},
},
}
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)
}
func TestTradeCollector_NilConvertManager(t *testing.T) {
symbol := "BTCUSDT"
position := types.NewPosition(symbol, "BTC", "USDT")