Skip to content

Commit

Permalink
Expand skeleton app (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleyvega committed Feb 16, 2021
1 parent b314c28 commit bb97231
Show file tree
Hide file tree
Showing 26 changed files with 2,787 additions and 19 deletions.
46 changes: 46 additions & 0 deletions bot/bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package bot

import (
"code.vegaprotocol.io/liqbot/config"

ppconfig "code.vegaprotocol.io/priceproxy/config"
ppservice "code.vegaprotocol.io/priceproxy/service"
tcwallet "code.vegaprotocol.io/vega/wallet"
)

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

// LiqBot represents one liquidity bot.
type LiqBot struct {
config config.BotConfig
pricingEngine PricingEngine
walletServer tcwallet.WalletHandler
}

// New returns a new instance of LiqBot.
func New(config config.BotConfig, pe PricingEngine, ws tcwallet.WalletHandler) *LiqBot {
lb := LiqBot{
config: config,
pricingEngine: pe,
walletServer: ws,
}
return &lb
}

// Start starts the liquidity bot goroutine(s).
func (lb *LiqBot) Start() {
go lb.run()
}

// Stop stops the liquidity bot goroutine(s).
func (lb *LiqBot) Stop() {
// TBD
}

func (lb *LiqBot) run() {
// TBD
}
50 changes: 50 additions & 0 deletions bot/mocks/pricingengine_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions cmd/liqbot/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Command liqbot runs one application instance which may contain many bots that
// provide liquidity. Each bot connects to one Vega node and submits orders to
// one market.
//
// $ make install
// $ $GOPATH/bin/liqbot -config=config.yml
package main
37 changes: 28 additions & 9 deletions cmd/liqbot/main.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
// Command liqbot runs one application instance which may contain many bots that
// provide liquidity. Each bot connects to one Vega node and submits orders to
// one market.
//
// $ make install
// $ $GOPATH/bin/liqbot -config=config.yml
package main

import (
"flag"
"fmt"
"math/rand"
"os"
"os/signal"
"strings"
"syscall"
"time"

"code.vegaprotocol.io/liqbot/config"
"code.vegaprotocol.io/liqbot/service"

"github.com/jinzhu/configor"
log "github.com/sirupsen/logrus"
Expand All @@ -28,8 +26,6 @@ var (
)

func main() {
rand.Seed(time.Now().UnixNano())

var configName string
var configVersion bool
flag.StringVar(&configName, "config", "", "Configuration YAML file")
Expand All @@ -41,6 +37,8 @@ func main() {
return
}

rand.Seed(time.Now().UnixNano())

var cfg config.Config
err := configor.Load(&cfg, configName)
// https://github.com/jinzhu/configor/issues/40
Expand Down Expand Up @@ -68,5 +66,26 @@ func main() {
"hash": VersionHash,
}).Info("Version")

// TODO: Instantiate service
var s *service.Service
s, err = service.NewService(cfg, nil, nil)
if err != nil {
log.WithFields(log.Fields{
"error": err.Error(),
}).Fatal("Failed to create service")
}

go func() {
err := s.Start()
if err != nil && err.Error() != "http: Server closed" {
log.WithFields(log.Fields{
"listen": cfg.Server.Listen,
"error": err.Error(),
}).Fatal("Could not listen")
}
}()
c := make(chan os.Signal, 2)
signal.Notify(c, syscall.SIGINT)
signal.Notify(c, syscall.SIGTERM)
<-c
s.Stop()
}
50 changes: 42 additions & 8 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,36 @@ import (
"net/url"
"time"

"code.vegaprotocol.io/liqbot/core"

log "github.com/sirupsen/logrus"
)

// ServerConfig describes the settings for running the price proxy.
// ServerConfig describes the settings for running the liquidity bot.
type ServerConfig struct {
Env string
Listen string
LogFormat string
LogLevel string
}

// NodeConfig describes the settings for contacting Vega nodes.
type NodeConfig struct {
Name string
Address *url.URL
}

// PricingConfig describes the settings for contacting the price proxy.
type PricingConfig struct {
Address *url.URL `yaml:"address"`
}

// BotConfig specifies the configuration parameters for one bot, which talks to one market on one
// Vega node.
type BotConfig struct {
// WalletID is the name of the bot. It is *not* a public key seen by Vega.
WalletID string `yaml:"marketID"`
// Name is the name of the bot. It is also used as the wallet name.
// It is *not* a public key seen by Vega.
Name string `yaml:"name"`

// NodeAddress specifies the URL of the Vega node to connect to.
// e.g. REST node:
Expand All @@ -36,19 +50,30 @@ type BotConfig struct {
// scheme: grpc
// host: node.example.com:1234
NodeAddress *url.URL `yaml:"nodeAddress"`
// node core.Node
node core.Node

// Strategy specifies which algorithm the bot is to use.
Strategy string
Strategy string `yaml:"strategy"`

// StrategyDetails contains the parameters needed by the strategy algorithm
StrategyDetails map[string]string `yaml:"strategyDetails"`
}

// ConfigDetails contains the parameters needed by the strategy algorithm
ConfigDetails map[string]string
// WalletConfig describes the settings for running an internal wallet server
type WalletConfig struct {
RootPath string `yaml:"rootPath"`
TokenExpiry int `yaml:"tokenExpiry"`
}

// Config describes the top level config file format.
type Config struct {
Server *ServerConfig `yaml:"server"`
Bots []BotConfig `yaml:"bots"`

Nodes []NodeConfig `yaml:"nodes"`
Pricing *PricingConfig `yaml:"pricing"`
Wallet *WalletConfig `yaml:"wallet"`

Bots []BotConfig `yaml:"bots"`
}

var (
Expand All @@ -71,6 +96,15 @@ func CheckConfig(cfg *Config) error {
if cfg.Server == nil {
return fmt.Errorf("%s: %s", ErrMissingEmptyConfigSection.Error(), "server")
}
if cfg.Nodes == nil || len(cfg.Nodes) == 0 {
return fmt.Errorf("%s: %s", ErrMissingEmptyConfigSection.Error(), "nodes")
}
if cfg.Pricing == nil {
return fmt.Errorf("%s: %s", ErrMissingEmptyConfigSection.Error(), "pricing")
}
if cfg.Wallet == nil {
return fmt.Errorf("%s: %s", ErrMissingEmptyConfigSection.Error(), "wallet")
}
if cfg.Bots == nil || len(cfg.Bots) == 0 {
return fmt.Errorf("%s: %s", ErrMissingEmptyConfigSection.Error(), "bots")
}
Expand Down
16 changes: 16 additions & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@ func TestCheckConfig(t *testing.T) {
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))

cfg.Nodes = []config.NodeConfig{}
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))

cfg.Nodes = append(cfg.Nodes, config.NodeConfig{})
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))

cfg.Pricing = &config.PricingConfig{}
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))

cfg.Wallet = &config.WalletConfig{}
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))

cfg.Bots = []config.BotConfig{}
err = config.CheckConfig(&cfg)
assert.True(t, strings.HasPrefix(err.Error(), config.ErrMissingEmptyConfigSection.Error()))
Expand Down
2 changes: 2 additions & 0 deletions config/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Package config contains structures and functions for configuring the app.
package config
71 changes: 71 additions & 0 deletions core/currencypair.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package core

import (
"errors"
"fmt"
"sort"
"strings"

ppconfig "code.vegaprotocol.io/priceproxy/config"
"code.vegaprotocol.io/vega/proto"
)

var currencyPairBaseTags = []string{
"base:",
"ticker:",
}

var currencyPairQuoteTags = []string{
"quote:",
}

var currencyPairCountryTags = []string{
"country:",
}

var countryToCurrency = map[string]string{
"US": "USD",
}

// MarketToPriceConfig converts a Market object to a PriceConfig object by looking in the Market's instrument metadata tag list.
func MarketToPriceConfig(mkt *proto.Market) (*ppconfig.PriceConfig, error) {
if mkt == nil || mkt.TradableInstrument == nil || mkt.TradableInstrument.Instrument == nil ||
mkt.TradableInstrument.Instrument.Metadata == nil {
return nil, errors.New("failed to create PriceConfig from Market (nil)")
}
if len(mkt.TradableInstrument.Instrument.Metadata.Tags) == 0 {
return nil, errors.New("failed to create PriceConfig from Market (zero Tags)")
}
base := "?"
quote := "?"
sort.Strings(mkt.TradableInstrument.Instrument.Metadata.Tags)
for _, tag := range mkt.TradableInstrument.Instrument.Metadata.Tags {
for _, pfx := range currencyPairBaseTags {
if strings.HasPrefix(tag, pfx) {
base = tag[len(pfx):]
}
}
for _, pfx := range currencyPairQuoteTags {
if strings.HasPrefix(tag, pfx) {
quote = tag[len(pfx):]
}
}
for _, pfx := range currencyPairCountryTags {
if strings.HasPrefix(tag, pfx) {
var found bool
quote, found = countryToCurrency[tag[len(pfx):]]
if !found {
return nil, fmt.Errorf("can't map country to currency: %s", tag[len(pfx):])
}
}
}
}
if base == "?" {
return nil, errors.New("failed to create PriceConfig from Market (missing Base)")
}
if quote == "?" {
return nil, errors.New("failed to create PriceConfig from Market (missing Quote)")
}

return &ppconfig.PriceConfig{Base: base, Quote: quote}, nil
}
Loading

0 comments on commit bb97231

Please sign in to comment.