Skip to content

Commit

Permalink
feat: add query trader positions (#985)
Browse files Browse the repository at this point in the history
* feat: add query trader positions
  • Loading branch information
matthiasmatt authored Oct 8, 2022
1 parent 020f7b7 commit c79db67
Show file tree
Hide file tree
Showing 14 changed files with 875 additions and 174 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#966](https://github.com/NibiruChain/nibiru/pull/966) - collections: add indexed map
* [#852](https://github.com/NibiruChain/nibiru/pull/852) - feat(genesis): add cli command to add pairs at genesis
* [#861](https://github.com/NibiruChain/nibiru/pull/861) - query cumulative funding payments
* [#985](https://github.com/NibiruChain/nibiru/pull/985) - query all active positions for a trader

### Fixes

Expand Down Expand Up @@ -174,7 +175,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#811](https://github.com/NibiruChain/nibiru/pull/811) Return the index twap in `QueryPrice` cmd
* [#813](https://github.com/NibiruChain/nibiru/pull/813) - (vpool): Expose mark price, mark TWAP, index price, and k (swap invariant) in the all-pools query
* [#816](https://github.com/NibiruChain/nibiru/pull/816) - Remove tobin tax from x/oracle
* [#810](https://github.com/NibiruChain/nibiru/pull/810) - feat(x/perp): expose 'marginRatioIndex' and block number on QueryTraderPosition
* [#810](https://github.com/NibiruChain/nibiru/pull/810) - feat(x/perp): expose 'marginRatioIndex' and block number on QueryPosition
* [#832](https://github.com/NibiruChain/nibiru/pull/832) - x/oracle app wiring

### Documentation
Expand Down
30 changes: 23 additions & 7 deletions proto/perp/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ service Query {
option (google.api.http).get = "/nibiru/perp/params";
}

rpc QueryTraderPosition(QueryTraderPositionRequest)
returns (QueryTraderPositionResponse) {
option (google.api.http).get = "/nibiru/perp/trader_position";
rpc QueryPosition(QueryPositionRequest)
returns (QueryPositionResponse) {
option (google.api.http).get = "/nibiru/perp/position";
}

rpc QueryPositions(QueryPositionsRequest)
returns (QueryPositionsResponse) {
option (google.api.http).get = "/nibiru/perp/positions";
}

rpc FundingRates(QueryFundingRatesRequest)
returns (QueryFundingRatesResponse) {
option (google.api.http).get = "/nibiru/perp/funding_rates";
Expand All @@ -37,17 +42,28 @@ message QueryParamsResponse {
Params params = 1 [ (gogoproto.nullable) = false ];
}

// ---------------------------------------- TraderPosition
// ---------------------------------------- Positions
message QueryPositionsRequest {
string trader = 1;
}

message QueryPositionsResponse {
repeated QueryPositionResponse positions = 1;
}

// QueryTraderPositionRequest is the request type for the position of the x/perp
// ---------------------------------------- Position

// QueryPositionRequest is the request type for the position of the x/perp
// module account.
message QueryTraderPositionRequest {
message QueryPositionRequest {
string token_pair = 1;

string trader = 2;
}

message QueryTraderPositionResponse {


message QueryPositionResponse {
// The position as it exists in the blockchain state
Position position = 1;

Expand Down
14 changes: 7 additions & 7 deletions x/perp/client/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.EqualValues(sdk.NewDec(60_000_000_000), reserveAssets.QuoteAssetReserve)

s.T().Log("A. check trader has no existing positions")
_, err = testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
_, err = testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
s.Error(err, "no position found")

s.T().Log("B. open position")
Expand All @@ -235,7 +235,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.EqualValues(sdk.NewDec(60_001_000_000), reserveAssets.QuoteAssetReserve)

s.T().Log("B. check vpool balances")
queryResp, err := testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
queryResp, err := testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
s.T().Logf("query response: %+v", queryResp)
s.NoError(err)
s.EqualValues(user.String(), queryResp.Position.TraderAddress)
Expand All @@ -262,7 +262,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.NoError(err)

s.T().Log("C. check trader position")
queryResp, err = testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
queryResp, err = testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
s.T().Logf("query response: %+v", queryResp)
s.NoError(err)
s.EqualValues(user.String(), queryResp.Position.TraderAddress)
Expand Down Expand Up @@ -296,7 +296,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.EqualValues(sdk.NewDec(60_002_999_900), reserveAssets.QuoteAssetReserve)

s.T().Log("D. Check trader position")
queryResp, err = testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
queryResp, err = testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
s.T().Logf("query response: %+v", queryResp)
s.NoError(err)
s.EqualValues(user.String(), queryResp.Position.TraderAddress)
Expand All @@ -323,7 +323,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.NotContains(res.String(), "fail")

s.T().Log("E. Check trader position")
queryResp, err = testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
queryResp, err = testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
s.T().Logf("query response: %+v", queryResp)
s.NoError(err)
s.EqualValues(user.String(), queryResp.Position.TraderAddress)
Expand All @@ -346,7 +346,7 @@ func (s *IntegrationTestSuite) TestOpenPositionsAndCloseCmd() {
s.NoError(err)

s.T().Log("F. check trader position")
queryResp, err = testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)
queryResp, err = testutilcli.QueryPosition(val.ClientCtx, common.Pair_BTC_NUSD, user)

s.T().Logf("query response: %+v", queryResp)
s.Error(err)
Expand All @@ -361,7 +361,7 @@ func (s *IntegrationTestSuite) TestPositionEmptyAndClose() {
user := s.users[0]

// verify trader has no position (empty)
_, err := testutilcli.QueryTraderPosition(val.ClientCtx, common.Pair_ETH_NUSD, user)
_, err := testutilcli.QueryPosition(val.ClientCtx, common.Pair_ETH_NUSD, user)
s.Error(err, "no position found")

// close position should produce error
Expand Down
43 changes: 40 additions & 3 deletions x/perp/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func GetQueryCmd() *cobra.Command {
cmds := []*cobra.Command{
CmdQueryParams(),
CmdQueryPosition(),
CmdQueryPositions(),
CmdQueryFundingRates(),
}
for _, cmd := range cmds {
Expand Down Expand Up @@ -68,7 +69,7 @@ func CmdQueryParams() *cobra.Command {
// sample token-pair: btc:nusd
func CmdQueryPosition() *cobra.Command {
cmd := &cobra.Command{
Use: "trader-position [trader] [token-pair]",
Use: "position [trader] [token-pair]",
Short: "trader's position for a given token pair/vpool",
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -89,8 +90,8 @@ func CmdQueryPosition() *cobra.Command {
return err
}

res, err := queryClient.QueryTraderPosition(
cmd.Context(), &types.QueryTraderPositionRequest{
res, err := queryClient.QueryPosition(
cmd.Context(), &types.QueryPositionRequest{
Trader: trader.String(),
TokenPair: tokenPair.String(),
},
Expand All @@ -108,6 +109,42 @@ func CmdQueryPosition() *cobra.Command {
return cmd
}

func CmdQueryPositions() *cobra.Command {
cmd := &cobra.Command{
Use: "positions [trader]",
Short: "return all of a trader's open positions",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

queryClient := types.NewQueryClient(clientCtx)

trader, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return fmt.Errorf("invalid trader address: %w", err)
}

res, err := queryClient.QueryPositions(
cmd.Context(), &types.QueryPositionsRequest{
Trader: trader.String(),
},
)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// sample token-pair: btc:nusd
func CmdQueryFundingRates() *cobra.Command {
cmd := &cobra.Command{
Expand Down
40 changes: 34 additions & 6 deletions x/perp/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,37 @@ func NewQuerier(k Keeper) types.QueryServer {

var _ types.QueryServer = queryServer{}

func (q queryServer) QueryTraderPosition(
goCtx context.Context, req *types.QueryTraderPositionRequest,
) (*types.QueryTraderPositionResponse, error) {
func (q queryServer) QueryPositions(
goCtx context.Context, req *types.QueryPositionsRequest,
) (*types.QueryPositionsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
_, err := sdk.AccAddressFromBech32(req.Trader) // just for validation purposes
if err != nil {
return nil, err
}

ctx := sdk.UnwrapSDKContext(goCtx)

pools := q.k.VpoolKeeper.GetAllPools(ctx)
var positions []*types.QueryPositionResponse

for _, pool := range pools {
position, err := q.position(ctx, pool.Pair, req.Trader)
if err == nil {
positions = append(positions, position)
}
}

return &types.QueryPositionsResponse{
Positions: positions,
}, nil
}

func (q queryServer) QueryPosition(
goCtx context.Context, req *types.QueryPositionRequest,
) (*types.QueryPositionResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
Expand All @@ -41,10 +69,10 @@ func (q queryServer) QueryTraderPosition(
return nil, err
}

return q.traderPosition(ctx, pair, req.Trader)
return q.position(ctx, pair, req.Trader)
}

func (q queryServer) traderPosition(ctx sdk.Context, pair common.AssetPair, trader string) (*types.QueryTraderPositionResponse, error) {
func (q queryServer) position(ctx sdk.Context, pair common.AssetPair, trader string) (*types.QueryPositionResponse, error) {
position, err := q.k.Positions.Get(ctx, keys.Join(pair, keys.String(trader)))
if err != nil {
return nil, err
Expand All @@ -67,7 +95,7 @@ func (q queryServer) traderPosition(ctx sdk.Context, pair common.AssetPair, trad
marginRatioIndex = sdk.Dec{}
}

return &types.QueryTraderPositionResponse{
return &types.QueryPositionResponse{
Position: &position,
PositionNotional: positionNotional,
UnrealizedPnl: unrealizedPnl,
Expand Down
Loading

0 comments on commit c79db67

Please sign in to comment.