@@ -14,7 +14,9 @@ import (
14
14
15
15
var defaultLeverage = fixedpoint .NewFromInt (3 )
16
16
17
- var maxLeverage = fixedpoint .NewFromInt (10 )
17
+ var maxIsolatedMarginLeverage = fixedpoint .NewFromInt (10 )
18
+
19
+ var maxCrossMarginLeverage = fixedpoint .NewFromInt (3 )
18
20
19
21
type AccountValueCalculator struct {
20
22
session * ExchangeSession
@@ -128,13 +130,14 @@ func (c *AccountValueCalculator) NetValue(ctx context.Context) (fixedpoint.Value
128
130
continue
129
131
}
130
132
131
- symbol := b .Currency + c .quoteCurrency
132
- price , ok := c .prices [symbol ]
133
- if ! ok {
134
- continue
133
+ symbol := b .Currency + c .quoteCurrency // for BTC/USDT, ETH/USDT pairs
134
+ symbolReverse := c .quoteCurrency + b .Currency // for USDT/USDC or USDT/TWD pairs
135
+ if price , ok := c .prices [symbol ]; ok {
136
+ accountValue = accountValue .Add (b .Net ().Mul (price ))
137
+ } else if priceReverse , ok2 := c .prices [symbolReverse ]; ok2 {
138
+ price2 := one .Div (priceReverse )
139
+ accountValue = accountValue .Add (b .Net ().Mul (price2 ))
135
140
}
136
-
137
- accountValue = accountValue .Add (b .Net ().Mul (price ))
138
141
}
139
142
140
143
return accountValue , nil
@@ -186,60 +189,116 @@ func (c *AccountValueCalculator) MarginLevel(ctx context.Context) (fixedpoint.Va
186
189
return marginLevel , nil
187
190
}
188
191
192
+ func aggregateUsdValue (balances types.BalanceMap ) fixedpoint.Value {
193
+ totalUsdValue := fixedpoint .Zero
194
+ // get all usd value if any
195
+ for currency , balance := range balances {
196
+ if types .IsUSDFiatCurrency (currency ) {
197
+ totalUsdValue = totalUsdValue .Add (balance .Net ())
198
+ }
199
+ }
200
+
201
+ return totalUsdValue
202
+ }
203
+
204
+ func usdFiatBalances (balances types.BalanceMap ) (fiats types.BalanceMap , rest types.BalanceMap ) {
205
+ rest = make (types.BalanceMap )
206
+ fiats = make (types.BalanceMap )
207
+ for currency , balance := range balances {
208
+ if types .IsUSDFiatCurrency (currency ) {
209
+ fiats [currency ] = balance
210
+ } else {
211
+ rest [currency ] = balance
212
+ }
213
+ }
214
+
215
+ return fiats , rest
216
+ }
217
+
189
218
func CalculateBaseQuantity (session * ExchangeSession , market types.Market , price , quantity , leverage fixedpoint.Value ) (fixedpoint.Value , error ) {
190
219
// default leverage guard
191
220
if leverage .IsZero () {
192
221
leverage = defaultLeverage
193
222
}
194
223
195
- baseBalance , _ := session .Account .Balance (market .BaseCurrency )
224
+ baseBalance , hasBaseBalance := session .Account .Balance (market .BaseCurrency )
196
225
quoteBalance , _ := session .Account .Balance (market .QuoteCurrency )
226
+ balances := session .Account .Balances ()
197
227
198
228
usingLeverage := session .Margin || session .IsolatedMargin || session .Futures || session .IsolatedFutures
199
229
if ! usingLeverage {
200
230
// For spot, we simply sell the base quoteCurrency
201
- balance , hasBalance := session .Account .Balance (market .BaseCurrency )
202
- if hasBalance {
231
+ if hasBaseBalance {
203
232
if quantity .IsZero () {
204
- log .Warnf ("sell quantity is not set, using all available base balance: %v" , balance )
205
- if ! balance .Available .IsZero () {
206
- return balance .Available , nil
233
+ log .Warnf ("sell quantity is not set, using all available base balance: %v" , baseBalance )
234
+ if ! baseBalance .Available .IsZero () {
235
+ return baseBalance .Available , nil
207
236
}
208
237
} else {
209
- return fixedpoint .Min (quantity , balance .Available ), nil
238
+ return fixedpoint .Min (quantity , baseBalance .Available ), nil
210
239
}
211
240
}
212
241
213
- return quantity , fmt .Errorf ("quantity is zero, can not submit sell order, please check your quantity settings" )
242
+ return quantity , fmt .Errorf ("quantity is zero, can not submit sell order, please check your quantity settings, your account balances: %+v" , balances )
243
+ }
244
+
245
+ usdBalances , restBalances := usdFiatBalances (balances )
246
+
247
+ // for isolated margin we can calculate from these two pair
248
+ totalUsdValue := fixedpoint .Zero
249
+ if len (restBalances ) == 1 && types .IsUSDFiatCurrency (market .QuoteCurrency ) {
250
+ totalUsdValue = aggregateUsdValue (balances )
251
+ } else if len (restBalances ) > 1 {
252
+ accountValue := NewAccountValueCalculator (session , "USDT" )
253
+ netValue , err := accountValue .NetValue (context .Background ())
254
+ if err != nil {
255
+ return quantity , err
256
+ }
257
+
258
+ totalUsdValue = netValue
259
+ } else {
260
+ // TODO: translate quote currency like BTC of ETH/BTC to usd value
261
+ totalUsdValue = aggregateUsdValue (usdBalances )
214
262
}
215
263
216
264
if ! quantity .IsZero () {
217
265
return quantity , nil
218
266
}
219
267
268
+ if price .IsZero () {
269
+ return quantity , fmt .Errorf ("%s price can not be zero" , market .Symbol )
270
+ }
271
+
220
272
// using leverage -- starts from here
221
273
log .Infof ("calculating available leveraged base quantity: base balance = %+v, quote balance = %+v" , baseBalance , quoteBalance )
222
274
223
275
// calculate the quantity automatically
224
276
if session .Margin || session .IsolatedMargin {
225
277
baseBalanceValue := baseBalance .Net ().Mul (price )
226
- accountValue := baseBalanceValue .Add (quoteBalance . Net () )
278
+ accountUsdValue := baseBalanceValue .Add (totalUsdValue )
227
279
228
280
// avoid using all account value since there will be some trade loss for interests and the fee
229
- accountValue = accountValue .Mul (one .Sub (fixedpoint .NewFromFloat (0.01 )))
281
+ accountUsdValue = accountUsdValue .Mul (one .Sub (fixedpoint .NewFromFloat (0.01 )))
230
282
231
- log .Infof ("calculated account value %f %s" , accountValue .Float64 (), market .QuoteCurrency )
283
+ log .Infof ("calculated account usd value %f %s" , accountUsdValue .Float64 (), market .QuoteCurrency )
232
284
285
+ originLeverage := leverage
233
286
if session .IsolatedMargin {
234
- originLeverage := leverage
235
- leverage = fixedpoint .Min (leverage , maxLeverage )
236
- log .Infof ("using isolated margin, maxLeverage=10 originalLeverage=%f currentLeverage=%f" ,
287
+ leverage = fixedpoint .Min (leverage , maxIsolatedMarginLeverage )
288
+ log .Infof ("using isolated margin, maxLeverage=%f originalLeverage=%f currentLeverage=%f" ,
289
+ maxIsolatedMarginLeverage .Float64 (),
290
+ originLeverage .Float64 (),
291
+ leverage .Float64 ())
292
+ } else {
293
+ leverage = fixedpoint .Min (leverage , maxCrossMarginLeverage )
294
+ log .Infof ("using cross margin, maxLeverage=%f originalLeverage=%f currentLeverage=%f" ,
295
+ maxCrossMarginLeverage .Float64 (),
237
296
originLeverage .Float64 (),
238
297
leverage .Float64 ())
239
298
}
240
299
241
300
// spot margin use the equity value, so we use the total quote balance here
242
- maxPosition := risk .CalculateMaxPosition (price , accountValue , leverage )
301
+ maxPosition := risk .CalculateMaxPosition (price , accountUsdValue , leverage )
243
302
debt := baseBalance .Debt ()
244
303
maxQuantity := maxPosition .Sub (debt )
245
304
@@ -248,7 +307,7 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
248
307
maxPosition .Float64 (),
249
308
debt .Float64 (),
250
309
price .Float64 (),
251
- accountValue .Float64 (),
310
+ accountUsdValue .Float64 (),
252
311
market .QuoteCurrency ,
253
312
leverage .Float64 ())
254
313
@@ -257,10 +316,10 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
257
316
258
317
if session .Futures || session .IsolatedFutures {
259
318
// TODO: get mark price here
260
- maxPositionQuantity := risk .CalculateMaxPosition (price , quoteBalance . Available , leverage )
319
+ maxPositionQuantity := risk .CalculateMaxPosition (price , totalUsdValue , leverage )
261
320
requiredPositionCost := risk .CalculatePositionCost (price , price , maxPositionQuantity , leverage , types .SideTypeSell )
262
321
if quoteBalance .Available .Compare (requiredPositionCost ) < 0 {
263
- return maxPositionQuantity , fmt .Errorf ("available margin %f %s is not enough, can not submit order" , quoteBalance . Available . Float64 (), market . QuoteCurrency )
322
+ return maxPositionQuantity , fmt .Errorf ("margin total usd value %f is not enough, can not submit order" , totalUsdValue . Float64 ())
264
323
}
265
324
266
325
return maxPositionQuantity , nil
@@ -276,15 +335,30 @@ func CalculateQuoteQuantity(ctx context.Context, session *ExchangeSession, quote
276
335
}
277
336
278
337
quoteBalance , _ := session .Account .Balance (quoteCurrency )
279
- accountValue := NewAccountValueCalculator (session , quoteCurrency )
280
338
281
339
usingLeverage := session .Margin || session .IsolatedMargin || session .Futures || session .IsolatedFutures
282
340
if ! usingLeverage {
283
341
// For spot, we simply return the quote balance
284
342
return quoteBalance .Available .Mul (fixedpoint .Min (leverage , fixedpoint .One )), nil
285
343
}
286
344
345
+ originLeverage := leverage
346
+ if session .IsolatedMargin {
347
+ leverage = fixedpoint .Min (leverage , maxIsolatedMarginLeverage )
348
+ log .Infof ("using isolated margin, maxLeverage=%f originalLeverage=%f currentLeverage=%f" ,
349
+ maxIsolatedMarginLeverage .Float64 (),
350
+ originLeverage .Float64 (),
351
+ leverage .Float64 ())
352
+ } else {
353
+ leverage = fixedpoint .Min (leverage , maxCrossMarginLeverage )
354
+ log .Infof ("using cross margin, maxLeverage=%f originalLeverage=%f currentLeverage=%f" ,
355
+ maxCrossMarginLeverage .Float64 (),
356
+ originLeverage .Float64 (),
357
+ leverage .Float64 ())
358
+ }
359
+
287
360
// using leverage -- starts from here
361
+ accountValue := NewAccountValueCalculator (session , quoteCurrency )
288
362
availableQuote , err := accountValue .AvailableQuote (ctx )
289
363
if err != nil {
290
364
log .WithError (err ).Errorf ("can not update available quote" )
0 commit comments