Skip to content

Commit

Permalink
[Feature] New market params for the high volatility TERRA (#300)
Browse files Browse the repository at this point in the history
* add market parameter TobinTaxList to specify a more strict tobin tax for the denom with high volatility

* modify the param TobinTaxList to IlliquidTobinTaxList

* typo
  • Loading branch information
yys authored and dokwon committed Dec 3, 2019
1 parent 15fbf9c commit 1b35fb8
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 32 deletions.
1 change: 1 addition & 0 deletions types/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
MicroJPYDenom = assets.MicroJPYDenom
MicroEURDenom = assets.MicroEURDenom
MicroGBPDenom = assets.MicroGBPDenom
MicroMNTDenom = assets.MicroMNTDenom
MicroUnit = assets.MicroUnit
BlocksPerMinute = util.BlocksPerMinute
BlocksPerHour = util.BlocksPerHour
Expand Down
1 change: 1 addition & 0 deletions types/assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
MicroJPYDenom = "ujpy"
MicroEURDenom = "ueur"
MicroGBPDenom = "ugbp"
MicroMNTDenom = "umnt"

MicroUnit = int64(1e6)
)
21 changes: 11 additions & 10 deletions x/market/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,17 @@ var (
NewQuerier = keeper.NewQuerier

// variable aliases
ModuleCdc = types.ModuleCdc
TerraPoolDeltaKey = types.TerraPoolDeltaKey
ParamStoreKeyBasePool = types.ParamStoreKeyBasePool
ParamStoreKeyPoolRecoveryPeriod = types.ParamStoreKeyPoolRecoveryPeriod
ParamStoreKeyMinSpread = types.ParamStoreKeyMinSpread
ParmamStoreKeyTobinTax = types.ParmamStoreKeyTobinTax
DefaultBasePool = types.DefaultBasePool
DefaultPoolRecoveryPeriod = types.DefaultPoolRecoveryPeriod
DefaultMinSpread = types.DefaultMinSpread
DefaultTobinTax = types.DefaultTobinTax
ModuleCdc = types.ModuleCdc
TerraPoolDeltaKey = types.TerraPoolDeltaKey
ParamStoreKeyBasePool = types.ParamStoreKeyBasePool
ParamStoreKeyPoolRecoveryPeriod = types.ParamStoreKeyPoolRecoveryPeriod
ParamStoreKeyMinSpread = types.ParamStoreKeyMinSpread
ParmaStoreKeyTobinTax = types.ParmaStoreKeyTobinTax
ParmaStoreKeyIlliquidTobinTaxList = types.ParmaStoreKeyIlliquidTobinTaxList
DefaultBasePool = types.DefaultBasePool
DefaultPoolRecoveryPeriod = types.DefaultPoolRecoveryPeriod
DefaultMinSpread = types.DefaultMinSpread
DefaultTobinTax = types.DefaultTobinTax
)

type (
Expand Down
11 changes: 9 additions & 2 deletions x/market/internal/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ func (k Keeper) PoolRecoveryPeriod(ctx sdk.Context) (res int64) {
return
}

// TobinTax is a tax on all spot conversions of one Terra into another Terra
// TobinTax is a tax rate on all spot conversions of one Terra into another Terra
func (k Keeper) TobinTax(ctx sdk.Context) (res sdk.Dec) {
k.paramSpace.Get(ctx, types.ParmamStoreKeyTobinTax, &res)
k.paramSpace.Get(ctx, types.ParmaStoreKeyTobinTax, &res)
return
}

// IlliquidTobinTaxList is the exceptions that have to pay a higher tobin tax due to illiquidity
// TobinTax will be used for the denoms which are not in the list
func (k Keeper) IlliquidTobinTaxList(ctx sdk.Context) (res types.TobinTaxList) {
k.paramSpace.Get(ctx, types.ParmaStoreKeyIlliquidTobinTaxList, &res)
return
}

Expand Down
12 changes: 12 additions & 0 deletions x/market/internal/keeper/swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ func (k Keeper) ComputeSwap(ctx sdk.Context, offerCoin sdk.Coin, askDenom string
// Apply only tobin tax without constant product spread
if offerCoin.Denom != core.MicroLunaDenom && askDenom != core.MicroLunaDenom {
spread = k.TobinTax(ctx)
illiquidTobinTaxList := k.IlliquidTobinTaxList(ctx)

// Apply highest tobin tax for the denoms in the swap operation
for _, tobinTax := range illiquidTobinTaxList {
if tobinTax.Denom == offerCoin.Denom ||
tobinTax.Denom == askDenom {
if tobinTax.TaxRate.GT(spread) {
spread = tobinTax.TaxRate
}
}
}

return
}

Expand Down
43 changes: 43 additions & 0 deletions x/market/internal/keeper/swap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
core "github.com/terra-project/core/types"
"github.com/terra-project/core/x/market/internal/types"
)

func TestApplySwapToPool(t *testing.T) {
Expand Down Expand Up @@ -82,3 +83,45 @@ func TestComputeInternalSwap(t *testing.T) {
_, err := input.MarketKeeper.ComputeInternalSwap(input.Ctx, offerCoin, core.MicroLunaDenom)
require.Error(t, err)
}

func TestIlliquidTobinTaxListParams(t *testing.T) {
input := CreateTestInput(t)

// Set Oracle Price
lunaPriceInSDR := sdk.NewDecWithPrec(17, 1)
lunaPriceInMNT := sdk.NewDecWithPrec(7652, 1)
input.OracleKeeper.SetLunaExchangeRate(input.Ctx, core.MicroSDRDenom, lunaPriceInSDR)
input.OracleKeeper.SetLunaExchangeRate(input.Ctx, core.MicroMNTDenom, lunaPriceInMNT)

// Case 1: tobin tax 2% due to umnt denom
params := input.MarketKeeper.GetParams(input.Ctx)
params.TobinTax = sdk.NewDecWithPrec(25, 4)
params.IlliquidTobinTaxList = types.TobinTaxList{
types.TobinTax{
Denom: core.MicroSDRDenom,
TaxRate: sdk.NewDecWithPrec(25, 4),
},
types.TobinTax{
Denom: core.MicroMNTDenom,
TaxRate: sdk.NewDecWithPrec(2, 2),
},
}
input.MarketKeeper.SetParams(input.Ctx, params)

swapAmountInSDR := lunaPriceInSDR.MulInt64(rand.Int63()%10000 + 2).TruncateInt()
offerCoin := sdk.NewCoin(core.MicroSDRDenom, swapAmountInSDR)
_, spread, err := input.MarketKeeper.ComputeSwap(input.Ctx, offerCoin, core.MicroMNTDenom)
require.NoError(t, err)
require.Equal(t, sdk.NewDecWithPrec(2, 2), spread)

// Case 2: tobin tax 5% due to default
params.TobinTax = sdk.NewDecWithPrec(5, 2)
input.MarketKeeper.SetParams(input.Ctx, params)

swapAmountInSDR = lunaPriceInSDR.MulInt64(rand.Int63()%10000 + 2).TruncateInt()
offerCoin = sdk.NewCoin(core.MicroSDRDenom, swapAmountInSDR)
_, spread, err = input.MarketKeeper.ComputeSwap(input.Ctx, offerCoin, core.MicroMNTDenom)
require.NoError(t, err)
require.Equal(t, sdk.NewDecWithPrec(5, 2), spread)

}
2 changes: 2 additions & 0 deletions x/market/internal/types/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ func TestGenesisValidation(t *testing.T) {

genState.Params.MinSpread = sdk.NewDec(-1)
require.Error(t, ValidateGenesis(genState))

require.True(t, len(genState.Params.String()) != 0)
}

func TestGenesisEqual(t *testing.T) {
Expand Down
47 changes: 32 additions & 15 deletions x/market/internal/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,44 @@ var (
// Min spread
ParamStoreKeyMinSpread = []byte("minspread")
// Tobin tax
ParmamStoreKeyTobinTax = []byte("tobintax")
ParmaStoreKeyTobinTax = []byte("tobintax")
// Illiquid tobin tax list
ParmaStoreKeyIlliquidTobinTaxList = []byte("illiquidtobintaxlist")
)

// Default parameter values
var (
DefaultBasePool = sdk.NewDec(250000 * core.MicroUnit) // 250,000sdr = 250,000,000,000usdr
DefaultPoolRecoveryPeriod = core.BlocksPerDay // 14,400
DefaultMinSpread = sdk.NewDecWithPrec(2, 2) // 2%
DefaultTobinTax = sdk.NewDecWithPrec(25, 4) // 0.25%
DefaultBasePool = sdk.NewDec(250000 * core.MicroUnit) // 250,000sdr = 250,000,000,000usdr
DefaultPoolRecoveryPeriod = core.BlocksPerDay // 14,400
DefaultMinSpread = sdk.NewDecWithPrec(2, 2) // 2%
DefaultTobinTax = sdk.NewDecWithPrec(25, 4) // 0.25%
DefaultIlliquidTobinTaxList = TobinTaxList{
{
Denom: core.MicroMNTDenom,
TaxRate: sdk.NewDecWithPrec(2, 2), // 2%
},
}
)

var _ subspace.ParamSet = &Params{}

// Params market parameters
type Params struct {
PoolRecoveryPeriod int64 `json:"pool_recovery_period" yaml:"pool_recovery_period"`
BasePool sdk.Dec `json:"base_pool" yaml:"base_pool"`
MinSpread sdk.Dec `json:"min_spread" yaml:"min_spread"`
TobinTax sdk.Dec `json:"tobin_tax" yaml:"tobin_tax"`
PoolRecoveryPeriod int64 `json:"pool_recovery_period" yaml:"pool_recovery_period"`
BasePool sdk.Dec `json:"base_pool" yaml:"base_pool"`
MinSpread sdk.Dec `json:"min_spread" yaml:"min_spread"`
TobinTax sdk.Dec `json:"tobin_tax" yaml:"tobin_tax"`
IlliquidTobinTaxList TobinTaxList `json:"illiquid_tobin_tax_list" yaml:"illiquid_tobin_tax_list"`
}

// DefaultParams creates default market module parameters
func DefaultParams() Params {
return Params{
BasePool: DefaultBasePool,
PoolRecoveryPeriod: DefaultPoolRecoveryPeriod,
MinSpread: DefaultMinSpread,
TobinTax: DefaultTobinTax,
BasePool: DefaultBasePool,
PoolRecoveryPeriod: DefaultPoolRecoveryPeriod,
MinSpread: DefaultMinSpread,
TobinTax: DefaultTobinTax,
IlliquidTobinTaxList: DefaultIlliquidTobinTaxList,
}
}

Expand All @@ -66,6 +76,11 @@ func (params Params) Validate() error {
if params.TobinTax.IsNegative() || params.TobinTax.GT(sdk.OneDec()) {
return fmt.Errorf("tobin tax should be a value between [0,1], is %s", params.TobinTax)
}
for _, val := range params.IlliquidTobinTaxList {
if val.TaxRate.IsNegative() || val.TaxRate.GT(sdk.OneDec()) {
return fmt.Errorf("tobin tax should be a value between [0,1], is %s", val)
}
}

return nil
}
Expand All @@ -78,7 +93,8 @@ func (params *Params) ParamSetPairs() subspace.ParamSetPairs {
{Key: ParamStoreKeyBasePool, Value: &params.BasePool},
{Key: ParamStoreKeyPoolRecoveryPeriod, Value: &params.PoolRecoveryPeriod},
{Key: ParamStoreKeyMinSpread, Value: &params.MinSpread},
{Key: ParmamStoreKeyTobinTax, Value: &params.TobinTax},
{Key: ParmaStoreKeyTobinTax, Value: &params.TobinTax},
{Key: ParmaStoreKeyIlliquidTobinTaxList, Value: &params.IlliquidTobinTaxList},
}
}

Expand All @@ -89,5 +105,6 @@ func (params Params) String() string {
PoolRecoveryPeriod: %d
MinSpread: %s
TobinTax: %s
`, params.BasePool, params.PoolRecoveryPeriod, params.MinSpread, params.TobinTax)
IlliquidTobinTaxList: %s
`, params.BasePool, params.PoolRecoveryPeriod, params.MinSpread, params.TobinTax, params.IlliquidTobinTaxList)
}
34 changes: 34 additions & 0 deletions x/market/internal/types/tobin_tax.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package types

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
)

// TobinTax - struct to store tobin tax for the specific denom with high volatility
type TobinTax struct {
Denom string `json:"denom" yaml:"denom"`
TaxRate sdk.Dec `json:"tax_rate" yaml:"tax_rate"`
}

// String implements fmt.Stringer interface
func (tt TobinTax) String() string {
return fmt.Sprintf(`TobinTax
Denom: %s,
TaxRate: %s`,
tt.Denom, tt.TaxRate)
}

// TobinTaxList is convience wrapper to handle TobinTax array
type TobinTaxList []TobinTax

// String implements fmt.Stringer interface
func (ttl TobinTaxList) String() (out string) {
out = ""
for _, tt := range ttl {
out += tt.String() + "\n"
}

return
}
5 changes: 2 additions & 3 deletions x/oracle/internal/types/denom.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
type DenomList []string

// String implements fmt.Stringer interface
func (dl DenomList) String() (out string) {
strings.Join(dl, "\n")
return
func (dl DenomList) String() string {
return strings.Join(dl, "\n")
}
4 changes: 2 additions & 2 deletions x/oracle/internal/types/vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func (pv ExchangeRateVote) getPower(ctx sdk.Context, powerMap map[string]int64)
// String implements fmt.Stringer interface
func (pv ExchangeRateVote) String() string {
return fmt.Sprintf(`ExchangeRateVote
Denom: %s,
Voter: %s,
Denom: %s,
Voter: %s,
ExchangeRate: %s`,
pv.Denom, pv.Voter, pv.ExchangeRate)
}
Expand Down

0 comments on commit 1b35fb8

Please sign in to comment.