Skip to content

Commit

Permalink
Hotfix/v0.9.3 v2 best price in querier response (#40)
Browse files Browse the repository at this point in the history
* Move Best price field to response

Adjust unit test cases to look for best price in querier response.

* Remove commented code

* Update queries API

Co-authored-by: Martin Dyring-Andersen <martin@dyring-andersen.dk>
  • Loading branch information
blewater and mdyring authored Dec 12, 2020
1 parent ed883a2 commit 12b0302
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 20 deletions.
6 changes: 0 additions & 6 deletions x/market/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,12 +552,6 @@ func (k Keeper) GetAllInstruments(ctx sdk.Context) []*types.MarketData {
Destination: destination,
}

// fill in best price
bestPlan := k.createExecutionPlan(ctx, destination, source)
if !bestPlan.DestinationCapacity().IsZero() {
instrLst[idx].BestPrice = &bestPlan.Price
}

// fill in last order price, timestamp
md := k.GetInstrument(ctx, source, destination)
if md != nil && md.LastPrice != nil {
Expand Down
7 changes: 0 additions & 7 deletions x/market/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -448,20 +448,13 @@ func TestAllInstruments(t *testing.T) {
// 30 because of chf, eur, gbp, jpy, ngm, usd
require.Len(t, allInstruments, 30)

bestPricedCnt := 0
transactedInstruments := "chfusd"
for _, i := range allInstruments {
if (i.Source == "eur" || i.Destination == "eur") &&
(strings.Contains(transactedInstruments, i.Source) || strings.Contains(transactedInstruments, i.Destination)) {
require.NotNil(t, i.LastPrice)
if i.BestPrice != nil {
require.False(t, i.BestPrice.IsZero())
}
}
// No unfulfilled orders
require.Nil(t, i.BestPrice)
}
require.Zero(t, bestPricedCnt, "No unfulfilled orders")

// Sorting assertions by source+destination
// instruments in supply: chf, eur, gbp, jpy, ngm, usd
Expand Down
16 changes: 15 additions & 1 deletion x/market/keeper/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,20 @@ func (q QueryInstrumentsResponse) String() string {
return fmt.Sprintf("%v => %v", q.Source, q.Destination)
}

// getBestPrice returns the best priced passive order for source and
// destination instruments. Returns nil when executePlan cannot find a best
// plan.
func getBestPrice(ctx sdk.Context, k *Keeper, source, destination string) *sdk.Dec {
var bestPrice *sdk.Dec

bestPlan := k.createExecutionPlan(ctx, destination, source)
if !bestPlan.DestinationCapacity().IsZero() {
bestPrice = &bestPlan.Price
}

return bestPrice
}

func queryInstruments(ctx sdk.Context, k *Keeper) ([]byte, error) {
instruments := k.GetAllInstruments(ctx)

Expand All @@ -199,7 +213,7 @@ func queryInstruments(ctx sdk.Context, k *Keeper) ([]byte, error) {
Source: v.Source,
Destination: v.Destination,
LastPrice: v.LastPrice,
BestPrice: v.BestPrice,
BestPrice: getBestPrice(ctx, k, v.Source, v.Destination),
LastTraded: v.Timestamp,
}
}
Expand Down
83 changes: 82 additions & 1 deletion x/market/keeper/querier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,40 @@
package keeper

import (
"strings"
"testing"
"time"

json2 "encoding/json"

"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tidwall/gjson"
)

func TestQryGetAllInstruments(t *testing.T) {
func TestQryGetAllInstrumentsWithNonZeroBestPrices(t *testing.T) {
ctx, k, ak, _, _ := createTestComponents(t)

acc1 := createAccount(ctx, ak, "acc1", "5000eur,2500chf,400ngm")
acc2 := createAccount(ctx, ak, "acc2", "1000usd")

// generate passive order
o := order(acc1, "100eur", "120usd")
_, err := k.NewOrderSingle(ctx, o)
require.NoError(t, err)

// generate passive order
o = order(acc1, "72eur", "1213jpy")
_, err = k.NewOrderSingle(ctx, o)
require.NoError(t, err)

// generate passive order of half balances
o = order(acc1, "72chf", "312usd")
_, err = k.NewOrderSingle(ctx, o)
require.NoError(t, err)

// Execute an order
// fulfilled
o = order(acc2, "156usd", "36chf")
_, err = k.NewOrderSingle(ctx, o)
require.NoError(t, err)
Expand All @@ -42,6 +49,80 @@ func TestQryGetAllInstruments(t *testing.T) {
json := gjson.ParseBytes(bz)
instr := json.Get("instruments")
require.True(t, instr.IsArray())
var instrLst []QueryInstrumentsResponse
err = json2.Unmarshal([]byte(instr.String()), &instrLst)
require.Nil(t, err, "Unmarshal from instruments response")

bestPriced := 0
for _, instrResp := range instrLst {
// for the 3 passive orders above
if (instrResp.Source == "jpy" && instrResp.Destination == "eur") ||
(instrResp.Source == "usd" && instrResp.Destination == "chf") ||
(instrResp.Source == "usd" && instrResp.Destination == "eur") {
require.False(t, instrResp.BestPrice.IsZero())
bestPriced++
} else {
require.Nil(t, instrResp.BestPrice)
}
}
require.Equal(t, bestPriced, 3, "3 passive orders")

// 30 because of chf, eur, gbp, jpy, ngm, usd
require.Len(t, instr.Array(), 30)
}
}

func TestQryGetAllInstrumentsWithNilBestPrices(t *testing.T) {
ctx, k, ak, _, _ := createTestComponents(t)

acc1 := createAccount(ctx, ak, "acc1", "10000eur")
acc2 := createAccount(ctx, ak, "acc2", "7400usd")
acc3 := createAccount(ctx, ak, "acc3", "2200chf")

// generate passive order
_, err := k.NewOrderSingle(ctx, order(acc1, "10000eur", "11000usd"))
require.NoError(t, err)

_, err = k.NewOrderSingle(ctx, order(acc1, "10000eur", "1400chf"))
require.NoError(t, err)

res, err := k.NewOrderSingle(ctx, order(acc2, "7400usd", "5000eur"))
require.True(t, err == nil, res.Log)

res, err = k.NewOrderSingle(ctx, order(acc3, "2200chf", "5000eur"))
require.True(t, err == nil, res.Log)

// All acc1's EUR are sold by now. No orders should be on books
orders := k.GetOrdersByOwner(ctx, acc1.GetAddress())
require.Len(t, orders, 0)

allInstruments := k.GetAllInstruments(ctx)
// 30 because of chf, eur, gbp, jpy, ngm, usd
require.Len(t, allInstruments, 30)

{
bz, err := queryInstruments(ctx, k)
require.NoError(t, err)
json := gjson.ParseBytes(bz)
instr := json.Get("instruments")
require.True(t, instr.IsArray())
var instrLst []QueryInstrumentsResponse
err = json2.Unmarshal([]byte(instr.String()), &instrLst)
require.Nil(t, err, "Unmarshal from instruments response")

transactedInstruments := "chfusd"
for _, instrResp := range instrLst {
if (instrResp.Source == "eur" || instrResp.Destination == "eur") &&
(strings.Contains(transactedInstruments, instrResp.Source) || strings.Contains(transactedInstruments, instrResp.Destination)) {
require.NotNil(t, instrResp.LastPrice)
if instrResp.BestPrice != nil {
require.False(t, instrResp.BestPrice.IsZero())
}
}
// No unfulfilled orders
require.Nil(t, instrResp.BestPrice)
}

// 30 because of chf, eur, gbp, jpy, ngm, usd
require.Len(t, instr.Array(), 30)
}
Expand Down
8 changes: 4 additions & 4 deletions x/market/spec/04_queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@

The market module can be queried using `emcli` or the [REST interface](https://cosmos.network/rpc/) of any em-ledger node.

A public interface is exposed at https://emoney.validator.network/light/.
A public interface is exposed at <https://emoney.validator.network/api/>.

## Active account orders

Active orders for a given account can be queried using `https://emoney.validator.network/light/market/account/<owner>`.
Active orders for a given account can be queried using `https://emoney.validator.network/api/market/account/<owner>`.

Or using `emcli query market account <owner>`.

## Active instruments

All instruments with active orders can be queried using `https://emoney.validator.network/light/market/instruments`.
All instruments with active orders can be queried using `https://emoney.validator.network/api/market/instruments`.

Or using `emcli query market instruments`.

_Note that there is no listing requirement for new instruments, so these are created on-the-fly based on new orders._

## Active orders per instrument

All orders for a given instrument can be queried using `https://emoney.validator.network/light/market/instrument/<source>/<destination>`.
All orders for a given instrument can be queried using `https://emoney.validator.network/api/market/instrument/<source>/<destination>`.

Or using `emcli query market instrument <source-denom> <destination-denom>`.
1 change: 0 additions & 1 deletion x/market/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ type (
MarketData struct {
Source, Destination string
LastPrice *sdk.Dec
BestPrice *sdk.Dec
Timestamp *time.Time
}
)
Expand Down

0 comments on commit 12b0302

Please sign in to comment.