Skip to content
This repository has been archived by the owner on Feb 1, 2024. It is now read-only.

bugfix: volumeFilterFn should explicitly take in action buy/sell, closes #646 #647

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions plugins/volumeFilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const (
volumeFilterModeIgnore volumeFilterMode = "ignore"
)

// String is the Stringer method
func (v volumeFilterMode) String() string {
return string(v)
}

func parseVolumeFilterMode(mode string) (volumeFilterMode, error) {
if mode == string(volumeFilterModeExact) {
return volumeFilterModeExact, nil
Expand Down Expand Up @@ -147,25 +152,19 @@ func (f *volumeFilter) Apply(ops []txnbuild.Operation, sellingOffers []hProtocol
dateString, dailyValuesBaseSold.BaseVol, utils.Asset2String(f.baseAsset), dailyValuesBaseSold.QuoteVol, utils.Asset2String(f.quoteAsset), f.config)

// daily on-the-books
dailyOTB := &VolumeFilterConfig{
BaseAssetCapInBaseUnits: &dailyValuesBaseSold.BaseVol,
BaseAssetCapInQuoteUnits: &dailyValuesBaseSold.QuoteVol,
}
dailyOTB := makeIntermediateVolumeFilterConfig(&dailyValuesBaseSold.BaseVol, &dailyValuesBaseSold.QuoteVol)
// daily to-be-booked starts out as empty and accumulates the values of the operations
dailyTbbBase := 0.0
dailyTbbSellQuote := 0.0
dailyTBB := &VolumeFilterConfig{
BaseAssetCapInBaseUnits: &dailyTbbBase,
BaseAssetCapInQuoteUnits: &dailyTbbSellQuote,
}
dailyTBB := makeIntermediateVolumeFilterConfig(&dailyTbbBase, &dailyTbbSellQuote)

innerFn := func(op *txnbuild.ManageSellOffer) (*txnbuild.ManageSellOffer, error) {
limitParameters := limitParameters{
baseAssetCapInBaseUnits: f.config.BaseAssetCapInBaseUnits,
baseAssetCapInQuoteUnits: f.config.BaseAssetCapInQuoteUnits,
mode: f.config.mode,
}
return volumeFilterFn(dailyOTB, dailyTBB, op, f.baseAsset, f.quoteAsset, limitParameters)
return volumeFilterFn(f.config.action, dailyOTB, dailyTBB, op, f.baseAsset, f.quoteAsset, limitParameters)
}
ops, e = filterOps(f.name, f.baseAsset, f.quoteAsset, sellingOffers, buyingOffers, ops, innerFn)
if e != nil {
Expand All @@ -174,14 +173,22 @@ func (f *volumeFilter) Apply(ops []txnbuild.Operation, sellingOffers []hProtocol
return ops, nil
}

func volumeFilterFn(dailyOTB *VolumeFilterConfig, dailyTBBAccumulator *VolumeFilterConfig, op *txnbuild.ManageSellOffer, baseAsset hProtocol.Asset, quoteAsset hProtocol.Asset, lp limitParameters) (*txnbuild.ManageSellOffer, error) {
isFilterApplicable, e := offerSameTypeAsFilter(dailyOTB, op, baseAsset, quoteAsset)
func makeIntermediateVolumeFilterConfig(baseCapBaseUnits *float64, baseCapQuoteUnits *float64) *VolumeFilterConfig {
return &VolumeFilterConfig{
BaseAssetCapInBaseUnits: baseCapBaseUnits,
BaseAssetCapInQuoteUnits: baseCapQuoteUnits,
}
}

func volumeFilterFn(action queries.DailyVolumeAction, dailyOTB *VolumeFilterConfig, dailyTBBAccumulator *VolumeFilterConfig, op *txnbuild.ManageSellOffer, baseAsset hProtocol.Asset, quoteAsset hProtocol.Asset, lp limitParameters) (*txnbuild.ManageSellOffer, error) {
isFilterApplicable, e := offerSameTypeAsFilter(action, op, baseAsset, quoteAsset)
if e != nil {
return nil, fmt.Errorf("could not compare offer and filter: %s", e)
}

if !isFilterApplicable {
// ignore filter so return op directly
log.Printf("volumeFilter: isSell=%v, isFilterApplicable=false; keep=true", action.IsSell())
return op, nil
}

Expand All @@ -197,7 +204,7 @@ func volumeFilterFn(dailyOTB *VolumeFilterConfig, dailyTBBAccumulator *VolumeFil
// A "buy" op has amount = sellAmount * sellPrice, and price = 1/sellPrice
// So, we adjust the offer variables by "undoing" those adjustments
// We can then use the same computations as sell orders on buy orders
if dailyOTB.action.IsBuy() {
if action.IsBuy() {
offerAmount = offerAmount * offerPrice
offerPrice = 1 / offerPrice
}
Expand All @@ -219,17 +226,20 @@ func volumeFilterFn(dailyOTB *VolumeFilterConfig, dailyTBBAccumulator *VolumeFil
projected := otb + tbb + offerAmount*capPrice
if projected <= cap {
dailyTBBAccumulator = updateTBB(dailyTBBAccumulator, offerAmount, offerPrice)
log.Printf("volumeFilter: isSell=%v, offerPrice=%.10f, projected (%.10f) <= cap (%.10f); keep=true", action.IsSell(), offerPrice, projected, cap)
return op, nil
}

// for ignore type of filters we want to drop the operations when the cap is exceeded
if lp.mode == volumeFilterModeIgnore {
log.Printf("volumeFilter: isSell=%v, offerPrice=%.10f; lp.mode=%s, keep=false", action.IsSell(), offerPrice, lp.mode.String())
return nil, nil
}

// if exact mode and with remaining capacity, update the op amount and return the op otherwise return nil
newOfferAmount := (cap - otb - tbb) / capPrice
if newOfferAmount <= 0 {
log.Printf("volumeFilter: isSell=%v, offerPrice=%.10f, newOfferAmount (%.10f) <= 0; keep=false", action.IsSell(), offerPrice, newOfferAmount)
return nil, nil
}
dailyTBBAccumulator = updateTBB(dailyTBBAccumulator, newOfferAmount, offerPrice)
Expand All @@ -243,20 +253,22 @@ func volumeFilterFn(dailyOTB *VolumeFilterConfig, dailyTBBAccumulator *VolumeFil
// newOpAmount = newOpAmount * sellOfferPrice
// newOpAmount => newOpAmount * 1 / buyOfferPrice
newOpAmount := newOfferAmount
if dailyOTB.action.IsBuy() {
if action.IsBuy() {
newOpAmount = newOpAmount * offerPrice
}
op.Amount = fmt.Sprintf("%.7f", newOpAmount)

log.Printf("volumeFilter: isSell=%v, offerPrice=%.10f, newOpAmount=%s; keep=true", action.IsSell(), offerPrice, op.Amount)
return op, nil
}

func offerSameTypeAsFilter(filter *VolumeFilterConfig, op *txnbuild.ManageSellOffer, baseAsset hProtocol.Asset, quoteAsset hProtocol.Asset) (bool, error) {
func offerSameTypeAsFilter(action queries.DailyVolumeAction, op *txnbuild.ManageSellOffer, baseAsset hProtocol.Asset, quoteAsset hProtocol.Asset) (bool, error) {
opIsSelling, e := utils.IsSelling(baseAsset, quoteAsset, op.Selling, op.Buying)
if e != nil {
return false, fmt.Errorf("error when running the isSelling check for offer '%+v': %s", *op, e)
}
isSame := opIsSelling == filter.action.IsSell()
isSame := opIsSelling == action.IsSell()
log.Printf("volumeFilter: opIsSelling (%v) == filter.action.IsSell() (%v); isSame = %v", opIsSelling, action.IsSell(), isSame)
return isSame, nil
}

Expand Down
8 changes: 4 additions & 4 deletions plugins/volumeFilter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1229,8 +1229,8 @@ func runTestVolumeFilterFn(
}

// we pass in nil market IDs and account IDs, as they don't affect correctness
dailyOTB := makeRawVolumeFilterConfig(baseOTB, quoteOTB, action, mode, nil, nil)
dailyTBBAccumulator := makeRawVolumeFilterConfig(baseTBB, quoteTBB, action, mode, nil, nil)
dailyOTB := makeIntermediateVolumeFilterConfig(baseOTB, quoteOTB)
dailyTBBAccumulator := makeIntermediateVolumeFilterConfig(baseTBB, quoteTBB)
lp := limitParameters{
baseAssetCapInBaseUnits: baseCap,
baseAssetCapInQuoteUnits: quoteCap,
Expand All @@ -1239,15 +1239,15 @@ func runTestVolumeFilterFn(

base := utils.Asset2Asset2(testBaseAsset)
quote := utils.Asset2Asset2(testQuoteAsset)
actual, e := volumeFilterFn(dailyOTB, dailyTBBAccumulator, inputOp, base, quote, lp)
actual, e := volumeFilterFn(action, dailyOTB, dailyTBBAccumulator, inputOp, base, quote, lp)
if !assert.Nil(t, e) {
return
}
if !assert.Equal(t, wantOp, actual) {
return
}

wantTBBAccumulator := makeRawVolumeFilterConfig(wantBase, wantQuote, action, mode, nil, nil)
wantTBBAccumulator := makeIntermediateVolumeFilterConfig(wantBase, wantQuote)
assert.Equal(t, wantTBBAccumulator, dailyTBBAccumulator)
})
}
Expand Down