base/common/balance_lever.go
2024-06-26 00:59:13 +08:00

123 lines
2.9 KiB
Go

package common
import (
. "git.qtrade.icu/coin-quant/trademodel"
"github.com/shopspring/decimal"
)
var (
numOne = decimal.NewFromInt(1)
)
type LeverBalance struct {
vBalance *VBalance
total decimal.Decimal
// 开仓的总价值
lever decimal.Decimal
}
func NewLeverBalance() *LeverBalance {
lb := new(LeverBalance)
lb.vBalance = NewVBalance()
lb.lever = decimal.NewFromFloat(1)
return lb
}
func (b *LeverBalance) Set(total float64) {
b.total = decimal.NewFromFloat(total)
vTotal, _ := b.total.Mul(b.lever).Float64()
b.vBalance.Set(vTotal)
}
func (b *LeverBalance) SetFee(fee float64) {
b.vBalance.SetFee(fee)
}
func (b *LeverBalance) SetLever(lever float64) {
b.lever = decimal.NewFromFloat(lever)
vTotal, _ := b.total.Mul(b.lever).Float64()
b.vBalance.Set(vTotal)
}
func (b *LeverBalance) Pos() (pos float64) {
return b.vBalance.Pos()
}
// func (b *LeverBalance) LiquidationPrice() (price float64, valid bool) {
// pos, _ := b.position.Float64()
// if pos == 0 {
// return
// }
// valid = true
// if pos > 0 {
// price, _ = b.openPrice.Sub(b.openPrice.Div(b.lever)).Float64()
// } else {
// price, _ = b.openPrice.Add(b.openPrice.Div(b.lever)).Float64()
// }
// return
// }
func (b *LeverBalance) CheckLiquidation(price float64) (liqPrice float64, isLiq bool) {
openPrice := decimal.NewFromFloat(b.vBalance.AvgOpenPrice())
fee := b.vBalance.fee
switch b.vBalance.position.Sign() {
// <0
case -1:
// liqPrice + liqPrice * fee = openPrice + openPrice/lever
// liqPrice *(1 + fee) = openPrice * (1 + 1/lever)
// liqPrice = (openPrice * (1 + 1/lever))/(1-fee)
liqPrice, _ = openPrice.Add(openPrice.Div(b.lever)).Div(numOne.Add(fee)).Float64()
if price >= liqPrice {
isLiq = true
}
// =0
case 0:
return
// >0
case 1:
// liqPrice - liqPrice * fee = openPrice - openPrice/lever
// (1-fee) * liqPrice = openPrice * (1 - 1/lever)
// liqPrice = (openPrice * (1 - 1/lever))/(1-fee)
liqPrice, _ = openPrice.Sub(openPrice.Div(b.lever)).Div(numOne.Sub(fee)).Float64()
if price <= liqPrice {
isLiq = true
}
}
return
}
func (b *LeverBalance) Get() (total float64) {
total, _ = b.total.Float64()
return
}
func (b *LeverBalance) GetFeeTotal() float64 {
return b.vBalance.GetFeeTotal()
}
func (b *LeverBalance) AddTrade(tr Trade) (profit, onceFee float64, err error) {
if tr.Action.IsOpen() {
// check balance enough when open order
amount := decimal.NewFromFloat(tr.Amount).Abs()
cost := amount.Mul(decimal.NewFromFloat(tr.Price)).Abs()
fee := cost.Mul(b.vBalance.fee)
onceCost := cost.Div(b.lever).Add(fee)
if b.total.LessThan(onceCost) {
err = ErrNoBalance
return
}
} else {
liqPrice, isLiq := b.CheckLiquidation(tr.Price)
if isLiq {
tr.Price = liqPrice
}
}
profit, onceFee, err = b.vBalance.AddTrade(tr)
if err != nil {
return
}
b.total = b.total.Add(decimal.NewFromFloat(profit)).Sub(decimal.NewFromFloat(onceFee))
return
}