base/common/balance.go

115 lines
2.5 KiB
Go
Raw Normal View History

2024-06-25 16:59:13 +00:00
package common
import (
"errors"
. "git.qtrade.icu/coin-quant/trademodel"
"github.com/shopspring/decimal"
)
var (
ErrNoBalance = errors.New("no balance")
Zero = decimal.NewFromInt(0)
)
type VBalance struct {
total decimal.Decimal
prevRoundTotal decimal.Decimal
position decimal.Decimal
feeTotal decimal.Decimal
// 开仓的总价值
longCost decimal.Decimal
shortCost decimal.Decimal
fee decimal.Decimal
prevFee decimal.Decimal
}
func NewVBalance() *VBalance {
b := new(VBalance)
b.total = decimal.NewFromFloat(100000)
b.prevRoundTotal = b.total
b.fee = decimal.NewFromFloat(0.00075)
return b
}
func (b *VBalance) Set(total float64) {
b.total = decimal.NewFromFloat(total)
b.prevRoundTotal = b.total
}
func (b *VBalance) SetFee(fee float64) {
b.fee = decimal.NewFromFloat(fee)
}
func (b *VBalance) Pos() (pos float64) {
pos, _ = b.position.Float64()
return
}
func (b *VBalance) Get() (total float64) {
// return b.total + b.costTotal
total, _ = b.total.Float64()
return
}
func (b *VBalance) GetFeeTotal() (fee float64) {
fee, _ = b.feeTotal.Float64()
return
}
func (b *VBalance) AvgOpenPrice() (price float64) {
switch b.position.Sign() {
case -1:
price, _ = b.shortCost.Div(b.position.Abs()).Float64()
case 0:
return
case 1:
price, _ = b.longCost.Div(b.position.Abs()).Float64()
}
return
}
func (b *VBalance) AddTrade(tr Trade) (profit, onceFee float64, err error) {
amount := decimal.NewFromFloat(tr.Amount).Abs()
// 仓位价值
cost := amount.Mul(decimal.NewFromFloat(tr.Price)).Abs()
fee := cost.Mul(b.fee)
onceFee, _ = fee.Float64()
costAll, _ := cost.Add(fee).Float64()
if tr.Action.IsOpen() && costAll >= b.Get() {
err = ErrNoBalance
return
}
// close/stop just return if no position
if b.position.Equal(Zero) && !tr.Action.IsOpen() {
return
}
if tr.Action.IsLong() {
b.position = b.position.Add(amount)
b.longCost = b.longCost.Add(cost)
} else {
b.position = b.position.Sub(amount)
b.shortCost = b.shortCost.Add(cost)
}
isPositionZero := b.position.Equal(Zero)
if tr.Action.IsOpen() && !isPositionZero {
b.total = b.total.Sub(cost).Sub(fee)
}
b.feeTotal = b.feeTotal.Add(fee)
// 计算盈利
if isPositionZero {
totalFee := fee.Add(b.prevFee)
prof := b.shortCost.Sub(b.longCost).Sub(totalFee)
b.total = b.prevRoundTotal.Add(prof)
profit, _ = prof.Float64()
b.longCost = decimal.NewFromInt(0)
b.shortCost = decimal.NewFromInt(0)
b.prevRoundTotal = b.total
b.prevFee = decimal.Zero
} else {
b.prevFee = b.prevFee.Add(fee)
}
return
}