feat.
This commit is contained in:
@@ -19,6 +19,14 @@ BinanceApiSecret: "YqTpRybnBWllS0fA1yk0T1MEx0RxRazc2bH2iZuPEI8QJKesUueq3saCDdDj7
|
||||
SpotWatchList:
|
||||
- Symbol: BTCUSDT
|
||||
OrderQtyUsdt: 10
|
||||
- Symbol: ETHUSDT
|
||||
OrderQtyUsdt: 10
|
||||
- Symbol: SOLUSDT
|
||||
OrderQtyUsdt: 10
|
||||
- Symbol: ADAUSDT
|
||||
OrderQtyUsdt: 10
|
||||
- Symbol: XRPUSDT
|
||||
OrderQtyUsdt: 10
|
||||
|
||||
# 日志配置
|
||||
Log:
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"git.apinb.com/quant/coin/internal/impl"
|
||||
"git.apinb.com/quant/coin/internal/models"
|
||||
"github.com/adshao/go-binance/v2"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -15,7 +16,7 @@ var (
|
||||
|
||||
// trySpotRallySell 跟踪止盈:浮盈≥profitArmPct 后记录阶段峰值 PnL;继续上涨则刷新峰值;
|
||||
// 当未实现盈亏较峰值回落≥gridStartPct 时市价卖出(数量来自 RefreshAccount 写入的 account[sym])。
|
||||
func trySpotRallySell(pos *models.SpotPosition, pnlPct float64) error {
|
||||
func trySpotRallySell(pos *models.SpotPosition, pnlPct float64, price float64) error {
|
||||
if val, ok := CloseCache[pos.Symbol]; !ok || val == 0 {
|
||||
CloseCache[pos.Symbol] = pnlPct
|
||||
return nil
|
||||
@@ -45,9 +46,9 @@ func trySpotRallySell(pos *models.SpotPosition, pnlPct float64) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
orderPrice, err := strconv.ParseFloat(order.Price, 64)
|
||||
|
||||
pos.SellQuantity += parseFloat(order.ExecutedQuantity)
|
||||
pos.SellPrice = (pos.SellQuantity*pos.SellPrice + parseFloat(order.ExecutedQuantity)*orderPrice) / pos.SellQuantity
|
||||
pos.SellPrice = decimal.NewFromFloat(price).Round(2).InexactFloat64()
|
||||
pos.Status = 1
|
||||
impl.DBService.Save(pos)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"git.apinb.com/quant/coin/internal/impl"
|
||||
"git.apinb.com/quant/coin/internal/models"
|
||||
binance "github.com/adshao/go-binance/v2"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -31,7 +32,7 @@ func CreateNewSpotPosition(symbol string) error {
|
||||
}
|
||||
}
|
||||
|
||||
qty, err := CalculateQty(symbol)
|
||||
qty, price, err := CalculateQty(symbol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,7 +51,7 @@ func CreateNewSpotPosition(symbol string) error {
|
||||
position := &models.SpotPosition{
|
||||
Symbol: symbol,
|
||||
BuyQuantity: parseFloat(order.ExecutedQuantity),
|
||||
BuyCostPrice: parseFloat(order.Price),
|
||||
BuyCostPrice: decimal.NewFromFloat(price).Round(2).InexactFloat64(),
|
||||
}
|
||||
|
||||
if err := impl.DBService.Create(position).Error; err != nil {
|
||||
@@ -95,9 +96,9 @@ func AddSpotPosition(pos *models.SpotPosition, pnlPct, price float64) error {
|
||||
}
|
||||
|
||||
// 更新数据库
|
||||
orderPrice, err := strconv.ParseFloat(order.Price, 64)
|
||||
price = decimal.NewFromFloat(price).Round(2).InexactFloat64()
|
||||
pos.BuyQuantity += parseFloat(order.ExecutedQuantity)
|
||||
pos.BuyCostPrice = (pos.BuyQuantity*pos.BuyCostPrice + parseFloat(order.ExecutedQuantity)*orderPrice) / pos.BuyQuantity
|
||||
pos.BuyCostPrice = decimal.NewFromFloat((pos.BuyCostPrice + price) / 2).Round(2).InexactFloat64()
|
||||
pos.DipAddsDone++
|
||||
impl.DBService.Save(pos)
|
||||
|
||||
@@ -109,34 +110,34 @@ func AddSpotPosition(pos *models.SpotPosition, pnlPct, price float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func CalculateQty(symbol string) (string, error) {
|
||||
func CalculateQty(symbol string) (string, float64, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second)
|
||||
defer cancel()
|
||||
prices, err := BinanceClient.NewListPricesService().Symbol(symbol).Do(ctx)
|
||||
if err != nil || len(prices) == 0 {
|
||||
return "", err
|
||||
return "", 0, err
|
||||
}
|
||||
price, err := strconv.ParseFloat(prices[0].Price, 64)
|
||||
if err != nil || price <= 0 {
|
||||
return "", errors.New("现价无效")
|
||||
return "", 0, errors.New("现价无效")
|
||||
}
|
||||
cfg := getSymbolInfo(symbol)
|
||||
if cfg == nil {
|
||||
return "", errors.New("交易对不存在")
|
||||
return "", 0, errors.New("交易对不存在")
|
||||
}
|
||||
want := InvestUsdt[cfg.Symbol] / price
|
||||
lot := cfg.LotSizeFilter()
|
||||
if lot == nil {
|
||||
return "", errors.New("交易对无 LOT_SIZE 规则")
|
||||
return "", 0, errors.New("交易对无 LOT_SIZE 规则")
|
||||
}
|
||||
qtyStr, ok, err := formatQtyToLotStep(want, lot.StepSize)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return "", 0, err
|
||||
}
|
||||
if !ok {
|
||||
return "", errors.New("数量不足")
|
||||
return "", 0, errors.New("数量不足")
|
||||
}
|
||||
return qtyStr, nil
|
||||
return qtyStr, price, nil
|
||||
}
|
||||
|
||||
func CalculateQtyByPrice(symbol string, price float64) string {
|
||||
|
||||
@@ -61,7 +61,7 @@ func Run(ctx context.Context) error {
|
||||
pnl := spotUnrealizedPnLPct(pos, price)
|
||||
|
||||
if pnl >= profitArmPct {
|
||||
trySpotRallySell(pos, pnl)
|
||||
trySpotRallySell(pos, pnl, price)
|
||||
continue
|
||||
}
|
||||
if pos.DipAddsDone >= maxDipAdds {
|
||||
|
||||
Reference in New Issue
Block a user