Skip to content

Commit

Permalink
store calculated VWAP for each tokens with 10min interval
Browse files Browse the repository at this point in the history
  • Loading branch information
notJoon committed May 9, 2024
1 parent 130ca6f commit 4edd5bd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 19 deletions.
46 changes: 46 additions & 0 deletions store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package vwap

// VWAPData represents the specific token's VWAP data.
type VWAPData struct {
TokenName string `json:"token_name"` // VWAP data belongs token name
VWAP float64 `json:"vwap"` // calculated VWAP value
Timestamp int `json:"timestamp"` // timestamp of the VWAP value (UNIX, 10 min interval)
}

var vwapDataMap map[string][]VWAPData

func init() {
lastPrices = make(map[string]float64)
vwapDataMap = make(map[string][]VWAPData)
}

// store stores the VWAP data for the token or updates the existing data.
//
// Parameters:
//
// - tokenName: the token name
// - vwap: the VWAP value to store
// - timestamp: the timestamp of the VWAP value
func store(tokenName string, vwap float64, timestamp int) {
// adjust the timestamp to the 10 minutes interval.
adjustedTimestamp := timestamp - (timestamp % 600)

// get the VWAP data for the token
lst, ok := vwapDataMap[tokenName]
if !ok {
lst = []VWAPData{}
}

// check last VWAP data for the list
if len(lst) > 0 {
last := lst[len(lst)-1]
if last.Timestamp == adjustedTimestamp {
last.VWAP = vwap
vwapDataMap[tokenName] = lst
return
}
}

lst = append(lst, VWAPData{tokenName, vwap, adjustedTimestamp})
vwapDataMap[tokenName] = lst
}
40 changes: 40 additions & 0 deletions store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package vwap

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestVWAPStorage(t *testing.T) {
vwapDataMap = make(map[string][]VWAPData)
lastPrices = make(map[string]float64)

trades := generateMockTrades()

for _, trade := range trades {
VWAP([]TradeData{trade})
}

expectedVWAPData := map[string][]VWAPData{
"gno.land/r/demo/foo": {
{TokenName: "gno.land/r/demo/foo", VWAP: 1.2, Timestamp: 1623200400},
{TokenName: "gno.land/r/demo/foo", VWAP: 1.5, Timestamp: 1623201000},
},
"gno.land/r/demo/bar": {
{TokenName: "gno.land/r/demo/bar", VWAP: 2.1, Timestamp: 1623200400},
{TokenName: "gno.land/r/demo/bar", VWAP: 2.3, Timestamp: 1623201000},
},
}

assert.Equal(t, expectedVWAPData, vwapDataMap)
}

func generateMockTrades() []TradeData {
return []TradeData{
{TokenName: "gno.land/r/demo/foo", Volume: 100, Ratio: 1.2, Timestamp: 1623200400},
{TokenName: "gno.land/r/demo/bar", Volume: 200, Ratio: 2.1, Timestamp: 1623200400},
{TokenName: "gno.land/r/demo/foo", Volume: 150, Ratio: 1.5, Timestamp: 1623201000},
{TokenName: "gno.land/r/demo/bar", Volume: 250, Ratio: 2.3, Timestamp: 1623201000},
}
}
11 changes: 7 additions & 4 deletions vwap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const (
// TradeData represents the data for a single trade.
type TradeData struct {
TokenName string
Quantity float64
Volume float64
Ratio float64
Timestamp int
}
Expand All @@ -35,8 +35,8 @@ func VWAP(trades []TradeData) float64 {
var numerator, denominator float64

for _, trade := range trades {
numerator += trade.Quantity * trade.Ratio
denominator += trade.Quantity
numerator += trade.Volume * trade.Ratio
denominator += trade.Volume
}

// return last price if there is no trade
Expand All @@ -47,6 +47,8 @@ func VWAP(trades []TradeData) float64 {
vwap := numerator / denominator
lastPrices[trades[0].TokenName] = vwap // save the last price

store(trades[0].TokenName, vwap, trades[0].Timestamp)

return vwap
}

Expand All @@ -73,11 +75,12 @@ func updateTrades(jsonStr string) ([]TradeData, error) {

// TODO; remove testing logic
// testing purpose
// TODO: Get volume data by using API
tokenName := TokenIdentifier(r.Token)
if contains(tradableTokens, tokenName) {
trade := TradeData{
TokenName: string(tokenName),
Quantity: 100,
Volume: 100,
Ratio: floatRatio,
Timestamp: data.Stat.Timestamp,
}
Expand Down
34 changes: 19 additions & 15 deletions vwap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import (
)

func TestVWAPWithNoTrades(t *testing.T) {
t.Parallel()

trades := []TradeData{
{TokenName: "Token1", Quantity: 0, Ratio: 0, Timestamp: 1621000000},
{TokenName: "Token1", Volume: 0, Ratio: 0, Timestamp: 1621000000},
}

lastPrices = map[string]float64{
Expand All @@ -24,11 +26,11 @@ func TestVWAPWithNoTrades(t *testing.T) {
func TestVWAPWith10MinuteInterval(t *testing.T) {
// Mock trade data with different timestamps
trades := []TradeData{
{TokenName: "Token1", Quantity: 100, Ratio: 1.5, Timestamp: 1621000000},
{TokenName: "Token1", Quantity: 200, Ratio: 1.8, Timestamp: 1621000180},
{TokenName: "Token1", Quantity: 150, Ratio: 1.6, Timestamp: 1621000420},
{TokenName: "Token1", Quantity: 300, Ratio: 1.7, Timestamp: 1621000600},
{TokenName: "Token1", Quantity: 250, Ratio: 1.9, Timestamp: 1621000900},
{TokenName: "Token1", Volume: 100, Ratio: 1.5, Timestamp: 1621000000},
{TokenName: "Token1", Volume: 200, Ratio: 1.8, Timestamp: 1621000180},
{TokenName: "Token1", Volume: 150, Ratio: 1.6, Timestamp: 1621000420},
{TokenName: "Token1", Volume: 300, Ratio: 1.7, Timestamp: 1621000600},
{TokenName: "Token1", Volume: 250, Ratio: 1.9, Timestamp: 1621000900},
}

// Calculate VWAP for each 10-minute interval
Expand Down Expand Up @@ -72,8 +74,8 @@ func calculateExpectedVWAP(trades []TradeData) float64 {
var numerator, denominator float64

for _, trade := range trades {
numerator += trade.Quantity * trade.Ratio
denominator += trade.Quantity
numerator += trade.Volume * trade.Ratio
denominator += trade.Volume
}

if denominator == 0 {
Expand All @@ -84,6 +86,8 @@ func calculateExpectedVWAP(trades []TradeData) float64 {
}

func TestUpdateTrades(t *testing.T) {
t.Parallel()

// Mock the RPC response
mockResponse := `{
"stat": {
Expand Down Expand Up @@ -126,17 +130,17 @@ func TestUpdateTrades(t *testing.T) {
assert.Len(t, trades, 6, "Incorrect number of trades returned")

expectedTrades := []TradeData{
{TokenName: string(wugnot), Quantity: 100, Ratio: 1, Timestamp: expectedTimestamp},
{TokenName: string(foo), Quantity: 100, Ratio: 1.5281713042, Timestamp: expectedTimestamp},
{TokenName: string(qux), Quantity: 100, Ratio: 0.7640717751, Timestamp: expectedTimestamp},
{TokenName: string(gns), Quantity: 100, Ratio: 2.980939105, Timestamp: expectedTimestamp},
{TokenName: string(bar), Quantity: 100, Ratio: 0, Timestamp: expectedTimestamp},
{TokenName: string(baz), Quantity: 100, Ratio: 0, Timestamp: expectedTimestamp},
{TokenName: string(wugnot), Volume: 100, Ratio: 1, Timestamp: expectedTimestamp},
{TokenName: string(foo), Volume: 100, Ratio: 1.5281713042, Timestamp: expectedTimestamp},
{TokenName: string(qux), Volume: 100, Ratio: 0.7640717751, Timestamp: expectedTimestamp},
{TokenName: string(gns), Volume: 100, Ratio: 2.980939105, Timestamp: expectedTimestamp},
{TokenName: string(bar), Volume: 100, Ratio: 0, Timestamp: expectedTimestamp},
{TokenName: string(baz), Volume: 100, Ratio: 0, Timestamp: expectedTimestamp},
}

for i, trade := range trades {
assert.Equal(t, expectedTrades[i].TokenName, trade.TokenName, "Incorrect token name")
assert.Equal(t, expectedTrades[i].Quantity, trade.Quantity, "Incorrect quantity")
assert.Equal(t, expectedTrades[i].Volume, trade.Volume, "Incorrect quantity")
assert.InDelta(t, expectedTrades[i].Ratio, trade.Ratio, 1e-9, "Incorrect ratio")
assert.Equal(t, expectedTrades[i].Timestamp, trade.Timestamp, "Incorrect timestamp")
}
Expand Down

0 comments on commit 4edd5bd

Please sign in to comment.