quotes

package
v0.0.35 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 14, 2024 License: GPL-3.0 Imports: 31 Imported by: 0

README

Quotes service

The Quotes Service is a centralized system responsible for recording and disseminating real-time trade data from various trading platforms.

Available drivers:

  • Index Price
  • Binance
  • Kraken
  • Opendax
  • Bitfaker
  • Uniswap v3
    • based on Subgraph API
    • based on go-ethereum
  • Syncswap

Interface to connect

type Driver interface {
	Start() error
	Stop() error
	Subscribe(market Market) error
	Unsubscribe(market Market) error
}

Types of price sources

Top Tier CEX Altcoins CEX FIAT DEX
BitFinex Gate UpBit Sushi
OKX MEXC Kraken PancakeSwap
Binance KuCoin Coinbase Uniswap
weight: 20 weight: 5 weight: 15 weight: 50

Last Price

For candle sticks, Recent trade, tickers last price is calculated as follows:

last_price = price

Index Price

Index Price is used mainly in risk management for portfolio evaluation and is aggregated from multiple sources.

Trades from all sources are coming into one queue. IndexAggregator reads trades sequentially, calling calculateIndexPrice() method from a configured strategy.

You can leverage your index price calculation strategy by implementing the priceCalculator interface and passing it as an argument to the IndexAggregator constructor.

type priceCalculator interface {
	calculateIndexPrice(trade TradeEvent) (decimal.Decimal, bool)
}

The default strategy for index price calculation is Volume Weighted Average Price (VWAP), additionally weighted by trade source importance.


sourceMultiplier = (tradeSourceWeight/(activeSourcesWeightsSum(market)))

var totalPriceVolume, totalVolume num
for trade in range(N trades(market)) {
		totalPriceVolume += (Price * Volume * sourceMultiplier)
		totalVolume += (Volume * sourceMultiplier)
}

index_price = totalPriceVolume / totalVolume 

In the VWAP strategy Price cache is used to store a queue containing the last N (default=20) trades for each market.

Drivers may support different markets. That's why the price cache additionally stores active drivers for each market. By default, no drivers are active. When a trade is added, the source of the trade becomes active for a market.

Calculation flow

Drivers weights config example:

driverWeights = [{Binance: 20}, {Uniswap: 10}, {Kraken: 15}]

Trade received:

Trade {
	Source: Binance
	Market: btcusdt
	Volume: 0.5
	Price: 44000
}

The trade is skipped if the trade price or volume is zero.

  1. Calculate sourceMultiplier. Select active drivers for the market. By default, all drivers are not active. When the first trade from a driver is received, a market becomes active. Let's say, we are receiving btcusdt trades only from Binance and Uniswap.
activeSourcesWeightsSum(btcusdt) = {driversWeights[Binance] + driversWeights[Uniswap] = 2 + 1} = 3
tradeSourceWeight = driverWeights[Binance] = 2

sourceMultiplier = (tradeSourceWeight/(activeSourcesWeightsSum(market)))

  1. Add trade data to the price cache.
priceCache.AddTrade(event.Market, event.Price, event.Amount, sourceMultiplier)
  1. Fetch the last n trades from PriceCache.
var totalPriceVolume, totalVolume num
for trade in range(priceCache.trades(btcusdt)) {
		totalPriceVolume += (Price * Volume * sourceMultiplier)
		totalVolume += (Volume * sourceMultiplier)
}
  1. Return Index Price
index_price = totalPriceVolume / totalVolume 

VWA20 Index Price Example Over 20 trades

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Amount 1.0 1.1 2.4 0.2 2.0 3.3 4.0 2.9 1.0 0.1 0.01 0.04 9.0 0.4 4.4 5.0 6.0 0.1 2.0 1.0
Price 40000 42000 41500 44000 43000 40000 41000 42000 43000 42000 45500 41000 41500 42000 44000 46000 47000 46000 44000 42000
VWA20 40000 41047 41288 41404 41880 41260 41185 41325 41418 41422 41424 41423 41448 41457 41808 42377 43024 43031 43074 43051

More examples and test scenarios can be found in index_test.go.

How Uniswap adapter calculates swap price

Method 1: from sqrtPriceX96
Motivation

Uniswap V3 uses Q notation, which is a type of fixed-point arithmetics, to encode swap price.

Q notation allows variables to remain integers, but function similarly to floating point numbers. Additionally piping the price through square root allows to reduce dimentionality of the number. Predictable size of the number encoded this way enables efficient caching and fast retrieval of data from chain.

How to calculate price?

Actually this is a two step process:

  1. Decode price
  2. Convert the price into wei/wei ratio
Step 1

Here's the formula:

sqrtPrice = sqrtPriceX96 / (2^96)
price = sqrtPrice^2
Step 2

ERC20 tokens have built in decimal values. For example, 1 WETH actually represents WETH in the contract whereas USDC is 10^6. USDC has 6 decimals and WETH has 18. So the price calculated on step 1 actually depicts [wei of token0] / [unit token of token1]. Now let's convert that into [wei] / [wei] ratio:

price0 = price / (10^(decimal1-decimal0))
price1 = 1 / price0
Method 2: ticks

This method requires a different set of inputs to calculate.

Motivation

Ticks are related directly to price and enable simpler calculations compared to [[#Method 1]]. This requires just a tick value and a one-shot formula.

How to calculate price?

To convert from tick t, to price, take 1.0001^t to get the corresponding price.

price  = 1.0001^tick
price0 = price / (10^(decimal1-decimal0))
price1 = 1 / price0

It's also possible to convert sqrtPriceX96 to tick:

tick = floor[ log((sqrtPriceX96 / (2^96))^2) / log(1.0001) ]

TODO:

  • add specs or amendments to current interface

Documentation

Overview

Package quotes implements multiple price feed adapters.

Index

Constants

This section is empty.

Variables

View Source
var (
	DriverIndex         = DriverType{"index"}
	DriverBinance       = DriverType{"binance"}
	DriverKraken        = DriverType{"kraken"}
	DriverOpendax       = DriverType{"opendax"}
	DriverBitfaker      = DriverType{"bitfaker"}
	DriverUniswapV3Api  = DriverType{"uniswap_v3_api"}
	DriverUniswapV3Geth = DriverType{"uniswap_v3_geth"}
	DriverSyncswap      = DriverType{"syncswap"}
)
View Source
var (
	TakerTypeUnknown = TakerType{""}
	TakerTypeBuy     = TakerType{"sell"}
	TakerTypeSell    = TakerType{"buy"}
)
View Source
var (
	DefaultWeightsMap = map[DriverType]decimal.Decimal{
		DriverKraken:        decimal.NewFromInt(15),
		DriverBinance:       decimal.NewFromInt(20),
		DriverUniswapV3Api:  decimal.NewFromInt(50),
		DriverUniswapV3Geth: decimal.NewFromInt(50),
		DriverSyncswap:      decimal.NewFromInt(50),
	}
)

Functions

func NewStrategyVWA added in v0.0.26

func NewStrategyVWA(configs ...ConfFuncVWA) priceCalculator

NewStrategyVWA creates a new instance of Volume-Weighted Average Price index price calculator.

Types

type BinanceConfig added in v0.0.23

type BinanceConfig struct {
	Filter FilterConfig `yaml:"filter" env-prefix:"FILTER_"`
}

func (BinanceConfig) DriverType added in v0.0.23

func (BinanceConfig) DriverType() DriverType

type BitfakerConfig added in v0.0.23

type BitfakerConfig struct {
	Period time.Duration `yaml:"period" env:"PERIOD" env-default:"5s"`
	Filter FilterConfig  `yaml:"filter" env-prefix:"FILTER_"`
}

func (BitfakerConfig) DriverType added in v0.0.23

func (BitfakerConfig) DriverType() DriverType

type ConfFuncVWA added in v0.0.26

type ConfFuncVWA func(*strategyVWA)

func WithCustomPriceCacheVWA added in v0.0.26

func WithCustomPriceCacheVWA(priceCache *PriceCacheVWA) ConfFuncVWA

WithCustomPriceCacheVWA configures price cache. Should be passed as an argument to the NewStrategyVWA() constructor.

func WithCustomWeightsVWA added in v0.0.26

func WithCustomWeightsVWA(driversWeights map[DriverType]decimal.Decimal) ConfFuncVWA

WithCustomWeightsVWA configures custom drivers weights. Should be passed as an argument to the NewStrategyVWA() constructor.

type Config

type Config struct {
	Driver DriverType `yaml:"driver" env:"QUOTES_DRIVER" env-default:"index"`

	Binance       BinanceConfig       `yaml:"binance" env-prefix:"QUOTES_BINANCE_"`
	Kraken        KrakenConfig        `yaml:"kraken" env-prefix:"QUOTES_KRAKEN_"`
	Opendax       OpendaxConfig       `yaml:"opendax" env-prefix:"QUOTES_OPENDAX_"`
	Bitfaker      BitfakerConfig      `yaml:"bitfaker" env-prefix:"QUOTES_BITFAKER_"`
	UniswapV3Api  UniswapV3ApiConfig  `yaml:"uniswap_v3_api" env-prefix:"QUOTES_UNISWAP_V3_API_"`
	UniswapV3Geth UniswapV3GethConfig `yaml:"uniswap_v3_geth" env-prefix:"QUOTES_UNISWAP_V3_GETH_"`
	Syncswap      SyncswapConfig      `yaml:"syncswap" env-prefix:"QUOTES_SYNCSWAP_"`
	Index         IndexConfig         `yaml:"index" env-prefix:"QUOTES_INDEX_"`
}

func NewConfigFromEnv added in v0.0.22

func NewConfigFromEnv() (Config, error)

func NewConfigFromFile added in v0.0.24

func NewConfigFromFile(path string) (Config, error)

func ToConfig added in v0.0.26

func ToConfig(driver DriverConfig) Config

type DisabledFilter added in v0.0.30

type DisabledFilter struct{}

func (DisabledFilter) Allow added in v0.0.30

func (f DisabledFilter) Allow(trade TradeEvent) bool

type Driver

type Driver interface {
	Name() DriverType
	Start() error
	Stop() error
	Subscribe(market Market) error
	Unsubscribe(market Market) error
}

func NewDriver

func NewDriver(config Config, outbox chan<- TradeEvent) (Driver, error)

func NewIndexAggregator added in v0.0.26

func NewIndexAggregator(driversConfigs []Config, strategy priceCalculator, outbox chan<- TradeEvent) Driver

NewIndexAggregator creates a new instance of IndexAggregator.

type DriverConfig added in v0.0.26

type DriverConfig interface {
	DriverType() DriverType
}

type DriverType

type DriverType struct {
	// contains filtered or unexported fields
}

DriverType is enum that represents all available quotes providers.

func ToDriverType

func ToDriverType(raw string) (DriverType, error)

func (DriverType) MarshalJSON

func (t DriverType) MarshalJSON() ([]byte, error)

func (DriverType) MarshalYAML

func (t DriverType) MarshalYAML() (any, error)

func (DriverType) String

func (d DriverType) String() string

func (*DriverType) UnmarshalJSON

func (t *DriverType) UnmarshalJSON(raw []byte) error

func (*DriverType) UnmarshalYAML

func (t *DriverType) UnmarshalYAML(value *yaml.Node) error

type Filter added in v0.0.30

type Filter interface {
	Allow(trade TradeEvent) bool
}

func FilterFactory added in v0.0.30

func FilterFactory(conf FilterConfig) Filter

type FilterConfig added in v0.0.30

type FilterConfig struct {
	FilterType FilterType `yaml:"filter_type" env:"TYPE" env-default:"disabled"`

	SamplerFilter   SamplerFilterConfig   `yaml:"sampler" env-prefix:"SAMPLER_"`
	PriceDiffFilter PriceDiffFilterConfig `yaml:"price_diff" env-prefix:"PRICE_DIFF_"`
}

type FilterType added in v0.0.30

type FilterType string
const (
	SamplerFilterType   FilterType = "sampler"
	PriceDiffFilterType FilterType = "price_diff"
	DisabledFilterType  FilterType = "disabled"
)

type IndexConfig added in v0.0.26

type IndexConfig struct {
	TradesCached  int      `yaml:"trades_cached" env:"TRADES_CACHED" env-default:"20"`
	DriverConfigs []Config `yaml:"drivers" env:"DRIVERS"`
}

func (IndexConfig) DriverType added in v0.0.26

func (IndexConfig) DriverType() DriverType

type KrakenConfig added in v0.0.23

type KrakenConfig struct {
	URL             string        `yaml:"url" env:"URL" env-default:"wss://ws.kraken.com"`
	ReconnectPeriod time.Duration `yaml:"period" env:"RECONNECT_PERIOD" env-default:"5s"`
	Filter          FilterConfig  `yaml:"filter" env-prefix:"FILTER_"`
}

func (KrakenConfig) DriverType added in v0.0.23

func (KrakenConfig) DriverType() DriverType

type Market

type Market struct {
	// contains filtered or unexported fields
}

func NewMarket added in v0.0.26

func NewMarket(base, quote string) Market

func NewMarketFromString added in v0.0.26

func NewMarketFromString(s string) (Market, bool)

NewMarketFromString returns a new Market from a string "btc/usdt" -> Market{btc, usdt} NOTE: string should contain "/" delimiter

func (Market) Base added in v0.0.26

func (m Market) Base() string

func (Market) IsEmpty added in v0.0.26

func (m Market) IsEmpty() bool

func (Market) Quote added in v0.0.26

func (m Market) Quote() string

func (Market) String added in v0.0.26

func (m Market) String() string

String returns a string representation of the market Market{btc, usdt} -> "btc/usdt"

type OpendaxConfig added in v0.0.23

type OpendaxConfig struct {
	URL             string        `yaml:"url" env:"URL" env-default:"wss://alpha.yellow.org/api/v1/finex/ws"`
	ReconnectPeriod time.Duration `yaml:"period" env:"RECONNECT_PERIOD" env-default:"5s"`
	Filter          FilterConfig  `yaml:"filter" env-prefix:"FILTER_"`
}

func (OpendaxConfig) DriverType added in v0.0.23

func (OpendaxConfig) DriverType() DriverType

type PriceCacheVWA added in v0.0.26

type PriceCacheVWA struct {
	// contains filtered or unexported fields
}

func NewPriceCacheVWA added in v0.0.26

func NewPriceCacheVWA(driversWeights map[DriverType]decimal.Decimal, nTrades int) *PriceCacheVWA

NewPriceCacheVWA initializes a new cache to store last n trades for each market.

func (*PriceCacheVWA) ActivateDriver added in v0.0.26

func (p *PriceCacheVWA) ActivateDriver(driver DriverType, market Market)

ActivateDriver makes the driver active for the market.

func (*PriceCacheVWA) ActiveWeights added in v0.0.26

func (p *PriceCacheVWA) ActiveWeights(market Market) decimal.Decimal

ActiveWeights returns the sum of active driver weights for the market. TODO: cache the weights inside the marketHistory

func (*PriceCacheVWA) AddTrade added in v0.0.26

func (p *PriceCacheVWA) AddTrade(market Market, price, volume, weight decimal.Decimal)

AddTrade adds a new trade to the cache for a market.

func (*PriceCacheVWA) GetVWA added in v0.0.26

func (p *PriceCacheVWA) GetVWA(market Market) (decimal.Decimal, bool)

GetVWA calculates the VWA based on a list of trades.

type PriceDiffFilter added in v0.0.30

type PriceDiffFilter struct {
	// contains filtered or unexported fields
}

func (*PriceDiffFilter) Allow added in v0.0.30

func (f *PriceDiffFilter) Allow(trade TradeEvent) bool

type PriceDiffFilterConfig added in v0.0.30

type PriceDiffFilterConfig struct {
	Threshold string `yaml:"threshold" env:"THRESHOLD" env-default:"0.05"`
}

type SamplerFilterConfig added in v0.0.30

type SamplerFilterConfig struct {
	Percentage int64 `yaml:"percentage" env:"PERCENTAGE" env-default:"5"`
}

type SyncswapConfig added in v0.0.23

type SyncswapConfig struct {
	URL                       string       `yaml:"url" env:"URL" env-default:""`
	AssetsURL                 string       `` /* 136-byte string literal not displayed */
	ClassicPoolFactoryAddress string       `` /* 127-byte string literal not displayed */
	Filter                    FilterConfig `yaml:"filter" env-prefix:"FILTER_"`
}

func (SyncswapConfig) DriverType added in v0.0.23

func (SyncswapConfig) DriverType() DriverType

type TakerType

type TakerType struct {
	// contains filtered or unexported fields
}

TakerType is enum that represents the side of taker in a trade.

func ToTakerType

func ToTakerType(raw string) (TakerType, error)

func (TakerType) MarshalJSON

func (t TakerType) MarshalJSON() ([]byte, error)

func (TakerType) MarshalYAML

func (t TakerType) MarshalYAML() (any, error)

func (TakerType) String

func (t TakerType) String() string

func (*TakerType) UnmarshalJSON

func (t *TakerType) UnmarshalJSON(raw []byte) error

func (*TakerType) UnmarshalYAML

func (t *TakerType) UnmarshalYAML(value *yaml.Node) error

type TradeEvent

type TradeEvent struct {
	Source    DriverType
	Market    Market // e.g. `btc/usdt`
	Price     decimal.Decimal
	Amount    decimal.Decimal
	Total     decimal.Decimal
	TakerType TakerType
	CreatedAt time.Time
}

TradeEvent is a generic container for trades received from providers.

type UniswapV3ApiConfig added in v0.0.23

type UniswapV3ApiConfig struct {
	URL        string        `yaml:"url" env:"URL" env-default:"https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"`
	WindowSize time.Duration `yaml:"window_size" env:"WINDOW_SIZE" env-default:"2s"`
	Filter     FilterConfig  `yaml:"filter" env-prefix:"FILTER_"`
}

func (UniswapV3ApiConfig) DriverType added in v0.0.23

func (UniswapV3ApiConfig) DriverType() DriverType

type UniswapV3GethConfig added in v0.0.23

type UniswapV3GethConfig struct {
	URL            string       `yaml:"url" env:"GETH_URL" env-default:""`
	AssetsURL      string       `` /* 136-byte string literal not displayed */
	FactoryAddress string       `yaml:"factory_address" env:"FACTORY_ADDRESS" env-default:"0x1F98431c8aD98523631AE4a59f267346ea31F984"`
	Filter         FilterConfig `yaml:"filter" env-prefix:"FILTER_"`
}

func (UniswapV3GethConfig) DriverType added in v0.0.23

func (UniswapV3GethConfig) DriverType() DriverType

Directories

Path Synopsis
Types and helpers for easy formatting and parsing open-finance protocol messages.
Types and helpers for easy formatting and parsing open-finance protocol messages.
App for testing quotes drivers.
App for testing quotes drivers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL