Skip to content

Commit 4bec3c9

Browse files
authored
Merge pull request #163 from vegaprotocol/141-refactor-liqbot-iteration-3
141 refactor liqbot iteration 3
2 parents be761ce + b44702f commit 4bec3c9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+3574
-2875
lines changed

.github/workflows/continous-delivery.yml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ jobs:
2727
uses: docker/login-action@v2
2828
if: ${{ !github.event.repository.private }}
2929
with:
30-
# registry: registry.hub.docker.com
3130
username: ${{ secrets.DOCKERHUB_USERNAME }}
3231
password: ${{ secrets.DOCKERHUB_TOKEN }}
3332

@@ -48,13 +47,13 @@ jobs:
4847
tags: ghcr.io/${{ github.repository }}:latest,ghcr.io/${{ github.repository }}:${{ github.sha }},ghcr.io/${{ github.repository }}:${{ github.ref_name }}
4948

5049
# public registry builds
51-
- name: Build and push
52-
uses: docker/build-push-action@v3
53-
if: ${{!github.event.repository.private}}
54-
with:
55-
context: .
56-
push: true
57-
tags: ${{ github.repository }}:latest,${{ github.repository }}:${{ github.sha }},${{ github.repository }}:${{ github.ref_name }}
50+
#- name: Build and push
51+
# uses: docker/build-push-action@v3
52+
# if: ${{!github.event.repository.private}}
53+
# with:
54+
# context: .
55+
# push: true
56+
# tags: ${{ github.repository }}:latest,${{ github.repository }}:${{ github.sha }},${{ github.repository }}:${{ github.ref_name }}
5857

5958

6059
release-binary:
@@ -93,4 +92,3 @@ jobs:
9392
files: build/*.zip
9493
env:
9594
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
96-

.github/workflows/continous-deployment.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ name: "Continous Deployment Workflow"
77
- v*
88

99
jobs:
10-
integration:
11-
uses: ./.github/workflows/continous-integration.yml
10+
#integration:
11+
# uses: ./.github/workflows/continous-integration.yml
1212
delivery:
13-
needs: integration
13+
#needs: integration
1414
uses: ./.github/workflows/continous-delivery.yml
1515
secrets:
1616
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}

.github/workflows/continous-integration.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: "Continous Integration Workflow"
33
"on":
44
workflow_call:
55
pull_request:
6-
6+
77
push:
88
branches:
99

@@ -19,7 +19,7 @@ jobs:
1919
- name: Setup Go
2020
uses: actions/setup-go@v3
2121
with:
22-
go-version: '1.19.0'
22+
go-version: 1.19
2323

2424
- name: build
2525
run: make build
@@ -33,6 +33,5 @@ jobs:
3333
- name: golangci-lint
3434
uses: golangci/golangci-lint-action@v3
3535
with:
36-
version: v1.45
36+
version: v1.49.0
3737
args: --config .golangci.toml
38-

.golangci.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ linters = ["forbidigo"]
8080
path = "cmd/"
8181
linters = ["forbidigo"]
8282

83+
[[issues.exclude-rules]]
84+
path = "./"
85+
linters = ["nosnakecase","exhaustruct"]
86+
8387
[[issues.exclude-rules]]
8488
path = "flags.go"
8589
linters = ["forbidigo"]

account/interfaces.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package account
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"code.vegaprotocol.io/liqbot/data"
8+
"code.vegaprotocol.io/liqbot/types"
9+
"code.vegaprotocol.io/liqbot/types/num"
10+
v1 "code.vegaprotocol.io/vega/protos/vega/events/v1"
11+
)
12+
13+
type accountStream interface {
14+
Init(pubKey string, pauseCh chan types.PauseSignal)
15+
GetBalances(assetID string) (data.BalanceStore, error)
16+
WaitForStakeLinking(pubKey string) error
17+
WaitForTopUpToFinalise(ctx context.Context, evtType v1.BusEventType, walletPubKey, assetID string, amount *num.Uint, timeout time.Duration) error
18+
}

account/service.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package account
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
log "github.com/sirupsen/logrus"
8+
9+
"code.vegaprotocol.io/liqbot/data"
10+
"code.vegaprotocol.io/liqbot/types"
11+
"code.vegaprotocol.io/liqbot/types/num"
12+
)
13+
14+
type Service struct {
15+
name string
16+
pubKey string
17+
assetID string
18+
stores map[string]data.BalanceStore
19+
accountStream accountStream
20+
coinProvider types.CoinProvider
21+
log *log.Entry
22+
}
23+
24+
func NewAccountService(name, assetID string, accountStream accountStream, coinProvider types.CoinProvider) *Service {
25+
return &Service{
26+
name: name,
27+
assetID: assetID,
28+
accountStream: accountStream,
29+
coinProvider: coinProvider,
30+
log: log.WithField("component", "AccountService"),
31+
}
32+
}
33+
34+
func (a *Service) Init(pubKey string, pauseCh chan types.PauseSignal) {
35+
a.stores = make(map[string]data.BalanceStore)
36+
a.pubKey = pubKey
37+
a.accountStream.Init(pubKey, pauseCh)
38+
}
39+
40+
func (a *Service) EnsureBalance(ctx context.Context, assetID string, targetAmount *num.Uint, from string) error {
41+
store, err := a.getStore(assetID)
42+
if err != nil {
43+
return err
44+
}
45+
46+
balanceTotal := store.Balance().Total() // TODO: should it be total balance?
47+
48+
a.log.WithFields(
49+
log.Fields{
50+
"name": a.name,
51+
"partyId": a.pubKey,
52+
"balanceTotal": balanceTotal.String(),
53+
}).Debugf("%s: Total account balance", from)
54+
55+
if balanceTotal.GTE(targetAmount) {
56+
return nil
57+
}
58+
59+
a.log.WithFields(
60+
log.Fields{
61+
"name": a.name,
62+
"partyId": a.pubKey,
63+
"balanceTotal": balanceTotal.String(),
64+
"targetAmount": targetAmount.String(),
65+
}).Debugf("%s: Account balance is less than target amount, depositing...", from)
66+
67+
evtType, err := a.coinProvider.TopUpAsync(ctx, a.name, a.pubKey, assetID, targetAmount)
68+
if err != nil {
69+
return fmt.Errorf("failed to top up: %w", err)
70+
}
71+
72+
a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Waiting for top-up...", from)
73+
74+
if err = a.accountStream.WaitForTopUpToFinalise(ctx, evtType, a.pubKey, assetID, targetAmount, 0); err != nil {
75+
return fmt.Errorf("failed to finalise deposit: %w", err)
76+
}
77+
78+
a.log.WithFields(log.Fields{"name": a.name}).Debugf("%s: Top-up complete", from)
79+
80+
return nil
81+
}
82+
83+
func (a *Service) EnsureStake(ctx context.Context, receiverName, receiverPubKey, assetID string, targetAmount *num.Uint, from string) error {
84+
if receiverPubKey == "" {
85+
return fmt.Errorf("receiver public key is empty")
86+
}
87+
88+
store, err := a.getStore(assetID)
89+
if err != nil {
90+
return err
91+
}
92+
93+
// TODO: how the hell do we check for stake balance??
94+
balanceTotal := store.Balance().Total()
95+
96+
a.log.WithFields(
97+
log.Fields{
98+
"name": a.name,
99+
"partyId": a.pubKey,
100+
"balanceTotal": balanceTotal.String(),
101+
}).Debugf("%s: Total account stake balance", from)
102+
103+
if balanceTotal.GT(targetAmount) {
104+
return nil
105+
}
106+
107+
a.log.WithFields(
108+
log.Fields{
109+
"name": a.name,
110+
"receiverName": receiverName,
111+
"receiverPubKey": receiverPubKey,
112+
"partyId": a.pubKey,
113+
"balanceTotal": balanceTotal.String(),
114+
"targetAmount": targetAmount.String(),
115+
}).Debugf("%s: Account Stake balance is less than target amount, staking...", from)
116+
117+
if err = a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, targetAmount); err != nil {
118+
return fmt.Errorf("failed to stake: %w", err)
119+
}
120+
121+
a.log.WithFields(log.Fields{
122+
"name": a.name,
123+
"receiverName": receiverName,
124+
"receiverPubKey": receiverPubKey,
125+
"partyId": a.pubKey,
126+
"targetAmount": targetAmount.String(),
127+
}).Debugf("%s: Waiting for staking...", from)
128+
129+
if err = a.accountStream.WaitForStakeLinking(receiverPubKey); err != nil {
130+
return fmt.Errorf("failed to finalise stake: %w", err)
131+
}
132+
133+
return nil
134+
}
135+
136+
func (a *Service) StakeAsync(ctx context.Context, receiverPubKey, assetID string, amount *num.Uint) error {
137+
return a.coinProvider.StakeAsync(ctx, receiverPubKey, assetID, amount)
138+
}
139+
140+
func (a *Service) Balance() types.Balance {
141+
store, err := a.getStore(a.assetID)
142+
if err != nil {
143+
a.log.WithError(err).Error("failed to get balance store")
144+
return types.Balance{}
145+
}
146+
return store.Balance()
147+
}
148+
149+
func (a *Service) getStore(assetID string) (data.BalanceStore, error) {
150+
var err error
151+
152+
store, ok := a.stores[assetID]
153+
if !ok {
154+
store, err = a.accountStream.GetBalances(assetID)
155+
if err != nil {
156+
return nil, fmt.Errorf("failed to initialise balances for '%s': %w", assetID, err)
157+
}
158+
159+
a.stores[assetID] = store
160+
}
161+
162+
return store, nil
163+
}

bot/bot.go

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,67 @@
11
package bot
22

33
import (
4+
"context"
45
"errors"
6+
"fmt"
57

6-
ppconfig "code.vegaprotocol.io/priceproxy/config"
7-
ppservice "code.vegaprotocol.io/priceproxy/service"
8+
log "github.com/sirupsen/logrus"
89

10+
"code.vegaprotocol.io/liqbot/account"
911
"code.vegaprotocol.io/liqbot/bot/normal"
1012
"code.vegaprotocol.io/liqbot/config"
13+
"code.vegaprotocol.io/liqbot/data"
14+
"code.vegaprotocol.io/liqbot/market"
15+
"code.vegaprotocol.io/liqbot/node"
16+
"code.vegaprotocol.io/liqbot/types"
17+
"code.vegaprotocol.io/liqbot/wallet"
1118
)
1219

13-
// Bot is the generic bot interface.
14-
//
15-
//go:generate go run github.com/golang/mock/mockgen -destination mocks/bot_mock.go -package mocks code.vegaprotocol.io/liqbot/bot Bot
16-
type Bot interface {
17-
Start() error
18-
Stop()
19-
GetTraderDetails() string
20-
}
21-
22-
// PricingEngine is the source of price information from the price proxy.
23-
//
24-
//go:generate go run github.com/golang/mock/mockgen -destination mocks/pricingengine_mock.go -package mocks code.vegaprotocol.io/liqbot/bot PricingEngine
25-
type PricingEngine interface {
26-
GetPrice(pricecfg ppconfig.PriceConfig) (pi ppservice.PriceResponse, err error)
27-
}
28-
2920
// New returns a new Bot instance.
30-
func New(botConf config.BotConfig, locations []string, seedConf *config.TokenConfig, pe PricingEngine, wc normal.WalletClient) (Bot, error) {
21+
func New(
22+
botConf config.BotConfig,
23+
conf config.Config,
24+
pricing types.PricingEngine,
25+
whale types.CoinProvider,
26+
) (types.Bot, error) {
3127
switch botConf.Strategy {
3228
case config.BotStrategyNormal:
33-
return normal.New(botConf, locations, seedConf, pe, wc), nil
29+
bot, err := newNormalBot(botConf, conf, pricing, whale)
30+
if err != nil {
31+
return nil, fmt.Errorf("failed to create normal bot '%s': %w", botConf.Name, err)
32+
}
33+
return bot, nil
3434
default:
3535
return nil, errors.New("unrecognised bot strategy")
3636
}
3737
}
38+
39+
func newNormalBot(
40+
botConf config.BotConfig,
41+
conf config.Config,
42+
pricing types.PricingEngine,
43+
whale types.CoinProvider,
44+
) (types.Bot, error) {
45+
dataNode := node.NewDataNode(
46+
conf.Locations,
47+
conf.CallTimeoutMills,
48+
)
49+
50+
log.Debug("Attempting to connect to Vega gRPC node...")
51+
dataNode.MustDialConnection(context.Background()) // blocking
52+
53+
botWallet := wallet.NewClient(conf.Wallet.URL)
54+
accountStream := data.NewAccountStream(botConf.Name, dataNode)
55+
accountService := account.NewAccountService(botConf.Name, botConf.SettlementAssetID, accountStream, whale)
56+
57+
marketStream := data.NewMarketStream(botConf.Name, dataNode)
58+
marketService := market.NewService(botConf.Name, marketStream, dataNode, botWallet, pricing, accountService, botConf, conf.VegaAssetID)
59+
60+
return normal.New(
61+
botConf,
62+
conf.VegaAssetID,
63+
botWallet,
64+
accountService,
65+
marketService,
66+
), nil
67+
}

bot/normal/helpers.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func discreteThreeLevelProbabilities(V []float64, muHat float64, sigmaHat float6
9999
// generatePriceUsingDiscreteThreeLevel is a method for calculating price levels
100100
// input is a float price (so divide uint64 price by 10^{num of decimals})
101101
// it returns a float price which you want to multiply by 10^{num of decimals} and then round.
102-
func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac, n float64) (price float64, err error) {
102+
func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac, n float64) (float64, error) {
103103
muHat := -0.5 * sigma * sigma * tgtTimeHorizonYrFrac
104104
sigmaHat := math.Sqrt(n*tgtTimeHorizonYrFrac) * sigma
105105
v := make([]float64, 3)
@@ -117,9 +117,9 @@ func generatePriceUsingDiscreteThreeLevel(m0, delta, sigma, tgtTimeHorizonYrFrac
117117
shockX := v[randomChoice(probabilities)]
118118
y := math.Exp(shockX / n)
119119

120-
price = m0 * y
120+
price := m0 * y
121121

122-
return price, err
122+
return price, nil
123123
}
124124

125125
func randomChoice(probabilities []float64) uint64 {

0 commit comments

Comments
 (0)