mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-11 23:07:01 +08:00
fix: initial balance calculation and UI improvements
- Fix initial balance using available_balance instead of total_equity - Fix WSMonitor nil pointer by starting market monitor before loading traders - Add strategy name display on traders list and dashboard pages - Various position sync and trading improvements
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"nofx/market"
|
||||
"nofx/store"
|
||||
)
|
||||
|
||||
// AIConfig defines the AI client configuration used in backtesting.
|
||||
@@ -176,3 +177,61 @@ func validateFillPolicy(policy string) error {
|
||||
return fmt.Errorf("unsupported fill_policy '%s'", policy)
|
||||
}
|
||||
}
|
||||
|
||||
// ToStrategyConfig converts BacktestConfig to StrategyConfig for unified prompt generation.
|
||||
// This ensures backtest uses the same StrategyEngine logic as live trading.
|
||||
func (cfg *BacktestConfig) ToStrategyConfig() *store.StrategyConfig {
|
||||
// Determine primary and longer timeframe from the timeframes list
|
||||
primaryTF := "5m"
|
||||
longerTF := "4h"
|
||||
if len(cfg.Timeframes) > 0 {
|
||||
primaryTF = cfg.Timeframes[0]
|
||||
}
|
||||
if len(cfg.Timeframes) > 1 {
|
||||
longerTF = cfg.Timeframes[len(cfg.Timeframes)-1]
|
||||
}
|
||||
|
||||
return &store.StrategyConfig{
|
||||
CoinSource: store.CoinSourceConfig{
|
||||
SourceType: "static",
|
||||
StaticCoins: cfg.Symbols,
|
||||
UseCoinPool: false,
|
||||
CoinPoolLimit: len(cfg.Symbols),
|
||||
UseOITop: false,
|
||||
OITopLimit: 0,
|
||||
},
|
||||
Indicators: store.IndicatorConfig{
|
||||
Klines: store.KlineConfig{
|
||||
PrimaryTimeframe: primaryTF,
|
||||
PrimaryCount: 30,
|
||||
LongerTimeframe: longerTF,
|
||||
LongerCount: 10,
|
||||
EnableMultiTimeframe: len(cfg.Timeframes) > 1,
|
||||
SelectedTimeframes: cfg.Timeframes,
|
||||
},
|
||||
EnableRawKlines: true,
|
||||
EnableEMA: true,
|
||||
EnableMACD: true,
|
||||
EnableRSI: true,
|
||||
EnableATR: true,
|
||||
EnableVolume: true,
|
||||
EnableOI: true,
|
||||
EnableFundingRate: true,
|
||||
EMAPeriods: []int{20, 50},
|
||||
RSIPeriods: []int{7, 14},
|
||||
ATRPeriods: []int{14},
|
||||
},
|
||||
CustomPrompt: cfg.CustomPrompt,
|
||||
RiskControl: store.RiskControlConfig{
|
||||
MaxPositions: 3,
|
||||
BTCETHMaxLeverage: cfg.Leverage.BTCETHLeverage,
|
||||
AltcoinMaxLeverage: cfg.Leverage.AltcoinLeverage,
|
||||
BTCETHMaxPositionValueRatio: 5.0,
|
||||
AltcoinMaxPositionValueRatio: 1.0,
|
||||
MaxMarginUsage: 0.9,
|
||||
MinPositionSize: 12,
|
||||
MinRiskRewardRatio: 3.0,
|
||||
MinConfidence: 75,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,10 @@ const (
|
||||
|
||||
// Runner encapsulates the lifecycle of a single backtest run.
|
||||
type Runner struct {
|
||||
cfg BacktestConfig
|
||||
feed *DataFeed
|
||||
account *BacktestAccount
|
||||
cfg BacktestConfig
|
||||
feed *DataFeed
|
||||
account *BacktestAccount
|
||||
strategyEngine *decision.StrategyEngine
|
||||
|
||||
decisionLogDir string
|
||||
mcpClient mcp.AIClient
|
||||
@@ -115,10 +116,15 @@ func NewRunner(cfg BacktestConfig, mcpClient mcp.AIClient) (*Runner, error) {
|
||||
aiCache = cache
|
||||
}
|
||||
|
||||
// Create strategy engine from backtest config for unified prompt generation
|
||||
strategyConfig := cfg.ToStrategyConfig()
|
||||
strategyEngine := decision.NewStrategyEngine(strategyConfig)
|
||||
|
||||
r := &Runner{
|
||||
cfg: cfg,
|
||||
feed: feed,
|
||||
account: account,
|
||||
strategyEngine: strategyEngine,
|
||||
decisionLogDir: dLogDir,
|
||||
mcpClient: client,
|
||||
status: RunStateCreated,
|
||||
@@ -492,7 +498,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da
|
||||
|
||||
runtime := int((ts - int64(r.cfg.StartTS*1000)) / 60000)
|
||||
ctx := &decision.Context{
|
||||
CurrentTime: time.UnixMilli(ts).UTC().Format(time.RFC3339),
|
||||
CurrentTime: time.UnixMilli(ts).UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||
RuntimeMinutes: runtime,
|
||||
CallCount: callCount,
|
||||
Account: accountInfo,
|
||||
@@ -503,6 +509,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da
|
||||
MultiTFMarket: multiTF,
|
||||
BTCETHLeverage: r.cfg.Leverage.BTCETHLeverage,
|
||||
AltcoinLeverage: r.cfg.Leverage.AltcoinLeverage,
|
||||
Timeframes: r.cfg.Timeframes,
|
||||
}
|
||||
|
||||
record := &store.DecisionRecord{
|
||||
@@ -537,12 +544,13 @@ func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *decision
|
||||
func (r *Runner) invokeAIWithRetry(ctx *decision.Context) (*decision.FullDecision, error) {
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < aiDecisionMaxRetries; attempt++ {
|
||||
fd, err := decision.GetFullDecisionWithCustomPrompt(
|
||||
// Use GetFullDecisionWithStrategy with the pre-configured strategy engine
|
||||
// This ensures backtest uses the same unified prompt generation as live trading
|
||||
fd, err := decision.GetFullDecisionWithStrategy(
|
||||
ctx,
|
||||
r.mcpClient,
|
||||
r.cfg.CustomPrompt,
|
||||
r.cfg.OverrideBasePrompt,
|
||||
r.cfg.PromptTemplate,
|
||||
r.strategyEngine,
|
||||
r.cfg.PromptVariant,
|
||||
)
|
||||
if err == nil {
|
||||
return fd, nil
|
||||
|
||||
Reference in New Issue
Block a user