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

Commit b56d7b5

Browse files
authored
fix op_underfunded error when replenishing top offer (#253), fixes #250
* 1 - add log line for greater visibility into creation of preceding offers * 2 - update comment to clarify what we do after adding preceding levels * 3 - rename currentLevels -> desiredLevels in sellSideStrategy.go * 4 - log buy/sell action when creating preceding levels * 5 - remove padding of offers and use existingOfferIndex instead to be more logically explicit and to keep level logging more accurate * 6 - the fix: we should not return an extra level consumed because we may want to delete or modify it, whereas it currently skips that level entirely
1 parent fb6f4d4 commit b56d7b5

1 file changed

Lines changed: 33 additions & 27 deletions

File tree

plugins/sellSideStrategy.go

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type sellSideStrategy struct {
2828
action string
2929

3030
// uninitialized
31-
currentLevels []api.Level // levels for current iteration
31+
desiredLevels []api.Level // levels for current iteration
3232
maxAssetBase float64
3333
maxAssetQuote float64
3434
}
@@ -69,7 +69,7 @@ func makeSellSideStrategy(
6969
// PruneExistingOffers impl
7070
func (s *sellSideStrategy) PruneExistingOffers(offers []hProtocol.Offer) ([]build.TransactionMutator, []hProtocol.Offer) {
7171
// figure out which offers we want to prune
72-
shouldPrune := computeOffersToPrune(offers, s.currentLevels)
72+
shouldPrune := computeOffersToPrune(offers, s.desiredLevels)
7373

7474
pruneOps := []build.TransactionMutator{}
7575
updatedOffers := []hProtocol.Offer{}
@@ -139,14 +139,14 @@ func (s *sellSideStrategy) PreUpdate(maxAssetBase float64, maxAssetQuote float64
139139
nothingToSell := maxAssetBase == 0
140140
lineFull := maxAssetQuote == trustQuote
141141
if nothingToSell || lineFull {
142-
s.currentLevels = []api.Level{}
142+
s.desiredLevels = []api.Level{}
143143
log.Printf("no capacity to place sell orders (nothingToSell = %v, lineFull = %v)\n", nothingToSell, lineFull)
144144
return nil
145145
}
146146

147-
// load currentLevels only once here
147+
// load desiredLevels only once here
148148
var e error
149-
s.currentLevels, e = s.levelsProvider.GetLevels(s.maxAssetBase, s.maxAssetQuote)
149+
s.desiredLevels, e = s.levelsProvider.GetLevels(s.maxAssetBase, s.maxAssetQuote)
150150
if e != nil {
151151
log.Printf("levels couldn't be loaded: %s\n", e)
152152
return e
@@ -219,9 +219,8 @@ func (s *sellSideStrategy) createPrecedingOffers(
219219

220220
for i := 0; i < len(precedingLevels); i++ {
221221
if hitCapacityLimit {
222-
// we consider the ith level consumed because we don't want to create an offer for it anyway since we hit the capacity limit
223-
log.Printf("hitCapacityLimit in preceding level loop, returning numLevelsConsumed=%d\n", i+1)
224-
return (i + 1), true, ops, newTopOffer, nil
222+
log.Printf("%s, hitCapacityLimit in preceding level loop, returning numLevelsConsumed=%d\n", s.action, i)
223+
return i, true, ops, newTopOffer, nil
225224
}
226225

227226
targetPrice, targetAmount, e := s.computeTargets(precedingLevels[i])
@@ -246,36 +245,42 @@ func (s *sellSideStrategy) createPrecedingOffers(
246245
}
247246
}
248247

248+
numLevelsConsumed := len(precedingLevels)
249+
newTopOfferPrice := "<nil>"
250+
if newTopOffer != nil {
251+
newTopOfferPrice = newTopOffer.AsString()
252+
}
253+
log.Printf("%s, done creating preceding offers (numLevelsConsumed=%d, hitCapacityLimit=%v, numOps=%d, newTopOfferPrice=%s)",
254+
s.action, numLevelsConsumed, hitCapacityLimit, len(ops), newTopOfferPrice,
255+
)
256+
249257
// hitCapacityLimit can be updated after the check inside the for loop
250-
return len(precedingLevels), hitCapacityLimit, ops, newTopOffer, nil
258+
return numLevelsConsumed, hitCapacityLimit, ops, newTopOffer, nil
251259
}
252260

253261
// UpdateWithOps impl
254262
func (s *sellSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []build.TransactionMutator, newTopOffer *model.Number, e error) {
255263
deleteOps := []build.TransactionMutator{}
256264

257265
// first we want to re-create any offers that precede our existing offers and are additions to the existing offers that we have
258-
precedingLevels := computePrecedingLevels(offers, s.currentLevels)
266+
precedingLevels := computePrecedingLevels(offers, s.desiredLevels)
259267
var hitCapacityLimit bool
260268
var numLevelsConsumed int
261269
numLevelsConsumed, hitCapacityLimit, ops, newTopOffer, e = s.createPrecedingOffers(precedingLevels)
262270
if e != nil {
263271
return nil, nil, fmt.Errorf("unable to create preceding offers: %s", e)
264272
}
265-
// pad the offers so it lines up correctly with numLevelsConsumed.
266-
// alternatively we could chop off the beginning of s.currentLevels but then that affects the logging of levels downstream
267-
for i := 0; i < numLevelsConsumed; i++ {
268-
offers = append([]hProtocol.Offer{{}}, offers...)
269-
}
270273

271-
// next we want to adjust our remaining offers to be in line with what is desired, creating new offers that may not exist at the end of our existing offers
272-
for i := numLevelsConsumed; i < len(s.currentLevels); i++ {
273-
isModify := i < len(offers)
274+
// next we want to adjust our remaining offers to be in line with what is desired
275+
// either modifying the existing offers, or creating new offers at the end of our existing offers
276+
for i := numLevelsConsumed; i < len(s.desiredLevels); i++ {
277+
existingOffersIdx := i - numLevelsConsumed
278+
isModify := existingOffersIdx < len(offers)
274279
// we only want to delete offers after we hit the capacity limit which is why we perform this check in the beginning
275280
if hitCapacityLimit {
276281
if isModify {
277-
delOp := s.sdex.DeleteOffer(offers[i])
278-
log.Printf("deleting offer because we previously hit the capacity limit, offerId=%d\n", offers[i].ID)
282+
delOp := s.sdex.DeleteOffer(offers[existingOffersIdx])
283+
log.Printf("deleting offer because we previously hit the capacity limit, offerId=%d\n", offers[existingOffersIdx].ID)
279284
deleteOps = append(deleteOps, delOp)
280285
continue
281286
} else {
@@ -285,23 +290,23 @@ func (s *sellSideStrategy) UpdateWithOps(offers []hProtocol.Offer) (ops []build.
285290
}
286291

287292
// hitCapacityLimit can be updated below
288-
targetPrice, targetAmount, e := s.computeTargets(s.currentLevels[i])
293+
targetPrice, targetAmount, e := s.computeTargets(s.desiredLevels[i])
289294
if e != nil {
290295
return nil, nil, fmt.Errorf("could not compute targets: %s", e)
291296
}
292297

293298
var offerPrice *model.Number
294299
var op *build.ManageOfferBuilder
295300
if isModify {
296-
offerPrice, hitCapacityLimit, op, e = s.modifySellLevel(offers, i, *targetPrice, *targetAmount)
301+
offerPrice, hitCapacityLimit, op, e = s.modifySellLevel(offers, existingOffersIdx, i, *targetPrice, *targetAmount)
297302
} else {
298303
offerPrice, hitCapacityLimit, op, e = s.createSellLevel(i, *targetPrice, *targetAmount)
299304
}
300305
if e != nil {
301306
return nil, nil, fmt.Errorf("unable to update existing offers or create new offers: %s", e)
302307
}
303308
if op != nil {
304-
reducedOrderSize := isModify && targetAmount.AsFloat() < utils.AmountStringAsFloat(offers[i].Amount)
309+
reducedOrderSize := isModify && targetAmount.AsFloat() < utils.AmountStringAsFloat(offers[existingOffersIdx].Amount)
305310
hitCapacityLimitModify := isModify && hitCapacityLimit
306311
if reducedOrderSize || hitCapacityLimitModify {
307312
// prepend operations that reduce the size of an existing order because they decrease our liabilities
@@ -382,7 +387,7 @@ func (s *sellSideStrategy) createSellLevel(index int, targetPrice model.Number,
382387
priceLogged = 1 / price
383388
amountLogged = amount * price
384389
}
385-
log.Printf("%s | create | level=%d | priceQuote=%.8f | amtBase=%.8f\n", s.action, index+1, priceLogged, amountLogged)
390+
log.Printf("%s | create | new level=%d | priceQuote=%.8f | amtBase=%.8f\n", s.action, index+1, priceLogged, amountLogged)
386391
return s.sdex.CreateSellOffer(*s.assetBase, *s.assetQuote, price, amount, incrementalNativeAmountRaw)
387392
},
388393
*s.assetBase,
@@ -392,7 +397,7 @@ func (s *sellSideStrategy) createSellLevel(index int, targetPrice model.Number,
392397
}
393398

394399
// modifySellLevel returns offerPrice, hitCapacityLimit, op, error.
395-
func (s *sellSideStrategy) modifySellLevel(offers []hProtocol.Offer, index int, targetPrice model.Number, targetAmount model.Number) (*model.Number, bool, *build.ManageOfferBuilder, error) {
400+
func (s *sellSideStrategy) modifySellLevel(offers []hProtocol.Offer, index int, newIndex int, targetPrice model.Number, targetAmount model.Number) (*model.Number, bool, *build.ManageOfferBuilder, error) {
396401
highestPrice := targetPrice.AsFloat() + targetPrice.AsFloat()*s.priceTolerance
397402
lowestPrice := targetPrice.AsFloat() - targetPrice.AsFloat()*s.priceTolerance
398403
minAmount := targetAmount.AsFloat() - targetAmount.AsFloat()*s.amountTolerance
@@ -409,6 +414,7 @@ func (s *sellSideStrategy) modifySellLevel(offers []hProtocol.Offer, index int,
409414
if !priceTrigger && !amountTrigger {
410415
// always add back the current offer in the cached liabilities when we don't modify it
411416
s.ieif.AddLiabilities(offers[index].Selling, offers[index].Buying, curAmount, curAmount*curPrice, incrementalNativeAmountRaw)
417+
log.Printf("%s | modify | unmodified original level = %d | newLevel number = %d\n", s.action, index+1, newIndex+1)
412418
offerPrice := model.NumberFromFloat(curPrice, s.orderConstraints.PricePrecision)
413419
return offerPrice, false, nil, nil
414420
}
@@ -439,8 +445,8 @@ func (s *sellSideStrategy) modifySellLevel(offers []hProtocol.Offer, index int,
439445
lowestPriceLogged = 1 / highestPrice
440446
highestPriceLogged = 1 / lowestPrice
441447
}
442-
log.Printf("%s | modify | level=%d | targetPriceQuote=%.8f | targetAmtBase=%.8f | curPriceQuote=%.8f | lowPriceQuote=%.8f | highPriceQuote=%.8f | curAmtBase=%.8f | minAmtBase=%.8f | maxAmtBase=%.8f\n",
443-
s.action, index+1, priceLogged, amountLogged, curPriceLogged, lowestPriceLogged, highestPriceLogged, curAmountLogged, minAmountLogged, maxAmountLogged)
448+
log.Printf("%s | modify | old level=%d | new level = %d | targetPriceQuote=%.8f | targetAmtBase=%.8f | curPriceQuote=%.8f | lowPriceQuote=%.8f | highPriceQuote=%.8f | curAmtBase=%.8f | minAmtBase=%.8f | maxAmtBase=%.8f\n",
449+
s.action, index+1, newIndex+1, priceLogged, amountLogged, curPriceLogged, lowestPriceLogged, highestPriceLogged, curAmountLogged, minAmountLogged, maxAmountLogged)
444450
return s.sdex.ModifySellOffer(offers[index], price, amount, incrementalNativeAmountRaw)
445451
},
446452
offers[index].Selling,

0 commit comments

Comments
 (0)