## Summary
- Add POST /traders/:id/sync-balance endpoint (Option B)
- Add smart detection showing balance change percentage (Option C)
- Fix balance display bug caused by commit 2b9c4d2
## Changes
### api/server.go
- Add handleSyncBalance() handler
- Query actual exchange balance via trader.GetBalance()
- Calculate change percentage for smart detection
- Update initial_balance in database
- Reload trader into memory after update
### config/database.go
- Add UpdateTraderInitialBalance() method
- Update traders.initial_balance field
## Root Cause
Commit 2b9c4d2 auto-queries exchange balance at trader creation time,
but never updates after user deposits more funds, causing:
- Wrong initial_balance (400 USDT vs actual 3000 USDT)
- Wrong P&L calculations (-2598.55 USDT instead of actual)
## Solution
Provides manual sync API + smart detection to update initial_balance
when user deposits funds after trader creation.
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
## Problem
When editing trader configuration, if `system_prompt_template` was set to an empty string (""), the UI would incorrectly treat it as falsy and overwrite it with 'default', losing the user's selection.
**Root cause:**
```tsx
if (traderData && !traderData.system_prompt_template) {
// ❌ This triggers for both undefined AND empty string ""
setFormData({ system_prompt_template: 'default' });
}
```
JavaScript falsy values that trigger `!` operator:
- `undefined` ✅ Should trigger default
- `null` ✅ Should trigger default
- `""` ❌ Should NOT trigger (user explicitly chose empty)
- `false`, `0`, `NaN` (less relevant here)
## Solution
Change condition to explicitly check for `undefined`:
```tsx
if (traderData && traderData.system_prompt_template === undefined) {
// ✅ Only triggers for truly missing field
setFormData({ system_prompt_template: 'default' });
}
```
## Impact
- ✅ Empty string selections are preserved
- ✅ Legacy data (undefined) still gets default value
- ✅ User's explicit choices are respected
- ✅ No breaking changes to existing functionality
## Testing
- ✅ Code compiles
- ⚠️ Requires manual UI testing:
- [ ] Edit trader with empty system_prompt_template
- [ ] Verify it doesn't reset to 'default'
- [ ] Create new trader → should default to 'default'
- [ ] Edit old trader (undefined field) → should default to 'default'
## Code Changes
```
web/src/components/TraderConfigModal.tsx:
- Line 99: Changed !traderData.system_prompt_template → === undefined
```
## Problem
Multiple partial_close actions on the same position were being counted as separate trades, inflating TotalTrades count and distorting win rate/profit factor statistics.
**Example of bug:**
- Open 1 BTC @ $100,000
- Partial close 30% @ $101,000 → Counted as trade #1❌
- Partial close 50% @ $102,000 → Counted as trade #2❌
- Close remaining 20% @ $103,000 → Counted as trade #3❌
- **Result:** 3 trades instead of 1 ❌
## Solution
### 1. Added tracking fields to openPositions map
- `remainingQuantity`: Tracks remaining position size
- `accumulatedPnL`: Accumulates PnL from all partial closes
- `partialCloseCount`: Counts number of partial close operations
- `partialCloseVolume`: Total volume closed partially
### 2. Modified partial_close handling logic
- Each partial_close:
- Accumulates PnL into `accumulatedPnL`
- Reduces `remainingQuantity`
- **Does NOT increment TotalTrades++**
- Keeps position in openPositions map
- Only when `remainingQuantity <= 0.0001`:
- Records ONE TradeOutcome with aggregated PnL
- Increments TotalTrades++ once
- Removes from openPositions map
### 3. Updated full close handling
- If position had prior partial closes:
- Adds `accumulatedPnL` to final close PnL
- Reports total PnL in TradeOutcome
### 4. Fixed GetStatistics()
- Removed `partial_close` from TotalClosePositions count
- Only `close_long/close_short/auto_close` count as close operations
## Impact
- ✅ Statistics now accurate: multiple partial closes = 1 trade
- ✅ Win rate calculated correctly
- ✅ Profit factor reflects true performance
- ✅ Backward compatible: handles positions without tracking fields
## Testing
- ✅ Compiles successfully
- ⚠️ Requires validation with live partial_close scenarios
## Code Changes
```
logger/decision_logger.go:
- Lines 420-430: Add tracking fields to openPositions
- Lines 441-534: Implement partial_close aggregation logic
- Lines 536-593: Update full close to include accumulated PnL
- Lines 246-250: Fix GetStatistics() to exclude partial_close
```
## Problem
AI was calculating position_size_usd incorrectly, treating it as margin requirement instead of notional value, causing code=-2019 errors (insufficient margin).
## Solution
### 1. Updated AI prompts with correct formula
- **prompts/adaptive.txt**: Added clear position sizing calculation steps
- **prompts/nof1.txt**: Added English version with example
- **prompts/default.txt**: Added Chinese version with example
**Correct formula:**
1. Available Margin = Available Cash × 0.95 × Allocation % (reserve 5% for fees)
2. Notional Value = Available Margin × Leverage
3. position_size_usd = Notional Value (this is the value for JSON)
**Example:** $500 cash, 5x leverage → position_size_usd = $2,375 (not $500)
### 2. Added code-level validation
- **trader/auto_trader.go**: Added margin checks in executeOpenLong/ShortWithRecord
- Validates required margin + fees ≤ available balance before opening position
- Returns clear error message if insufficient
## Impact
- Prevents code=-2019 errors
- AI now understands the difference between notional value and margin requirement
- Double validation: AI prompt + code check
## Testing
- ✅ Compiles successfully
- ⚠️ Requires live trading environment testing
- Fix compilation error on Alpine: off64_t type not defined in v1.14.16
- Remove unused pure-Go sqlite implementation (modernc.org/sqlite) and its dependencies
- v1.14.22 is the first version fixing Alpine/musl build issues (2024-02-02)
- Minimizes version jump (v1.14.16 → v1.14.22, 18 commits) to reduce risk
Reference: https://github.com/mattn/go-sqlite3/issues/1164
Verified: Builds successfully on golang:1.25-alpine
## Problem
GetCurrentKlines had two critical bugs causing price data to become stale:
1. Incorrect return logic: returned error even when data fetch succeeded
2. Race condition: returned slice reference instead of deep copy, causing concurrent data corruption
## Impact
- BTC price stuck at 106xxx while actual market price was 107xxx+
- LLM calculated take-profit based on stale prices → orders failed validation
- Statistics showed incorrect P&L (0.00%) due to corrupted historical data
- Alt-coins filtered out due to failed market data fetch
## Solution
1. Fixed return logic: only return error when actual failure occurs
2. Return deep copy instead of reference to prevent race conditions
3. Downgrade subscription errors to warnings (non-blocking)
## Test Results
✅ Price updates in real-time
✅ Take-profit orders execute successfully
✅ P&L calculations accurate
✅ Alt-coins now tradeable
Related: Price feed mechanism, concurrent data access
Add detailed guidance chapter for dynamic TP/SL management and partial close operations.
## Changes
- New chapter: "动态止盈止损与部分平仓指引" (Dynamic TP/SL & Partial Close Guidance)
- Inserted between "可用动作" (Actions) and "决策流程" (Decision Flow) sections
- 4 key guidance points covering:
1. Partial close best practices (use clear percentages like 25%/50%/75%)
2. Reassessing remaining position after partial exit
3. Proper use cases for update_stop_loss / update_take_profit
4. Multi-stage exit strategy requirements
## Benefits
- ✅ Provides concrete operational guidelines for AI decision-making
- ✅ Clarifies when and how to use partial_close effectively
- ✅ Emphasizes remaining position management (prevents "orphan" positions)
- ✅ Aligns with existing backend support for partial_close action
## Background
While adaptive.txt already lists partial_close as an available action,
it lacked detailed operational guidance. This enhancement fills that gap
by providing specific percentages, use cases, and multi-stage exit examples.
Backend (decision/engine.go) already validates partial_close with
close_percentage field, so this is purely a prompt enhancement with
no code changes required.
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
- This PR should only contain backend core functionality
- prompts/adaptive.txt v2.0 is already in upstream
- Prompt enhancements will be in separate PR (Batch 3)
Fixed compilation error caused by variable name mismatch:
- Line 404: defined as 'trader'
- Line 425: was using 'traderRecord' (undefined)
This aligns with upstream dev branch naming convention.
Problem:
- Users could input arbitrary initial balance when creating traders
- This didn't reflect the actual available balance in exchange account
- Could lead to incorrect position sizing and risk calculations
Solution:
- Before creating trader, query exchange API for actual balance
- Use GetBalance() from respective trader implementation:
* Binance: NewFuturesTrader + GetBalance()
* Hyperliquid: NewHyperliquidTrader + GetBalance()
* Aster: NewAsterTrader + GetBalance()
- Extract 'available_balance' or 'balance' from response
- Override user input with actual balance
- Fallback to user input if query fails
Changes:
- Added 'nofx/trader' import
- Query GetExchanges() to find matching exchange config
- Create temporary trader instance based on exchange type
- Call GetBalance() to fetch actual available balance
- Use actualBalance instead of req.InitialBalance
- Comprehensive error handling with fallback logic
Benefits:
- ✅ Ensures accurate initial balance matches exchange account
- ✅ Prevents user errors in balance input
- ✅ Improves position sizing accuracy
- ✅ Maintains data integrity between system and exchange
Example logs:
✓ 查询到交易所实际余额: 150.00 USDT (用户输入: 100.00 USDT)
⚠️ 查询交易所余额失败,使用用户输入的初始资金: connection timeout
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
The chart was not showing data because the API response format changed.
Fixed the calculation of PnL percentage by computing it from total_pnl
and balance values (initial_balance = balance - total_pnl).
Now the AI competition chart should properly display performance comparison data.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: tinkle-community <tinklefund@gmail.com>