mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-15 08:46:58 +08:00
feat: add debate arena and fix multiple issues
- Add AI debate arena for multi-AI trading decisions - Fix debate consensus calculation and display - Fix vote parsing to support both <decision> and <final_vote> tags - Fix JSON field name compatibility (stop_loss/stop_loss_pct) - Fix symbol validation to prevent AI hallucinating invalid symbols - Fix Bybit position side display (was uppercase, now lowercase for consistency) - Fix NOFX logo navigation to home page - Add detailed logging for debugging trade execution
This commit is contained in:
@@ -795,6 +795,29 @@ func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, act
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteDecision executes a trading decision from external sources (e.g., debate consensus)
|
||||
// This is a public method that can be called by other modules
|
||||
func (at *AutoTrader) ExecuteDecision(d *decision.Decision) error {
|
||||
logger.Infof("[%s] Executing external decision: %s %s", at.name, d.Action, d.Symbol)
|
||||
|
||||
// Create a minimal action record for tracking
|
||||
actionRecord := &store.DecisionAction{
|
||||
Symbol: d.Symbol,
|
||||
Action: d.Action,
|
||||
Leverage: d.Leverage,
|
||||
}
|
||||
|
||||
// Execute the decision
|
||||
err := at.executeDecisionWithRecord(d, actionRecord)
|
||||
if err != nil {
|
||||
logger.Errorf("[%s] External decision execution failed: %v", at.name, err)
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Infof("[%s] External decision executed successfully: %s %s", at.name, d.Action, d.Symbol)
|
||||
return nil
|
||||
}
|
||||
|
||||
// executeOpenLongWithRecord executes open long position and records detailed information
|
||||
func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
||||
logger.Infof(" 📈 Open long: %s", decision.Symbol)
|
||||
|
||||
@@ -233,16 +233,23 @@ func (t *BybitTrader) GetPositions() ([]map[string]interface{}, error) {
|
||||
updatedTimeStr, _ := pos["updatedTime"].(string)
|
||||
updatedTime, _ := strconv.ParseInt(updatedTimeStr, 10, 64)
|
||||
|
||||
positionSide, _ := pos["side"].(string) // Buy = LONG, Sell = SHORT
|
||||
positionSide, _ := pos["side"].(string) // Buy = long, Sell = short
|
||||
|
||||
// Convert to unified format
|
||||
side := "LONG"
|
||||
// Log raw position data for debugging
|
||||
logger.Infof("[Bybit] GetPositions raw: symbol=%v, side=%s, size=%v", pos["symbol"], positionSide, sizeStr)
|
||||
|
||||
// Convert to unified format (use lowercase for consistency with other exchanges)
|
||||
// Bybit returns "Buy" for long, "Sell" for short
|
||||
side := "long"
|
||||
positionAmt := size
|
||||
if positionSide == "Sell" {
|
||||
side = "SHORT"
|
||||
positionSideLower := strings.ToLower(positionSide)
|
||||
if positionSideLower == "sell" {
|
||||
side = "short"
|
||||
positionAmt = -size
|
||||
}
|
||||
|
||||
logger.Infof("[Bybit] GetPositions converted: symbol=%v, rawSide=%s -> side=%s", pos["symbol"], positionSide, side)
|
||||
|
||||
position := map[string]interface{}{
|
||||
"symbol": pos["symbol"],
|
||||
"side": side,
|
||||
@@ -271,6 +278,8 @@ func (t *BybitTrader) GetPositions() ([]map[string]interface{}, error) {
|
||||
|
||||
// OpenLong opens a long position
|
||||
func (t *BybitTrader) OpenLong(symbol string, quantity float64, leverage int) (map[string]interface{}, error) {
|
||||
logger.Infof("[Bybit] ===== OpenLong called: symbol=%s, qty=%.6f, leverage=%d =====", symbol, quantity, leverage)
|
||||
|
||||
// Set leverage first
|
||||
if err := t.SetLeverage(symbol, leverage); err != nil {
|
||||
logger.Infof("⚠️ [Bybit] Failed to set leverage: %v", err)
|
||||
@@ -288,6 +297,8 @@ func (t *BybitTrader) OpenLong(symbol string, quantity float64, leverage int) (m
|
||||
"positionIdx": 0, // One-way position mode
|
||||
}
|
||||
|
||||
logger.Infof("[Bybit] OpenLong placing order: %+v", params)
|
||||
|
||||
result, err := t.client.NewUtaBybitServiceWithParams(params).PlaceOrder(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Bybit open long failed: %w", err)
|
||||
@@ -301,6 +312,8 @@ func (t *BybitTrader) OpenLong(symbol string, quantity float64, leverage int) (m
|
||||
|
||||
// OpenShort opens a short position
|
||||
func (t *BybitTrader) OpenShort(symbol string, quantity float64, leverage int) (map[string]interface{}, error) {
|
||||
logger.Infof("[Bybit] ===== OpenShort called: symbol=%s, qty=%.6f, leverage=%d =====", symbol, quantity, leverage)
|
||||
|
||||
// Set leverage first
|
||||
if err := t.SetLeverage(symbol, leverage); err != nil {
|
||||
logger.Infof("⚠️ [Bybit] Failed to set leverage: %v", err)
|
||||
@@ -318,6 +331,8 @@ func (t *BybitTrader) OpenShort(symbol string, quantity float64, leverage int) (
|
||||
"positionIdx": 0, // One-way position mode
|
||||
}
|
||||
|
||||
logger.Infof("[Bybit] OpenShort placing order: %+v", params)
|
||||
|
||||
result, err := t.client.NewUtaBybitServiceWithParams(params).PlaceOrder(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Bybit open short failed: %w", err)
|
||||
@@ -338,7 +353,8 @@ func (t *BybitTrader) CloseLong(symbol string, quantity float64) (map[string]int
|
||||
return nil, err
|
||||
}
|
||||
for _, pos := range positions {
|
||||
if pos["symbol"] == symbol && pos["side"] == "LONG" {
|
||||
side, _ := pos["side"].(string)
|
||||
if pos["symbol"] == symbol && strings.ToLower(side) == "long" {
|
||||
quantity = pos["positionAmt"].(float64)
|
||||
break
|
||||
}
|
||||
@@ -382,7 +398,8 @@ func (t *BybitTrader) CloseShort(symbol string, quantity float64) (map[string]in
|
||||
return nil, err
|
||||
}
|
||||
for _, pos := range positions {
|
||||
if pos["symbol"] == symbol && pos["side"] == "SHORT" {
|
||||
side, _ := pos["side"].(string)
|
||||
if pos["symbol"] == symbol && strings.ToLower(side) == "short" {
|
||||
quantity = -pos["positionAmt"].(float64) // Short position is negative
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user