Skip to content

Commit c2529ff

Browse files
committed
refactor: take max of two sides for isolated
1 parent 8aaa811 commit c2529ff

File tree

2 files changed

+83
-34
lines changed

2 files changed

+83
-34
lines changed

core/risk/margins_calculation.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -340,27 +340,28 @@ func calcOrderSideMarginIsolatedMode(currentPosition int64, orders []*OrderInfo,
340340
}
341341

342342
func CalculateRequiredMarginInIsolatedMode(sizePosition int64, averageEntryPrice, marketObservable num.Decimal, buyOrders, sellOrders []*OrderInfo, positionFactor, marginFactor num.Decimal) (num.Decimal, num.Decimal) {
343-
totalOrderNotional := num.DecimalZero()
343+
totalBuyOrderNotional, totalSellOrderNotional := num.DecimalZero(), num.DecimalZero()
344344
marketOrderAdjustedPositionNotional := averageEntryPrice.Copy().Mul(num.DecimalFromInt64(sizePosition))
345345

346346
// assume market orders fill immediately at marketObservable price
347347
for _, o := range buyOrders {
348348
if o.IsMarketOrder {
349349
marketOrderAdjustedPositionNotional = marketOrderAdjustedPositionNotional.Add(marketObservable.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
350350
} else {
351-
totalOrderNotional = totalOrderNotional.Add(o.Price.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
351+
totalBuyOrderNotional = totalBuyOrderNotional.Add(o.Price.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
352352
}
353353
}
354354
for _, o := range sellOrders {
355355
if o.IsMarketOrder {
356356
marketOrderAdjustedPositionNotional = marketOrderAdjustedPositionNotional.Sub(marketObservable.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
357357
} else {
358-
totalOrderNotional = totalOrderNotional.Add(o.Price.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
358+
totalSellOrderNotional = totalSellOrderNotional.Add(o.Price.Mul(num.DecimalFromInt64(int64(o.TrueRemaining))))
359359
}
360360
}
361361

362362
requiredPositionMargin := marketOrderAdjustedPositionNotional.Abs().Mul(marginFactor).Div(positionFactor)
363-
requiredOrderMargin := totalOrderNotional.Mul(marginFactor).Div(positionFactor)
363+
requiredBuyOrderMargin := totalBuyOrderNotional.Mul(marginFactor).Div(positionFactor)
364+
requiredSellOrderMargin := totalSellOrderNotional.Mul(marginFactor).Div(positionFactor)
364365

365-
return requiredPositionMargin, requiredOrderMargin
366+
return requiredPositionMargin, num.MaxD(requiredBuyOrderMargin, requiredSellOrderMargin)
366367
}

datanode/api/trading_data_v2_test.go

Lines changed: 77 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ func TestEstimatePosition(t *testing.T) {
133133
rfLong := num.DecimalFromFloat(0.1)
134134
rfShort := num.DecimalFromFloat(0.2)
135135

136-
markPrice := 123.456 * math.Pow10(marketDecimals)
137136
auctionEnd := int64(0)
138137
fundingPayment := 1234.56789
139138

@@ -197,7 +196,7 @@ func TestEstimatePosition(t *testing.T) {
197196
expectedLiquidationBestVolumeOnly string
198197
}{
199198
{
200-
markPrice: markPrice,
199+
markPrice: 123.456 * math.Pow10(marketDecimals),
201200
openVolume: 0,
202201
avgEntryPrice: 0,
203202
orders: []*v2.OrderInfo{
@@ -214,7 +213,7 @@ func TestEstimatePosition(t *testing.T) {
214213
marginMode: vega.MarginMode_MARGIN_MODE_CROSS_MARGIN,
215214
},
216215
{
217-
markPrice: markPrice,
216+
markPrice: 123.456 * math.Pow10(marketDecimals),
218217
openVolume: 0,
219218
avgEntryPrice: 0,
220219
orders: []*v2.OrderInfo{
@@ -232,7 +231,7 @@ func TestEstimatePosition(t *testing.T) {
232231
marginFactor: 0.1,
233232
},
234233
{
235-
markPrice: markPrice,
234+
markPrice: 123.456 * math.Pow10(marketDecimals),
236235
openVolume: int64(10 * math.Pow10(positionDecimalPlaces)),
237236
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
238237
orders: []*v2.OrderInfo{
@@ -249,7 +248,7 @@ func TestEstimatePosition(t *testing.T) {
249248
marginMode: vega.MarginMode_MARGIN_MODE_CROSS_MARGIN,
250249
},
251250
{
252-
markPrice: markPrice,
251+
markPrice: 123.456 * math.Pow10(marketDecimals),
253252
openVolume: int64(-10 * math.Pow10(positionDecimalPlaces)),
254253
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
255254
orders: []*v2.OrderInfo{
@@ -267,7 +266,7 @@ func TestEstimatePosition(t *testing.T) {
267266
marginFactor: 0.5,
268267
},
269268
{
270-
markPrice: markPrice,
269+
markPrice: 123.456 * math.Pow10(marketDecimals),
271270
openVolume: int64(-10 * math.Pow10(positionDecimalPlaces)),
272271
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
273272
orders: []*v2.OrderInfo{
@@ -290,7 +289,7 @@ func TestEstimatePosition(t *testing.T) {
290289
marginMode: vega.MarginMode_MARGIN_MODE_CROSS_MARGIN,
291290
},
292291
{
293-
markPrice: markPrice,
292+
markPrice: 123.456 * math.Pow10(marketDecimals),
294293
openVolume: int64(-10 * math.Pow10(positionDecimalPlaces)),
295294
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
296295
orders: []*v2.OrderInfo{
@@ -314,7 +313,7 @@ func TestEstimatePosition(t *testing.T) {
314313
marginFactor: 0.3,
315314
},
316315
{
317-
markPrice: markPrice,
316+
markPrice: 123.456 * math.Pow10(marketDecimals),
318317
openVolume: int64(10 * math.Pow10(positionDecimalPlaces)),
319318
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
320319
orders: []*v2.OrderInfo{
@@ -361,7 +360,7 @@ func TestEstimatePosition(t *testing.T) {
361360
marginMode: vega.MarginMode_MARGIN_MODE_CROSS_MARGIN,
362361
},
363362
{
364-
markPrice: markPrice,
363+
markPrice: 123.456 * math.Pow10(marketDecimals),
365364
openVolume: -int64(10 * math.Pow10(positionDecimalPlaces)),
366365
avgEntryPrice: 111.1 * math.Pow10(marketDecimals),
367366
orders: []*v2.OrderInfo{
@@ -409,13 +408,13 @@ func TestEstimatePosition(t *testing.T) {
409408
marginFactor: 0.1,
410409
},
411410
{
412-
markPrice: markPrice,
411+
markPrice: 123.456 * math.Pow10(marketDecimals),
413412
openVolume: 0,
414413
avgEntryPrice: 0,
415414
orders: []*v2.OrderInfo{
416415
{
417416
Side: entities.SideBuy,
418-
Price: fmt.Sprintf("%f", markPrice),
417+
Price: floatToStringWithDp(123.456, marketDecimals),
419418
Remaining: uint64(1 * math.Pow10(positionDecimalPlaces)),
420419
IsMarketOrder: false,
421420
},
@@ -428,7 +427,7 @@ func TestEstimatePosition(t *testing.T) {
428427
expectedCollIncBest: "3703680000",
429428
},
430429
{
431-
markPrice: markPrice,
430+
markPrice: 123.456 * math.Pow10(marketDecimals),
432431
openVolume: 0,
433432
avgEntryPrice: 0,
434433
orders: []*v2.OrderInfo{
@@ -447,9 +446,9 @@ func TestEstimatePosition(t *testing.T) {
447446
expectedCollIncBest: "3703680000",
448447
},
449448
{
450-
markPrice: markPrice,
449+
markPrice: 123.456 * math.Pow10(marketDecimals),
451450
openVolume: int64(1 * math.Pow10(positionDecimalPlaces)),
452-
avgEntryPrice: markPrice,
451+
avgEntryPrice: 123.456 * math.Pow10(marketDecimals),
453452
orders: []*v2.OrderInfo{},
454453
marginAccountBalance: 0,
455454
generalAccountBalance: 0,
@@ -459,13 +458,13 @@ func TestEstimatePosition(t *testing.T) {
459458
expectedCollIncBest: "3703680000",
460459
},
461460
{
462-
markPrice: markPrice,
461+
markPrice: 123.456 * math.Pow10(marketDecimals),
463462
openVolume: 0,
464463
avgEntryPrice: 0,
465464
orders: []*v2.OrderInfo{
466465
{
467466
Side: entities.SideSell,
468-
Price: fmt.Sprintf("%f", markPrice),
467+
Price: floatToStringWithDp(123.456, marketDecimals),
469468
Remaining: uint64(1 * math.Pow10(positionDecimalPlaces)),
470469
IsMarketOrder: false,
471470
},
@@ -478,7 +477,7 @@ func TestEstimatePosition(t *testing.T) {
478477
expectedCollIncBest: "3703680000",
479478
},
480479
{
481-
markPrice: markPrice,
480+
markPrice: 123.456 * math.Pow10(marketDecimals),
482481
openVolume: 0,
483482
avgEntryPrice: 0,
484483
orders: []*v2.OrderInfo{
@@ -497,9 +496,9 @@ func TestEstimatePosition(t *testing.T) {
497496
expectedCollIncBest: "3703680000",
498497
},
499498
{
500-
markPrice: markPrice,
499+
markPrice: 123.456 * math.Pow10(marketDecimals),
501500
openVolume: -int64(1 * math.Pow10(positionDecimalPlaces)),
502-
avgEntryPrice: markPrice,
501+
avgEntryPrice: 123.456 * math.Pow10(marketDecimals),
503502
orders: []*v2.OrderInfo{},
504503
marginAccountBalance: 0,
505504
generalAccountBalance: 0,
@@ -509,9 +508,9 @@ func TestEstimatePosition(t *testing.T) {
509508
expectedCollIncBest: "3703680000",
510509
},
511510
{
512-
markPrice: markPrice,
511+
markPrice: 123.456 * math.Pow10(marketDecimals),
513512
openVolume: -int64(1 * math.Pow10(positionDecimalPlaces)),
514-
avgEntryPrice: markPrice,
513+
avgEntryPrice: 123.456 * math.Pow10(marketDecimals),
515514
orders: []*v2.OrderInfo{
516515
{
517516
Side: entities.SideBuy,
@@ -528,9 +527,9 @@ func TestEstimatePosition(t *testing.T) {
528527
expectedCollIncBest: "0",
529528
},
530529
{
531-
markPrice: markPrice,
530+
markPrice: 123.456 * math.Pow10(marketDecimals),
532531
openVolume: int64(1 * math.Pow10(positionDecimalPlaces)),
533-
avgEntryPrice: markPrice,
532+
avgEntryPrice: 123.456 * math.Pow10(marketDecimals),
534533
orders: []*v2.OrderInfo{
535534
{
536535
Side: entities.SideSell,
@@ -558,6 +557,50 @@ func TestEstimatePosition(t *testing.T) {
558557
marginFactor: 0.01277,
559558
expectedLiquidationBestVolumeOnly: "6781300000",
560559
},
560+
{
561+
markPrice: 3225 * math.Pow10(marketDecimals),
562+
openVolume: 0,
563+
avgEntryPrice: 0,
564+
orders: []*v2.OrderInfo{
565+
{
566+
Side: entities.SideSell,
567+
Price: floatToStringWithDp(5000, marketDecimals),
568+
Remaining: uint64(1 * math.Pow10(positionDecimalPlaces)),
569+
IsMarketOrder: false,
570+
},
571+
},
572+
marginAccountBalance: 0,
573+
generalAccountBalance: 0,
574+
orderMarginAccountBalance: 0,
575+
marginMode: vega.MarginMode_MARGIN_MODE_ISOLATED_MARGIN,
576+
marginFactor: 0.1,
577+
expectedCollIncBest: "50000000000",
578+
},
579+
{
580+
markPrice: 3225 * math.Pow10(marketDecimals),
581+
openVolume: 0,
582+
avgEntryPrice: 0,
583+
orders: []*v2.OrderInfo{
584+
{
585+
Side: entities.SideSell,
586+
Price: floatToStringWithDp(5000, marketDecimals),
587+
Remaining: uint64(1 * math.Pow10(positionDecimalPlaces)),
588+
IsMarketOrder: false,
589+
},
590+
{
591+
Side: entities.SideBuy,
592+
Price: floatToStringWithDp(2500, marketDecimals),
593+
Remaining: uint64(2 * math.Pow10(positionDecimalPlaces)),
594+
IsMarketOrder: false,
595+
},
596+
},
597+
marginAccountBalance: 0,
598+
generalAccountBalance: 0,
599+
orderMarginAccountBalance: 50000000000,
600+
marginMode: vega.MarginMode_MARGIN_MODE_ISOLATED_MARGIN,
601+
marginFactor: 0.1,
602+
expectedCollIncBest: "0",
603+
},
561604
}
562605
for i, tc := range testCases {
563606
mktData := entities.MarketData{
@@ -676,8 +719,8 @@ func TestEstimatePosition(t *testing.T) {
676719
adjNotional := (tc.avgEntryPrice*priceFactor*float64(tc.openVolume)/math.Pow10(positionDecimalPlaces) + marketOrderNotional)
677720

678721
requiredPositionMargin := math.Abs(adjNotional) * tc.marginFactor
679-
requiredOrderMargin := getLimitOrderNotional(t, tc.orders, priceFactor, positionDecimalPlaces) * tc.marginFactor
680-
expectedCollIncBest = requiredPositionMargin + requiredOrderMargin - tc.marginAccountBalance - tc.orderMarginAccountBalance
722+
requiredBuyOrderMargin, requireSellOrderMargin := getLimitOrderNotionalScaledByMarginFactor(t, tc.orders, priceFactor, positionDecimalPlaces, tc.marginFactor)
723+
expectedCollIncBest = requiredPositionMargin + max(requiredBuyOrderMargin, requireSellOrderMargin) - tc.marginAccountBalance - tc.orderMarginAccountBalance
681724
expectedCollIncWorst = expectedCollIncBest
682725

683726
expectedPosMarginIncrease = max(0, requiredPositionMargin-tc.marginAccountBalance)
@@ -804,18 +847,23 @@ func (s *mockStream) Context() context.Context { return context.Backgroun
804847
func (s *mockStream) SendMsg(m interface{}) error { return nil }
805848
func (s *mockStream) RecvMsg(m interface{}) error { return nil }
806849

807-
func getLimitOrderNotional(t *testing.T, orders []*v2.OrderInfo, priceFactor float64, positionDecimals int) float64 {
850+
func getLimitOrderNotionalScaledByMarginFactor(t *testing.T, orders []*v2.OrderInfo, priceFactor float64, positionDecimals int, marginFactor float64) (float64, float64) {
808851
t.Helper()
809-
notional := 0.0
852+
buyNotional, sellNotional := 0.0, 0.0
810853
for _, o := range orders {
811854
if o.IsMarketOrder {
812855
continue
813856
}
814857
price, err := strconv.ParseFloat(o.Price, 64)
815858
require.NoError(t, err)
816-
notional += price * priceFactor * float64(o.Remaining) / math.Pow10(positionDecimals)
859+
if o.Side == entities.SideBuy {
860+
buyNotional += price * priceFactor * float64(o.Remaining) / math.Pow10(positionDecimals)
861+
}
862+
if o.Side == entities.SideSell {
863+
sellNotional += price * priceFactor * float64(o.Remaining) / math.Pow10(positionDecimals)
864+
}
817865
}
818-
return notional
866+
return buyNotional * marginFactor, sellNotional * marginFactor
819867
}
820868

821869
func getMarketOrderNotional(marketObservable float64, orders []*v2.OrderInfo, priceFactor float64, positionDecimals int) float64 {

0 commit comments

Comments
 (0)