2025-02-06 11:14:33 +08:00
package binanceservice
import (
"context"
"errors"
"fmt"
DbModels "go-admin/app/admin/models"
"go-admin/app/admin/service/dto"
"go-admin/common/const/rediskey"
commondto "go-admin/common/dto"
"go-admin/common/global"
"go-admin/common/helper"
"go-admin/models"
"go-admin/models/spot"
"go-admin/pkg/httputils"
"go-admin/pkg/utility"
"strings"
"time"
"github.com/shopspring/decimal"
"gorm.io/gorm"
"github.com/bytedance/sonic"
log "github.com/go-admin-team/go-admin-core/logger"
)
const (
binanceRestApi = "https://api.binance.com"
)
var ErrorMaps = map [ float64 ] string {
- 2021 : "订单已拒绝。请调整触发价并重新下订单。 对于买入/做多,止盈订单触发价应低于市场价,止损订单的触发价应高于市场价。卖出/做空则与之相反" ,
- 4164 : "下单失败。少于最小下单金额" ,
- 4061 : "持仓方向需要设置为单向持仓。" ,
- 2019 : "保证金不足" ,
- 1111 : "金额设置错误。精度错误" ,
- 1021 : "请求的时间戳在recvWindow之外" ,
- 2011 : "该交易对没有订单" ,
- 2010 : "账户余额不足" ,
}
type SpotRestApi struct {
}
/ *
获取 现货交易 - 规范信息
- return @ info 规范信息
- return @ err 错误信息
* /
func ( e SpotRestApi ) GetExchangeInfo ( ) ( symbols [ ] spot . Symbol , err error ) {
url := fmt . Sprintf ( "%s%s?permissions=SPOT" , binanceRestApi , "/api/v3/exchangeInfo" )
mapData , err := httputils . NewHttpRequestWithFasthttp ( "GET" , url , "" , map [ string ] string { } )
if err != nil {
return
}
if len ( mapData ) == 0 {
err = errors . New ( "获取现货交易-规范信息数量为空" )
return
}
var info spot . ExchangeInfo
err = sonic . Unmarshal ( mapData , & info )
if err == nil {
return info . Symbols , nil
}
return
}
/ *
获取现货24h行情变更
* /
func ( e SpotRestApi ) GetSpotTicker24h ( tradeSet * map [ string ] models . TradeSet ) ( deleteSymbols [ ] string , err error ) {
tickerApi := fmt . Sprintf ( "%s%s" , binanceRestApi , "/api/v3/ticker/24hr" )
mapData , err := httputils . NewHttpRequestWithFasthttp ( "GET" , tickerApi , "" , map [ string ] string { } )
if err != nil {
return [ ] string { } , err
}
deleteSymbols = make ( [ ] string , 0 )
if len ( mapData ) == 0 {
return deleteSymbols , errors . New ( "获取交易对失败,或数量为空" )
}
tickers := make ( [ ] spot . SpotTicker24h , 0 )
err = sonic . Unmarshal ( [ ] byte ( mapData ) , & tickers )
if err != nil {
log . Error ( "反序列化json失败" , err )
}
for _ , item := range tickers {
2025-02-08 14:05:57 +08:00
key := fmt . Sprintf ( global . TICKER_SPOT , global . EXCHANGE_BINANCE , item . Symbol )
2025-02-06 11:14:33 +08:00
symbol , exits := ( * tradeSet ) [ item . Symbol ]
if ! exits {
helper . DefaultRedis . DeleteString ( key )
continue
}
symbol . OpenPrice = utility . StringAsFloat ( item . OpenPrice )
symbol . PriceChange = utility . StringAsFloat ( item . PriceChangePercent )
symbol . LowPrice = item . LowPrice
symbol . HighPrice = item . HighPrice
symbol . Volume = item . Volume
symbol . QuoteVolume = item . QuoteVolume
symbol . LastPrice = item . LastPrice
val , err := sonic . Marshal ( symbol )
if ! strings . HasSuffix ( item . Symbol , symbol . Currency ) || item . Count <= 0 || utility . StringToFloat64 ( item . QuoteVolume ) <= 0 {
helper . DefaultRedis . DeleteString ( key )
deleteSymbols = append ( deleteSymbols , item . Symbol )
continue
}
if err != nil {
log . Error ( "序列化失败" , item . Symbol )
continue
}
err = helper . DefaultRedis . SetString ( key , string ( val ) )
if err != nil {
log . Error ( "缓存交易对失败|" , item . Symbol , err )
}
helper . DefaultRedis . AddSortSet ( global . COIN_PRICE_CHANGE , symbol . PriceChange , symbol . Coin )
}
return deleteSymbols , nil
}
/ *
获取单个交易对24h行情
- @ symbol 交易对
- @ data 结果
* /
func ( e SpotRestApi ) GetSpotTicker24 ( symbol string , data * models . Ticker24 , tradeSet * models . TradeSet ) error {
2025-02-08 14:05:57 +08:00
key := fmt . Sprintf ( global . TICKER_SPOT , global . EXCHANGE_BINANCE , symbol )
2025-02-06 11:14:33 +08:00
val , err := helper . DefaultRedis . GetString ( key )
if err != nil {
return err
}
err = sonic . Unmarshal ( [ ] byte ( val ) , tradeSet )
if err != nil {
return err
}
if tradeSet . Coin != "" {
data . HighPrice = tradeSet . HighPrice
data . ChangePercent = fmt . Sprintf ( "%g" , tradeSet . PriceChange )
data . LastPrice = tradeSet . LastPrice
data . LowPrice = tradeSet . LowPrice
data . OpenPrice = fmt . Sprintf ( "%g" , tradeSet . OpenPrice )
data . QuoteVolume = tradeSet . QuoteVolume
data . Volume = tradeSet . Volume
}
return nil
}
type Ticker struct {
Symbol string ` json:"symbol" `
Price string ` json:"price" `
}
func ( e SpotRestApi ) Ticker ( ) {
tickerApi := fmt . Sprintf ( "%s%s" , binanceRestApi , "/api/v3/ticker/price" )
mapData , _ := httputils . NewHttpRequestWithFasthttp ( "GET" , tickerApi , "" , map [ string ] string { } )
//sonic.Unmarshal(mapData, &tickerData)
helper . DefaultRedis . SetString ( rediskey . SpotSymbolTicker , string ( mapData ) )
}
// OrderPlace 现货下单
func ( e SpotRestApi ) OrderPlace ( orm * gorm . DB , params OrderPlacementService ) error {
if orm == nil {
return errors . New ( "数据库实例为空" )
}
err2 := params . CheckParams ( )
if err2 != nil {
return err2
}
paramsMaps := map [ string ] string {
"symbol" : params . Symbol ,
"side" : params . Side ,
"quantity" : params . Quantity . String ( ) ,
"type" : params . Type ,
"newClientOrderId" : params . NewClientOrderId ,
}
if strings . ToUpper ( params . Type ) != "MARKET" { //市价
paramsMaps [ "price" ] = params . Price . String ( )
paramsMaps [ "timeInForce" ] = "GTC"
if strings . ToUpper ( params . Type ) == "TAKE_PROFIT_LIMIT" || strings . ToUpper ( params . Type ) == "STOP_LOSS_LIMIT" {
paramsMaps [ "stopPrice" ] = params . StopPrice . String ( )
}
}
var apiUserInfo DbModels . LineApiUser
err := orm . Model ( & DbModels . LineApiUser { } ) . Where ( "id = ?" , params . ApiId ) . Find ( & apiUserInfo ) . Error
if err != nil && ! errors . Is ( err , gorm . ErrRecordNotFound ) {
log . Errorf ( "api用户出错 err: %+v" , err )
return err
}
client := GetClient ( & apiUserInfo )
resp , _ , err := client . SendSpotAuth ( "/api/v3/order" , "POST" , paramsMaps )
if err != nil {
dataMap := make ( map [ string ] interface { } )
if err . Error ( ) != "" {
if err := sonic . Unmarshal ( [ ] byte ( err . Error ( ) ) , & dataMap ) ; err != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 下订单失败:%+v" , apiUserInfo . Id , params . Symbol , err . Error ( ) )
}
}
code , ok := dataMap [ "code" ]
if ok {
errContent := ErrorMaps [ code . ( float64 ) ]
paramsVal , _ := sonic . MarshalString ( paramsMaps )
log . Error ( "api_id:" , utility . IntToString ( apiUserInfo . Id ) , " 交易对:" , params . Symbol , " 下单参数:" , paramsVal )
if errContent == "" {
errContent , _ = dataMap [ "msg" ] . ( string )
}
return fmt . Errorf ( "api_id:%d 交易对:%s 下订单失败:%s" , apiUserInfo . Id , params . Symbol , errContent )
}
if strings . Contains ( err . Error ( ) , "Unknown order sent." ) {
return fmt . Errorf ( "api_id:%d 交易对:%s 下单失败:%+v" , apiUserInfo . Id , params . Symbol , ErrorMaps [ - 2011 ] )
}
return fmt . Errorf ( "api_id:%d 交易对:%s 下单失败:%v" , apiUserInfo . Id , params . Symbol , err )
}
var dataMap map [ string ] interface { }
if err := sonic . Unmarshal ( resp , & dataMap ) ; err != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 下单失败:%+v" , apiUserInfo . Id , params . Symbol , err . Error ( ) )
}
return nil
}
// CancelOpenOrders 撤销单一交易对下所有挂单 包括了来自订单列表的挂单
func ( e SpotRestApi ) CancelOpenOrders ( orm * gorm . DB , req CancelOpenOrdersReq ) error {
if orm == nil {
return errors . New ( "数据库实例为空" )
}
err := req . CheckParams ( )
if err != nil {
return err
}
params := map [ string ] string {
"symbol" : req . Symbol ,
}
var apiUserInfo DbModels . LineApiUser
err = orm . Model ( & DbModels . LineApiUser { } ) . Where ( "id = ?" , req . ApiId ) . Find ( & apiUserInfo ) . Error
if err != nil && ! errors . Is ( err , gorm . ErrRecordNotFound ) {
return fmt . Errorf ( "api_id:%d 交易对:%s api用户出错:%+v" , apiUserInfo . Id , req . Symbol , err )
}
var client * helper . BinanceClient
if apiUserInfo . UserPass == "" {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "" , apiUserInfo . IpAddress )
} else {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "socks5" , apiUserInfo . UserPass + "@" + apiUserInfo . IpAddress )
}
_ , _ , err = client . SendSpotAuth ( "/api/v3/openOrders" , "DELETE" , params )
if err != nil {
dataMap := make ( map [ string ] interface { } )
if err . Error ( ) != "" {
if err := sonic . Unmarshal ( [ ] byte ( err . Error ( ) ) , & dataMap ) ; err != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , req . Symbol , err . Error ( ) )
}
}
code , ok := dataMap [ "code" ]
if ok {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%s" , apiUserInfo . Id , req . Symbol , ErrorMaps [ code . ( float64 ) ] )
}
if strings . Contains ( err . Error ( ) , "Unknown order sent." ) {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , req . Symbol , ErrorMaps [ - 2011 ] )
}
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , req . Symbol , err . Error ( ) )
}
return nil
}
// CancelOpenOrderByOrderSn 通过单一订单号取消委托
func ( e SpotRestApi ) CancelOpenOrderByOrderSn ( apiUserInfo DbModels . LineApiUser , symbol string , newClientOrderId string ) error {
params := map [ string ] string {
"symbol" : symbol ,
"origClientOrderId" : newClientOrderId ,
"recvWindow" : "10000" ,
}
var client * helper . BinanceClient
if apiUserInfo . UserPass == "" {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "" , apiUserInfo . IpAddress )
} else {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "socks5" , apiUserInfo . UserPass + "@" + apiUserInfo . IpAddress )
}
2025-02-08 14:05:57 +08:00
_ , code , err := client . SendSpotAuth ( "/api/v3/order" , "DELETE" , params )
2025-02-06 11:14:33 +08:00
if err != nil || code != 200 {
log . Error ( "取消现货委托失败 参数:" , params )
log . Error ( "取消现货委托失败 code:" , code )
log . Error ( "取消现货委托失败 err:" , err )
dataMap := make ( map [ string ] interface { } )
if err . Error ( ) != "" {
if err := sonic . Unmarshal ( [ ] byte ( err . Error ( ) ) , & dataMap ) ; err != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , symbol , err . Error ( ) )
}
}
code , ok := dataMap [ "code" ]
if ok {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%s" , apiUserInfo . Id , symbol , ErrorMaps [ code . ( float64 ) ] )
}
if strings . Contains ( err . Error ( ) , "Unknown order sent." ) {
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , symbol , ErrorMaps [ - 2011 ] )
}
return fmt . Errorf ( "api_id:%d 交易对:%s 撤销订单失败:%+v" , apiUserInfo . Id , symbol , err . Error ( ) )
}
return nil
}
// ClosePosition 平仓
// symbol 交易对
// orderSn 平仓单号
// quantity 平仓数量
// side 原始仓位方向
// apiUserInfo 用户信息
// orderType 平仓类型 限价( LIMIT) 市价()
func ( e SpotRestApi ) ClosePosition ( symbol string , orderSn string , quantity decimal . Decimal , side string ,
apiUserInfo DbModels . LineApiUser , orderType string , rate string , price decimal . Decimal ) error {
endpoint := "/api/v3/order "
params := map [ string ] string {
"symbol" : symbol ,
"type" : orderType ,
"quantity" : quantity . String ( ) ,
"newClientOrderId" : orderSn ,
}
if side == "SELL" {
params [ "side" ] = "BUY"
} else {
params [ "side" ] = "SELL"
}
if orderType == "LIMIT" {
2025-02-08 14:05:57 +08:00
key := fmt . Sprintf ( global . TICKER_SPOT , global . EXCHANGE_BINANCE , symbol )
2025-02-06 11:14:33 +08:00
tradeSet , _ := helper . GetObjString [ models . TradeSet ] ( helper . DefaultRedis , key )
rateFloat , _ := decimal . NewFromString ( rate )
if rateFloat . GreaterThan ( decimal . Zero ) {
if side == "SELL" { //仓位是空 平空的话
price = price . Mul ( decimal . NewFromInt ( 1 ) . Add ( rateFloat ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
} else {
price = price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( rateFloat ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
}
params [ "price" ] = price . String ( )
}
}
params [ "timeInForce" ] = "GTC"
client := GetClient ( & apiUserInfo )
resp , _ , err := client . SendFuturesRequestAuth ( endpoint , "POST" , params )
if err != nil {
var dataMap map [ string ] interface { }
if err2 := sonic . Unmarshal ( [ ] byte ( err . Error ( ) ) , & dataMap ) ; err2 != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 平仓出错:%s" , apiUserInfo . Id , symbol , err . Error ( ) )
}
code , ok := dataMap [ "code" ]
if ok {
errContent := FutErrorMaps [ code . ( float64 ) ]
if errContent == "" {
errContent = err . Error ( )
}
return fmt . Errorf ( "api_id:%d 交易对:%s 平仓出错:%s" , apiUserInfo . Id , symbol , errContent )
}
}
var orderResp FutOrderResp
err = sonic . Unmarshal ( resp , & orderResp )
if err != nil {
return fmt . Errorf ( "api_id:%d 交易对:%s 平仓出错:%s" , apiUserInfo . Id , symbol , err . Error ( ) )
}
if orderResp . Symbol == "" {
return fmt . Errorf ( "api_id:%d 交易对:%s 平仓出错:未找到订单信息" , apiUserInfo . Id , symbol )
}
return nil
}
func GetClient ( apiUserInfo * DbModels . LineApiUser ) * helper . BinanceClient {
var client * helper . BinanceClient
if apiUserInfo . UserPass == "" {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "" , apiUserInfo . IpAddress )
} else {
client , _ = helper . NewBinanceClient ( apiUserInfo . ApiKey , apiUserInfo . ApiSecret , "socks5" , apiUserInfo . UserPass + "@" + apiUserInfo . IpAddress )
}
return client
}
/ *
判断是否触发
* /
func JudgeSpotPrice ( trade models . TradeSet ) {
2025-02-06 18:03:09 +08:00
key := fmt . Sprintf ( rediskey . PreSpotOrderList , global . EXCHANGE_BINANCE )
preOrderVal , _ := helper . DefaultRedis . GetAllList ( key )
2025-02-06 11:14:33 +08:00
db := GetDBConnection ( )
if len ( preOrderVal ) == 0 {
return
}
spotApi := SpotRestApi { }
for _ , item := range preOrderVal {
preOrder := dto . PreOrderRedisList { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & preOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if preOrder . Symbol == trade . Coin + trade . Currency {
orderPrice , _ := decimal . NewFromString ( preOrder . Price )
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//买入
if strings . ToUpper ( preOrder . Site ) == "BUY" && orderPrice . Cmp ( tradePrice ) >= 0 && orderPrice . Cmp ( decimal . Zero ) > 0 && tradePrice . Cmp ( decimal . Zero ) > 0 {
SpotOrderLock ( db , & preOrder , item , spotApi )
}
}
}
}
// 分布式锁下单
// v 预下单信息
// item 预下单源文本
func SpotOrderLock ( db * gorm . DB , v * dto . PreOrderRedisList , item string , spotApi SpotRestApi ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , v . ApiId , v . Symbol ) , 20 , 5 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
return
} else if ok {
defer lock . Release ( )
2025-02-06 18:03:09 +08:00
key := fmt . Sprintf ( rediskey . PreSpotOrderList , global . EXCHANGE_BINANCE )
2025-02-06 11:14:33 +08:00
preOrder := DbModels . LinePreOrder { }
if err := db . Where ( "id = ?" , v . Id ) . First ( & preOrder ) . Error ; err != nil {
log . Error ( "获取预下单失败" , err )
if errors . Is ( err , gorm . ErrRecordNotFound ) {
log . Error ( "不存在待触发主单" , item )
2025-02-06 18:03:09 +08:00
helper . DefaultRedis . LRem ( key , item )
2025-02-06 11:14:33 +08:00
}
return
}
2025-02-06 18:03:09 +08:00
hasrecord , _ := helper . DefaultRedis . IsElementInList ( key , item )
2025-02-06 11:14:33 +08:00
if ! hasrecord {
log . Error ( "不存在待触发主单" , item )
return
}
price , _ := decimal . NewFromString ( v . Price )
num , _ := decimal . NewFromString ( preOrder . Num )
params := OrderPlacementService {
ApiId : v . ApiId ,
Symbol : v . Symbol ,
Side : v . Site ,
Type : preOrder . MainOrderType ,
TimeInForce : "GTC" ,
Price : price ,
Quantity : num ,
NewClientOrderId : v . OrderSn ,
}
preOrderVal , _ := sonic . MarshalString ( & v )
if err := spotApi . OrderPlace ( db , params ) ; err != nil {
log . Error ( "下单失败" , v . Symbol , " err:" , err )
2025-02-06 18:03:09 +08:00
err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? AND status =0" , preOrder . Id ) . Updates ( map [ string ] interface { } { "status" : "2" , "desc" : err . Error ( ) } ) . Error
2025-02-06 11:14:33 +08:00
if err != nil {
log . Error ( "下单失败后修改订单失败" )
}
if preOrderVal != "" {
2025-02-06 18:03:09 +08:00
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
2025-02-06 11:14:33 +08:00
log . Error ( "删除redis 预下单失败:" , err )
}
}
return
}
if preOrderVal != "" {
2025-02-06 18:03:09 +08:00
if _ , err := helper . DefaultRedis . LRem ( key , preOrderVal ) ; err != nil {
2025-02-06 11:14:33 +08:00
log . Error ( "删除redis 预下单失败:" , err )
}
}
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "id =? AND status ='0'" , preOrder . Id ) . Update ( "status" , "1" ) . Error ; err != nil {
log . Error ( "更新预下单状态失败 ordersn:" , v . OrderSn , " status:1" )
}
return
} else {
log . Error ( "获取锁失败" )
return
}
}
2025-02-08 14:05:57 +08:00
// 判断是否触发止损
func JudgeSpotStopLoss ( trade models . TradeSet ) {
key := fmt . Sprintf ( rediskey . SpotStopLossList , global . EXCHANGE_BINANCE )
stopLossVal , _ := helper . DefaultRedis . GetAllList ( key )
if len ( stopLossVal ) == 0 {
return
}
db := GetDBConnection ( )
spotApi := SpotRestApi { }
setting , err := GetSystemSetting ( db )
if err != nil {
log . Error ( "获取系统设置失败" )
return
}
tradeSet , err := GetTradeSet ( trade . Coin + trade . Currency , 0 )
if err != nil {
log . Error ( "获取交易设置失败" )
return
}
for _ , item := range stopLossVal {
stopOrder := dto . StopLossRedisList { }
if err := sonic . Unmarshal ( [ ] byte ( item ) , & stopOrder ) ; err != nil {
log . Error ( "反序列化失败" )
continue
}
if stopOrder . Symbol == trade . Coin + trade . Currency {
orderPrice := stopOrder . Price
tradePrice , _ := decimal . NewFromString ( trade . LastPrice )
//买入
if strings . ToUpper ( stopOrder . Site ) == "SELL" &&
orderPrice . Cmp ( tradePrice ) >= 0 &&
orderPrice . Cmp ( decimal . Zero ) > 0 &&
tradePrice . Cmp ( decimal . Zero ) > 0 {
SpotStopLossTrigger ( db , stopOrder , spotApi , setting , tradeSet , key , item )
}
}
}
}
// 触发现货止损
func SpotStopLossTrigger ( db * gorm . DB , stopOrder dto . StopLossRedisList , spotApi SpotRestApi , setting DbModels . LineSystemSetting , tradeSet models . TradeSet , key string , item string ) {
lock := helper . NewRedisLock ( fmt . Sprintf ( rediskey . SpotTrigger , stopOrder . ApiId , stopOrder . Symbol ) , 20 , 5 , 100 * time . Millisecond )
if ok , err := lock . AcquireWait ( context . Background ( ) ) ; err != nil {
log . Error ( "获取锁失败" , err )
return
} else if ok {
defer lock . Release ( )
takeOrder := DbModels . LinePreOrder { }
if err := db . Model ( & DbModels . LinePreOrder { } ) . Where ( "pid =? AND order_type =1" , stopOrder . PId ) . Find ( & takeOrder ) . Error ; err != nil {
log . Error ( "查询止盈单失败" )
return
}
apiInfo , _ := GetApiInfo ( takeOrder . ApiId )
if apiInfo . Id == 0 {
log . Error ( "现货止损 查询api用户不存在" )
return
}
var err error
for x := 1 ; x <= 4 ; x ++ {
err = spotApi . CancelOpenOrderByOrderSn ( apiInfo , takeOrder . Symbol , takeOrder . OrderSn )
if err == nil {
break
}
}
if err != nil {
log . Error ( "现货止损撤单失败" , err )
return
}
stopPreOrder , _ := GetOrderById ( db , stopOrder . Id )
price := stopOrder . Price . Mul ( decimal . NewFromInt ( 1 ) . Sub ( setting . StopLossPremium . Div ( decimal . NewFromInt ( 100 ) ) ) ) . Truncate ( int32 ( tradeSet . PriceDigit ) )
num := utility . StrToDecimal ( takeOrder . Num ) . Truncate ( int32 ( tradeSet . AmountDigit ) )
params := OrderPlacementService {
ApiId : takeOrder . ApiId ,
Side : takeOrder . Site ,
Type : "LIMIT" ,
TimeInForce : "GTC" ,
Symbol : takeOrder . Symbol ,
Price : price ,
Quantity : num ,
NewClientOrderId : stopPreOrder . OrderSn ,
}
if err := spotApi . OrderPlace ( db , params ) ; err != nil {
log . Errorf ( "现货止损挂单失败 id: %s err:%v" , stopOrder . Id , err )
}
if _ , err := helper . DefaultRedis . LRem ( key , item ) ; err != nil {
log . Errorf ( "现货止损 删除缓存失败 id:%v err:%v" , stopOrder . Id , err )
}
} else {
log . Error ( "获取锁失败" )
}
}
2025-02-06 11:14:33 +08:00
/ *
获取api用户信息
* /
func GetApiInfo ( apiId int ) ( DbModels . LineApiUser , error ) {
api := DbModels . LineApiUser { }
key := fmt . Sprintf ( rediskey . API_USER , apiId )
val , _ := helper . DefaultRedis . GetString ( key )
if val != "" {
if err := sonic . UnmarshalString ( val , & api ) ; err == nil {
return api , nil
}
}
db := GetDBConnection ( )
if err := db . Model ( & api ) . Where ( "id =?" , apiId ) . First ( & api ) . Error ; err != nil {
return api , err
}
val , _ = sonic . MarshalString ( & api )
if val != "" {
helper . DefaultRedis . SetString ( key , val )
}
return api , nil
}
/ *
根据A账户获取B账号信息
* /
func GetChildApiInfo ( apiId int ) ( DbModels . LineApiUser , error ) {
var api DbModels . LineApiUser
childApiId := 0
groups := GetApiGroups ( )
for _ , item := range groups {
if item . ApiUserId == apiId {
childApiId = item . ChildApiUserId
break
}
}
if childApiId > 0 {
return GetApiInfo ( childApiId )
}
return api , nil
}
func GetApiGroups ( ) [ ] commondto . ApiGroupDto {
apiGroups := make ( [ ] commondto . ApiGroupDto , 0 )
apiGroupStr , _ := helper . DefaultRedis . GetAllKeysAndValues ( rediskey . ApiGroupAll )
if len ( apiGroupStr ) == 0 {
return apiGroups
}
for _ , item := range apiGroupStr {
apiGroup := commondto . ApiGroupDto { }
if err := sonic . UnmarshalString ( item , & apiGroup ) ; err != nil {
log . Error ( "groups 序列化失败" , err )
continue
}
apiGroups = append ( apiGroups , apiGroup )
}
return apiGroups
}
// GetSpotSymbolLastPrice 获取现货交易对最新价格
func ( e SpotRestApi ) GetSpotSymbolLastPrice ( targetSymbol string ) ( lastPrice decimal . Decimal ) {
tickerSymbol := helper . DefaultRedis . Get ( rediskey . SpotSymbolTicker ) . Val ( )
tickerSymbolMaps := make ( [ ] dto . Ticker , 0 )
sonic . Unmarshal ( [ ] byte ( tickerSymbol ) , & tickerSymbolMaps )
//key := fmt.Sprintf("%s:%s", global.TICKER_SPOT, targetSymbol)
//tradeSet, _ := helper.GetObjString[models.TradeSet](helper.DefaultRedis, key)
for _ , symbolMap := range tickerSymbolMaps {
if symbolMap . Symbol == strings . ToUpper ( targetSymbol ) {
lastPrice = utility . StringToDecimal ( symbolMap . Price )
}
}
return lastPrice
}