mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-12 07:16:56 +08:00
Add MarginMode configration
This commit is contained in:
@@ -819,6 +819,38 @@ func (t *AsterTrader) CloseShort(symbol string, quantity float64) (map[string]in
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SetMarginMode 设置仓位模式
|
||||
func (t *AsterTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
|
||||
// Aster支持仓位模式设置
|
||||
// API格式与币安相似:CROSSED(全仓) / ISOLATED(逐仓)
|
||||
marginType := "CROSSED"
|
||||
if !isCrossMargin {
|
||||
marginType = "ISOLATED"
|
||||
}
|
||||
|
||||
params := map[string]interface{}{
|
||||
"symbol": symbol,
|
||||
"marginType": marginType,
|
||||
}
|
||||
|
||||
// 使用request方法调用API
|
||||
_, err := t.request("POST", "/fapi/v3/marginType", params)
|
||||
if err != nil {
|
||||
// 如果错误表示无需更改,忽略错误
|
||||
if strings.Contains(err.Error(), "No need to change") ||
|
||||
strings.Contains(err.Error(), "Margin type cannot be changed") {
|
||||
log.Printf(" ✓ %s 仓位模式已是 %s 或有持仓无法更改", symbol, marginType)
|
||||
return nil
|
||||
}
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
// 不返回错误,让交易继续
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginType)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLeverage 设置杠杆倍数
|
||||
func (t *AsterTrader) SetLeverage(symbol string, leverage int) error {
|
||||
params := map[string]interface{}{
|
||||
|
||||
@@ -63,6 +63,9 @@ type AutoTraderConfig struct {
|
||||
MaxDailyLoss float64 // 最大日亏损百分比(提示)
|
||||
MaxDrawdown float64 // 最大回撤百分比(提示)
|
||||
StopTradingTime time.Duration // 触发风控后暂停时长
|
||||
|
||||
// 仓位模式
|
||||
IsCrossMargin bool // true=全仓模式, false=逐仓模式
|
||||
}
|
||||
|
||||
// AutoTrader 自动交易器
|
||||
@@ -135,6 +138,13 @@ func NewAutoTrader(config AutoTraderConfig) (*AutoTrader, error) {
|
||||
var trader Trader
|
||||
var err error
|
||||
|
||||
// 记录仓位模式(通用)
|
||||
marginModeStr := "全仓"
|
||||
if !config.IsCrossMargin {
|
||||
marginModeStr = "逐仓"
|
||||
}
|
||||
log.Printf("📊 [%s] 仓位模式: %s", config.Name, marginModeStr)
|
||||
|
||||
switch config.Exchange {
|
||||
case "binance":
|
||||
log.Printf("🏦 [%s] 使用币安合约交易", config.Name)
|
||||
@@ -589,6 +599,12 @@ func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, act
|
||||
actionRecord.Quantity = quantity
|
||||
actionRecord.Price = marketData.CurrentPrice
|
||||
|
||||
// 设置仓位模式
|
||||
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
// 继续执行,不影响交易
|
||||
}
|
||||
|
||||
// 开仓
|
||||
order, err := at.trader.OpenLong(decision.Symbol, quantity, decision.Leverage)
|
||||
if err != nil {
|
||||
@@ -642,6 +658,12 @@ func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, ac
|
||||
actionRecord.Quantity = quantity
|
||||
actionRecord.Price = marketData.CurrentPrice
|
||||
|
||||
// 设置仓位模式
|
||||
if err := at.trader.SetMarginMode(decision.Symbol, at.config.IsCrossMargin); err != nil {
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
// 继续执行,不影响交易
|
||||
}
|
||||
|
||||
// 开仓
|
||||
order, err := at.trader.OpenShort(decision.Symbol, quantity, decision.Leverage)
|
||||
if err != nil {
|
||||
@@ -885,7 +907,7 @@ func (at *AutoTrader) GetPositions() ([]map[string]interface{}, error) {
|
||||
|
||||
// 计算占用保证金
|
||||
marginUsed := (quantity * markPrice) / float64(leverage)
|
||||
|
||||
|
||||
// 计算盈亏百分比(基于保证金)
|
||||
// 收益率 = 未实现盈亏 / 保证金 × 100%
|
||||
pnlPct := 0.0
|
||||
|
||||
@@ -131,6 +131,46 @@ func (t *FuturesTrader) GetPositions() ([]map[string]interface{}, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SetMarginMode 设置仓位模式
|
||||
func (t *FuturesTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
|
||||
var marginType futures.MarginType
|
||||
if isCrossMargin {
|
||||
marginType = futures.MarginTypeCrossed
|
||||
} else {
|
||||
marginType = futures.MarginTypeIsolated
|
||||
}
|
||||
|
||||
// 尝试设置仓位模式
|
||||
err := t.client.NewChangeMarginTypeService().
|
||||
Symbol(symbol).
|
||||
MarginType(marginType).
|
||||
Do(context.Background())
|
||||
|
||||
marginModeStr := "全仓"
|
||||
if !isCrossMargin {
|
||||
marginModeStr = "逐仓"
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// 如果错误信息包含"No need to change",说明仓位模式已经是目标值
|
||||
if contains(err.Error(), "No need to change margin type") {
|
||||
log.Printf(" ✓ %s 仓位模式已是 %s", symbol, marginModeStr)
|
||||
return nil
|
||||
}
|
||||
// 如果有持仓,无法更改仓位模式,但不影响交易
|
||||
if contains(err.Error(), "Margin type cannot be changed if there exists position") {
|
||||
log.Printf(" ⚠️ %s 有持仓,无法更改仓位模式,继续使用当前模式", symbol)
|
||||
return nil
|
||||
}
|
||||
log.Printf(" ⚠️ 设置仓位模式失败: %v", err)
|
||||
// 不返回错误,让交易继续
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf(" ✓ %s 仓位模式已设置为 %s", symbol, marginModeStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLeverage 设置杠杆(智能判断+冷却期)
|
||||
func (t *FuturesTrader) SetLeverage(symbol string, leverage int) error {
|
||||
// 先尝试获取当前杠杆(从持仓信息)
|
||||
@@ -177,31 +217,6 @@ func (t *FuturesTrader) SetLeverage(symbol string, leverage int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMarginType 设置保证金模式
|
||||
func (t *FuturesTrader) SetMarginType(symbol string, marginType futures.MarginType) error {
|
||||
err := t.client.NewChangeMarginTypeService().
|
||||
Symbol(symbol).
|
||||
MarginType(marginType).
|
||||
Do(context.Background())
|
||||
|
||||
if err != nil {
|
||||
// 如果已经是该模式,不算错误
|
||||
if contains(err.Error(), "No need to change") {
|
||||
log.Printf(" ✓ %s 保证金模式已是 %s", symbol, marginType)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("设置保证金模式失败: %w", err)
|
||||
}
|
||||
|
||||
log.Printf(" ✓ %s 保证金模式已切换为 %s", symbol, marginType)
|
||||
|
||||
// 切换保证金模式后等待3秒(避免冷却期错误)
|
||||
log.Printf(" ⏱ 等待3秒冷却期...")
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenLong 开多仓
|
||||
func (t *FuturesTrader) OpenLong(symbol string, quantity float64, leverage int) (map[string]interface{}, error) {
|
||||
// 先取消该币种的所有委托单(清理旧的止损止盈单)
|
||||
@@ -214,10 +229,7 @@ func (t *FuturesTrader) OpenLong(symbol string, quantity float64, leverage int)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置逐仓模式
|
||||
if err := t.SetMarginType(symbol, futures.MarginTypeIsolated); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 注意:仓位模式应该由调用方(AutoTrader)在开仓前通过 SetMarginMode 设置
|
||||
|
||||
// 格式化数量到正确精度
|
||||
quantityStr, err := t.FormatQuantity(symbol, quantity)
|
||||
@@ -260,10 +272,7 @@ func (t *FuturesTrader) OpenShort(symbol string, quantity float64, leverage int)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 设置逐仓模式
|
||||
if err := t.SetMarginType(symbol, futures.MarginTypeIsolated); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 注意:仓位模式应该由调用方(AutoTrader)在开仓前通过 SetMarginMode 设置
|
||||
|
||||
// 格式化数量到正确精度
|
||||
quantityStr, err := t.FormatQuantity(symbol, quantity)
|
||||
|
||||
@@ -13,10 +13,11 @@ import (
|
||||
|
||||
// HyperliquidTrader Hyperliquid交易器
|
||||
type HyperliquidTrader struct {
|
||||
exchange *hyperliquid.Exchange
|
||||
ctx context.Context
|
||||
walletAddr string
|
||||
meta *hyperliquid.Meta // 缓存meta信息(包含精度等)
|
||||
exchange *hyperliquid.Exchange
|
||||
ctx context.Context
|
||||
walletAddr string
|
||||
meta *hyperliquid.Meta // 缓存meta信息(包含精度等)
|
||||
isCrossMargin bool // 是否为全仓模式
|
||||
}
|
||||
|
||||
// NewHyperliquidTrader 创建Hyperliquid交易器
|
||||
@@ -63,10 +64,11 @@ func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool)
|
||||
}
|
||||
|
||||
return &HyperliquidTrader{
|
||||
exchange: exchange,
|
||||
ctx: ctx,
|
||||
walletAddr: walletAddr,
|
||||
meta: meta,
|
||||
exchange: exchange,
|
||||
ctx: ctx,
|
||||
walletAddr: walletAddr,
|
||||
meta: meta,
|
||||
isCrossMargin: true, // 默认使用全仓模式
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -187,13 +189,26 @@ func (t *HyperliquidTrader) GetPositions() ([]map[string]interface{}, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// SetMarginMode 设置仓位模式 (在SetLeverage时一并设置)
|
||||
func (t *HyperliquidTrader) SetMarginMode(symbol string, isCrossMargin bool) error {
|
||||
// Hyperliquid的仓位模式在SetLeverage时设置,这里只记录
|
||||
t.isCrossMargin = isCrossMargin
|
||||
marginModeStr := "全仓"
|
||||
if !isCrossMargin {
|
||||
marginModeStr = "逐仓"
|
||||
}
|
||||
log.Printf(" ✓ %s 将使用 %s 模式", symbol, marginModeStr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetLeverage 设置杠杆
|
||||
func (t *HyperliquidTrader) SetLeverage(symbol string, leverage int) error {
|
||||
// Hyperliquid symbol格式(去掉USDT后缀)
|
||||
coin := convertSymbolToHyperliquid(symbol)
|
||||
|
||||
// 调用UpdateLeverage (leverage int, name string, isCross bool)
|
||||
_, err := t.exchange.UpdateLeverage(t.ctx, leverage, coin, false) // false = 逐仓模式
|
||||
// 第三个参数: true=全仓模式, false=逐仓模式
|
||||
_, err := t.exchange.UpdateLeverage(t.ctx, leverage, coin, t.isCrossMargin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("设置杠杆失败: %w", err)
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ type Trader interface {
|
||||
// SetLeverage 设置杠杆
|
||||
SetLeverage(symbol string, leverage int) error
|
||||
|
||||
// SetMarginMode 设置仓位模式 (true=全仓, false=逐仓)
|
||||
SetMarginMode(symbol string, isCrossMargin bool) error
|
||||
|
||||
// GetMarketPrice 获取市场价格
|
||||
GetMarketPrice(symbol string) (float64, error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user