-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #163 from vegaprotocol/141-refactor-liqbot-iterati…
…on-3 141 refactor liqbot iteration 3
- Loading branch information
Showing
52 changed files
with
3,574 additions
and
2,875 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package account | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"code.vegaprotocol.io/liqbot/data" | ||
"code.vegaprotocol.io/liqbot/types" | ||
"code.vegaprotocol.io/liqbot/types/num" | ||
v1 "code.vegaprotocol.io/vega/protos/vega/events/v1" | ||
) | ||
|
||
type accountStream interface { | ||
Init(pubKey string, pauseCh chan types.PauseSignal) | ||
GetBalances(assetID string) (data.BalanceStore, error) | ||
WaitForStakeLinking(pubKey string) error | ||
WaitForTopUpToFinalise(ctx context.Context, evtType v1.BusEventType, walletPubKey, assetID string, amount *num.Uint, timeout time.Duration) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
package account | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
log "github.com/sirupsen/logrus" | ||
|
||
"code.vegaprotocol.io/liqbot/data" | ||
"code.vegaprotocol.io/liqbot/types" | ||
"code.vegaprotocol.io/liqbot/types/num" | ||
) | ||
|
||
type Service struct { | ||
name string | ||
pubKey string | ||
assetID string | ||
stores map[string]data.BalanceStore | ||
accountStream accountStream | ||
coinProvider types.CoinProvider | ||
log *log.Entry | ||
} | ||
|
||
func NewAccountService(name, assetID string, accountStream accountStream, coinProvider types.CoinProvider) *Service { | ||
return &Service{ | ||
name: name, | ||
assetID: assetID, | ||
accountStream: accountStream, | ||
coinProvider: coinProvider, | ||
log: log.WithField("component", "AccountService"), | ||
} | ||
} | ||
|
||
func (a *Service) Init(pubKey string, pauseCh chan types.PauseSignal) { | ||
a.stores = make(map[string]data.BalanceStore) | ||
a.pubKey = pubKey | ||
a.accountStream.Init(pubKey, pauseCh) | ||
} | ||
|
||
func (a *Service) EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error { | ||
store, err := a.getStore(assetID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
balanceTotal := store.Balance().Total() // TODO: should it be total balance? | ||
|
||
a.log.WithFields( | ||
log.Fields{ | ||
"name": a.name, | ||
"partyId": a.pubKey, | ||
"balanceTotal": balanceTotal.String(), | ||
}).Debugf("%s: Total account balance", from) | ||
|
||
if balanceTotal.GTE(targetAmount) { | ||
return nil | ||
} | ||
|
||
a.log.WithFields( | ||
log.Fields{ | ||
"name": a.name, | ||
"partyId": a.pubKey, | ||
"balanceTotal": balanceTotal.String(), | ||
"targetAmount": targetAmount.String(), | ||
}).Debugf("%s: Account balance is less than target amount, depositing...", from) | ||
|
||
evtType, err := a.coinProvider.TopUpAsync(ctx, a.name, a.pubKey, assetID, targetAmount) | ||
if err != nil { | ||
return fmt.Errorf("failed to top up: %w", err) | ||
} | ||
|
||
a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Waiting for top-up...", from) | ||
|
||
if err = a.accountStream.WaitForTopUpToFinalise(ctx, evtType, a.pubKey, assetID, targetAmount, 0); err != nil { | ||
return fmt.Errorf("failed to finalise deposit: %w", err) | ||
} | ||
|
||
a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Top-up complete", from) | ||
|
||
return nil | ||
} | ||
|
||
func (a *Service) EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error { | ||
if receiverPubKey == "" { | ||
return fmt.Errorf("receiver public key is empty") | ||
} | ||
|
||
store, err := a.getStore(assetID) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// TODO: how the hell do we check for stake balance?? | ||
balanceTotal := store.Balance().Total() | ||
|
||
a.log.WithFields( | ||
log.Fields{ | ||
"name": a.name, | ||
"partyId": a.pubKey, | ||
"balanceTotal": balanceTotal.String(), | ||
}).Debugf("%s: Total account stake balance", from) | ||
|
||
if balanceTotal.GT(targetAmount) { | ||
return nil | ||
} | ||
|
||
a.log.WithFields( | ||
log.Fields{ | ||
"name": a.name, | ||
"receiverName": receiverName, | ||
"receiverPubKey": receiverPubKey, | ||
"partyId": a.pubKey, | ||
"balanceTotal": balanceTotal.String(), | ||
"targetAmount": targetAmount.String(), | ||
}).Debugf("%s: Account Stake balance is less than target amount, staking...", from) | ||
|
||
if err = a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, targetAmount); err != nil { | ||
return fmt.Errorf("failed to stake: %w", err) | ||
} | ||
|
||
a.log.WithFields(log.Fields{ | ||
"name": a.name, | ||
"receiverName": receiverName, | ||
"receiverPubKey": receiverPubKey, | ||
"partyId": a.pubKey, | ||
"targetAmount": targetAmount.String(), | ||
}).Debugf("%s: Waiting for staking...", from) | ||
|
||
if err = a.accountStream.WaitForStakeLinking(receiverPubKey); err != nil { | ||
return fmt.Errorf("failed to finalise stake: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (a *Service) StakeAsync(ctx context.Context, receiverPubKey, assetID string, amount *num.Uint) error { | ||
return a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, amount) | ||
} | ||
|
||
func (a *Service) Balance() types.Balance { | ||
store, err := a.getStore(a.assetID) | ||
if err != nil { | ||
a.log.WithError(err).Error("failed to get balance store") | ||
return types.Balance{} | ||
} | ||
return store.Balance() | ||
} | ||
|
||
func (a *Service) getStore(assetID string) (data.BalanceStore, error) { | ||
var err error | ||
|
||
store, ok := a.stores[assetID] | ||
if !ok { | ||
store, err = a.accountStream.GetBalances(assetID) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to initialise balances for '%s': %w", assetID, err) | ||
} | ||
|
||
a.stores[assetID] = store | ||
} | ||
|
||
return store, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,67 @@ | ||
package bot | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
|
||
ppconfig "code.vegaprotocol.io/priceproxy/config" | ||
ppservice "code.vegaprotocol.io/priceproxy/service" | ||
log "github.com/sirupsen/logrus" | ||
|
||
"code.vegaprotocol.io/liqbot/account" | ||
"code.vegaprotocol.io/liqbot/bot/normal" | ||
"code.vegaprotocol.io/liqbot/config" | ||
"code.vegaprotocol.io/liqbot/data" | ||
"code.vegaprotocol.io/liqbot/market" | ||
"code.vegaprotocol.io/liqbot/node" | ||
"code.vegaprotocol.io/liqbot/types" | ||
"code.vegaprotocol.io/liqbot/wallet" | ||
) | ||
|
||
// Bot is the generic bot interface. | ||
// | ||
//go:generate go run github.com/golang/mock/mockgen -destination mocks/bot_mock.go -package mocks code.vegaprotocol.io/liqbot/bot Bot | ||
type Bot interface { | ||
Start() error | ||
Stop() | ||
GetTraderDetails() string | ||
} | ||
|
||
// PricingEngine is the source of price information from the price proxy. | ||
// | ||
//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot PricingEngine | ||
type PricingEngine interface { | ||
GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error) | ||
} | ||
|
||
// New returns a new Bot instance. | ||
func New(botConf config.BotConfig, locations []string, seedConf *config.TokenConfig, pe PricingEngine, wc normal.WalletClient) (Bot, error) { | ||
func New( | ||
botConf config.BotConfig, | ||
conf config.Config, | ||
pricing types.PricingEngine, | ||
whale types.CoinProvider, | ||
) (types.Bot, error) { | ||
switch botConf.Strategy { | ||
case config.BotStrategyNormal: | ||
return normal.New(botConf, locations, seedConf, pe, wc), nil | ||
bot, err := newNormalBot(botConf, conf, pricing, whale) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to create normal bot '%s': %w", botConf.Name, err) | ||
} | ||
return bot, nil | ||
default: | ||
return nil, errors.New("unrecognised bot strategy") | ||
} | ||
} | ||
|
||
func newNormalBot( | ||
botConf config.BotConfig, | ||
conf config.Config, | ||
pricing types.PricingEngine, | ||
whale types.CoinProvider, | ||
) (types.Bot, error) { | ||
dataNode := node.NewDataNode( | ||
conf.Locations, | ||
conf.CallTimeoutMills, | ||
) | ||
|
||
log.Debug("Attempting to connect to Vega gRPC node...") | ||
dataNode.MustDialConnection(context.Background()) // blocking | ||
|
||
botWallet := wallet.NewClient(conf.Wallet.URL) | ||
accountStream := data.NewAccountStream(botConf.Name, dataNode) | ||
accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, accountStream, whale) | ||
|
||
marketStream := data.NewMarketStream(botConf.Name, dataNode) | ||
marketService := market.NewService(botConf.Name, marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID) | ||
|
||
return normal.New( | ||
botConf, | ||
conf.VegaAssetID, | ||
botWallet, | ||
accountService, | ||
marketService, | ||
), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.