@@ -166,6 +166,278 @@ type volumeFilterFnTestCase struct {
166166 wantTbbQuote float64
167167}
168168
169+ func TestVolumeFilterFn_BaseCap_Exact (t * testing.T ) {
170+ // We want to test the following 4 valid combinations of OTB and TBB values:
171+ // otb = 0
172+ // tbb = 0
173+ // otb = 0 && tbb = 0
174+ // otb > 0 && tbb > 0
175+ // We also want to test 3 combinations of cap relationship to projected (<, =, >)
176+ // Finally, if projected > cap, we want to test 3 possible values of newAmount (+, 0, -)
177+ // The above gives 4 * (2 + 1*3) = 20.
178+ // One generated case is impossible in the code: otb = 0 && tbb = 0, newAmount < 0
179+ // so we have 19 cases
180+ testCases := []volumeFilterFnTestCase {
181+ {
182+ name : "1. otb = 0; projected < cap" ,
183+ cap : 10.0 ,
184+ otb : 0 ,
185+ tbb : 5 ,
186+ inputPrice : 2.0 ,
187+ inputAmount : 4.99 ,
188+ wantPrice : pointy .Float64 (2.0 ),
189+ wantAmount : pointy .Float64 (4.99 ),
190+ wantTbbBase : 9.99 ,
191+ wantTbbQuote : 9.98 ,
192+ },
193+ {
194+ name : "2. otb = 0; projected = cap" ,
195+ cap : 10.0 ,
196+ otb : 0 ,
197+ tbb : 5 ,
198+ inputPrice : 2.0 ,
199+ inputAmount : 5.0 ,
200+ wantPrice : pointy .Float64 (2.0 ),
201+ wantAmount : pointy .Float64 (5 ),
202+ wantTbbBase : 10 ,
203+ wantTbbQuote : 10 ,
204+ },
205+ {
206+ name : "3. otb = 0; projected > cap, newAmount > 0" ,
207+ cap : 10.0 ,
208+ otb : 0 ,
209+ tbb : 5 ,
210+ inputPrice : 2.0 ,
211+ inputAmount : 5.01 ,
212+ wantPrice : pointy .Float64 (2.0 ),
213+ wantAmount : pointy .Float64 (5.0 ),
214+ wantTbbBase : 10 ,
215+ wantTbbQuote : 10 ,
216+ },
217+ {
218+ name : "4. otb = 0; projected > cap, newAmount = 0" ,
219+ cap : 10.0 ,
220+ otb : 0 ,
221+ tbb : 10 ,
222+ inputPrice : 2.0 ,
223+ inputAmount : 5.01 ,
224+ wantPrice : nil ,
225+ wantAmount : nil ,
226+ wantTbbBase : 10 ,
227+ wantTbbQuote : 0 ,
228+ },
229+ {
230+ name : "5. otb = 0; projected > cap, newAmount < 0" ,
231+ cap : 10.0 ,
232+ otb : 0 ,
233+ tbb : 11 ,
234+ inputPrice : 2.0 ,
235+ inputAmount : 5.01 ,
236+ wantPrice : nil ,
237+ wantAmount : nil ,
238+ wantTbbBase : 11 ,
239+ wantTbbQuote : 0 ,
240+ },
241+ {
242+ name : "6. tbb = 0; projected < cap" ,
243+ cap : 10.0 ,
244+ otb : 5 ,
245+ tbb : 0 ,
246+ inputPrice : 2.0 ,
247+ inputAmount : 4.99 ,
248+ wantPrice : pointy .Float64 (2.0 ),
249+ wantAmount : pointy .Float64 (4.99 ),
250+ wantTbbBase : 4.99 ,
251+ wantTbbQuote : 9.98 ,
252+ },
253+ {
254+ name : "7. tbb = 0; projected = cap" ,
255+ cap : 10.0 ,
256+ otb : 5 ,
257+ tbb : 0 ,
258+ inputPrice : 2.0 ,
259+ inputAmount : 5.0 ,
260+ wantPrice : pointy .Float64 (2.0 ),
261+ wantAmount : pointy .Float64 (5.0 ),
262+ wantTbbBase : 5 ,
263+ wantTbbQuote : 10 ,
264+ },
265+ {
266+ name : "8. tbb = 0; projected > cap, newAmount > 0" ,
267+ cap : 10.0 ,
268+ otb : 5 ,
269+ tbb : 0 ,
270+ inputPrice : 2.0 ,
271+ inputAmount : 6.0 ,
272+ wantPrice : pointy .Float64 (2.0 ),
273+ wantAmount : pointy .Float64 (5.0 ),
274+ wantTbbBase : 5 ,
275+ wantTbbQuote : 10 ,
276+ },
277+ {
278+ name : "9. tbb = 0; projected > cap, newAmount = 0" ,
279+ cap : 10.0 ,
280+ otb : 10 ,
281+ tbb : 0 ,
282+ inputPrice : 2.0 ,
283+ inputAmount : 6.0 ,
284+ wantPrice : nil ,
285+ wantAmount : nil ,
286+ wantTbbBase : 0 ,
287+ wantTbbQuote : 0 ,
288+ },
289+ {
290+ name : "10. tbb = 0; projected > cap, newAmount < 0" ,
291+ cap : 10.0 ,
292+ otb : 11 ,
293+ tbb : 0 ,
294+ inputPrice : 2.0 ,
295+ inputAmount : 6.0 ,
296+ wantPrice : nil ,
297+ wantAmount : nil ,
298+ wantTbbBase : 0 ,
299+ wantTbbQuote : 0 ,
300+ },
301+ {
302+ name : "11. otb = 0 && tbb = 0; projected < cap" ,
303+ cap : 10.0 ,
304+ otb : 0 ,
305+ tbb : 0 ,
306+ inputPrice : 2.0 ,
307+ inputAmount : 5.0 ,
308+ wantPrice : pointy .Float64 (2.0 ),
309+ wantAmount : pointy .Float64 (5.0 ),
310+ wantTbbBase : 5 ,
311+ wantTbbQuote : 10 ,
312+ },
313+ {
314+ name : "12. otb = 0 && tbb = 0; projected = cap" ,
315+ cap : 10.0 ,
316+ otb : 0 ,
317+ tbb : 0 ,
318+ inputPrice : 2.0 ,
319+ inputAmount : 10.0 ,
320+ wantPrice : pointy .Float64 (2.0 ),
321+ wantAmount : pointy .Float64 (10.0 ),
322+ wantTbbBase : 10 ,
323+ wantTbbQuote : 20 ,
324+ },
325+ {
326+ // note that in this case, newAmount >= 0, since newAmount = cap - otb - tbb
327+ name : "13. otb = 0 && tbb = 0; projected > cap, newAmount > 0" ,
328+ cap : 10.0 ,
329+ otb : 0 ,
330+ tbb : 0 ,
331+ inputPrice : 2.0 ,
332+ inputAmount : 15.0 ,
333+ wantPrice : pointy .Float64 (2.0 ),
334+ wantAmount : pointy .Float64 (10.0 ),
335+ wantTbbBase : 10 ,
336+ wantTbbQuote : 20 ,
337+ },
338+ {
339+ name : "14. otb = 0 && tbb = 0; projected > cap, newAmount = 0" ,
340+ cap : 0.0 ,
341+ otb : 0 ,
342+ tbb : 0 ,
343+ inputPrice : 2.0 ,
344+ inputAmount : 15.0 ,
345+ wantPrice : nil ,
346+ wantAmount : nil ,
347+ wantTbbBase : 0 ,
348+ wantTbbQuote : 0 ,
349+ },
350+ // it is not possible for otb = 0 && tbb = 0 and newAmount < 0, so skipping that case
351+ {
352+ name : "15. otb > 0 && tbb > 0; projected < cap" ,
353+ cap : 10.0 ,
354+ otb : 1 ,
355+ tbb : 1 ,
356+ inputPrice : 2.0 ,
357+ inputAmount : 5.0 ,
358+ wantPrice : pointy .Float64 (2.0 ),
359+ wantAmount : pointy .Float64 (5.0 ),
360+ wantTbbBase : 6 ,
361+ wantTbbQuote : 10 ,
362+ },
363+ {
364+ name : "16. otb > 0 && tbb > 0; projected = cap" ,
365+ cap : 10.0 ,
366+ otb : 2 ,
367+ tbb : 2 ,
368+ inputPrice : 2.0 ,
369+ inputAmount : 6.0 ,
370+ wantPrice : pointy .Float64 (2.0 ),
371+ wantAmount : pointy .Float64 (6.0 ),
372+ wantTbbBase : 8 ,
373+ wantTbbQuote : 12 ,
374+ },
375+ {
376+ name : "17. otb > 0 && tbb > 0; projected > cap, newAmount > 0" ,
377+ cap : 10.0 ,
378+ otb : 2 ,
379+ tbb : 2 ,
380+ inputPrice : 2.0 ,
381+ inputAmount : 7.0 ,
382+ wantPrice : pointy .Float64 (2.0 ),
383+ wantAmount : pointy .Float64 (6.0 ),
384+ wantTbbBase : 8 ,
385+ wantTbbQuote : 12 ,
386+ },
387+ {
388+ name : "18. otb > 0 && tbb > 0; projected > cap, newAmount = 0" ,
389+ cap : 10.0 ,
390+ otb : 5 ,
391+ tbb : 5 ,
392+ inputPrice : 2.0 ,
393+ inputAmount : 7.0 ,
394+ wantPrice : nil ,
395+ wantAmount : nil ,
396+ wantTbbBase : 5 ,
397+ wantTbbQuote : 0 ,
398+ },
399+ {
400+ name : "19. otb > 0 && tbb > 0; projected > cap, newAmount < 0" ,
401+ cap : 10.0 ,
402+ otb : 5 ,
403+ tbb : 5.1 ,
404+ inputPrice : 2.0 ,
405+ inputAmount : 7.0 ,
406+ wantPrice : nil ,
407+ wantAmount : nil ,
408+ wantTbbBase : 5.1 ,
409+ wantTbbQuote : 0 ,
410+ },
411+ }
412+ for _ , k := range testCases {
413+ // convert to common format accepted by runTestVolumeFilterFn
414+ // doing this explicitly here is easier to read rather than if we were to add "logic" to convert it to a standard format
415+ inputOp := makeSellOpAmtPrice (k .inputAmount , k .inputPrice )
416+
417+ var wantOp * txnbuild.ManageSellOffer
418+ if k .wantPrice != nil && k .wantAmount != nil {
419+ wantOp = makeSellOpAmtPrice (* k .wantAmount , * k .wantPrice )
420+ }
421+
422+ runTestVolumeFilterFn (
423+ t ,
424+ k .name ,
425+ volumeFilterModeExact ,
426+ queries .DailyVolumeActionSell ,
427+ pointy .Float64 (k .cap ), // base cap
428+ nil , // quote cap nil because this test is for the BaseCap
429+ pointy .Float64 (k .otb ), // baseOTB
430+ nil , // quoteOTB nil because this test is for the BaseCap
431+ pointy .Float64 (k .tbb ), // baseTBB
432+ pointy .Float64 (0 ), // quoteTBB (non-nil since it accumulates)
433+ inputOp ,
434+ wantOp ,
435+ pointy .Float64 (k .wantTbbBase ),
436+ pointy .Float64 (k .wantTbbQuote ),
437+ )
438+ }
439+ }
440+
169441func TestVolumeFilterFn_BaseCap_Ignore (t * testing.T ) {
170442 // We want to test the following 4 valid combinations of OTB and TBB values:
171443 // otb = 0
0 commit comments