mirror of
https://github.com/laoxong/nofx.git
synced 2026-06-04 09:58:22 +08:00
refactor: rename decision package to kernel
This commit is contained in:
+6
-6
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/logger"
|
"nofx/logger"
|
||||||
"nofx/market"
|
"nofx/market"
|
||||||
"nofx/mcp"
|
"nofx/mcp"
|
||||||
@@ -397,7 +397,7 @@ func (s *Server) handlePreviewPrompt(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create strategy engine to build prompt
|
// Create strategy engine to build prompt
|
||||||
engine := decision.NewStrategyEngine(&req.Config)
|
engine := kernel.NewStrategyEngine(&req.Config)
|
||||||
|
|
||||||
// Build system prompt (using built-in method from strategy engine)
|
// Build system prompt (using built-in method from strategy engine)
|
||||||
systemPrompt := engine.BuildSystemPrompt(
|
systemPrompt := engine.BuildSystemPrompt(
|
||||||
@@ -443,7 +443,7 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create strategy engine to build prompt
|
// Create strategy engine to build prompt
|
||||||
engine := decision.NewStrategyEngine(&req.Config)
|
engine := kernel.NewStrategyEngine(&req.Config)
|
||||||
|
|
||||||
// Get candidate coins
|
// Get candidate coins
|
||||||
candidates, err := engine.GetCandidateCoins()
|
candidates, err := engine.GetCandidateCoins()
|
||||||
@@ -505,11 +505,11 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) {
|
|||||||
oiRankingData := engine.FetchOIRankingData()
|
oiRankingData := engine.FetchOIRankingData()
|
||||||
|
|
||||||
// Build real context (for generating User Prompt)
|
// Build real context (for generating User Prompt)
|
||||||
testContext := &decision.Context{
|
testContext := &kernel.Context{
|
||||||
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||||
RuntimeMinutes: 0,
|
RuntimeMinutes: 0,
|
||||||
CallCount: 1,
|
CallCount: 1,
|
||||||
Account: decision.AccountInfo{
|
Account: kernel.AccountInfo{
|
||||||
TotalEquity: 1000.0,
|
TotalEquity: 1000.0,
|
||||||
AvailableBalance: 1000.0,
|
AvailableBalance: 1000.0,
|
||||||
UnrealizedPnL: 0,
|
UnrealizedPnL: 0,
|
||||||
@@ -519,7 +519,7 @@ func (s *Server) handleStrategyTestRun(c *gin.Context) {
|
|||||||
MarginUsedPct: 0,
|
MarginUsedPct: 0,
|
||||||
PositionCount: 0,
|
PositionCount: 0,
|
||||||
},
|
},
|
||||||
Positions: []decision.PositionInfo{},
|
Positions: []kernel.PositionInfo{},
|
||||||
CandidateCoins: candidates,
|
CandidateCoins: candidates,
|
||||||
PromptVariant: req.PromptVariant,
|
PromptVariant: req.PromptVariant,
|
||||||
MarketDataMap: marketDataMap,
|
MarketDataMap: marketDataMap,
|
||||||
|
|||||||
+10
-10
@@ -9,7 +9,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/market"
|
"nofx/market"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ type cachedDecision struct {
|
|||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
PromptVariant string `json:"prompt_variant"`
|
PromptVariant string `json:"prompt_variant"`
|
||||||
Timestamp int64 `json:"ts"`
|
Timestamp int64 `json:"ts"`
|
||||||
Decision *decision.FullDecision `json:"decision"`
|
Decision *kernel.FullDecision `json:"decision"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AICache persists AI decisions for repeated backtesting or replay.
|
// AICache persists AI decisions for repeated backtesting or replay.
|
||||||
@@ -67,7 +67,7 @@ func (c *AICache) Path() string {
|
|||||||
return c.path
|
return c.path
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AICache) Get(key string) (*decision.FullDecision, bool) {
|
func (c *AICache) Get(key string) (*kernel.FullDecision, bool) {
|
||||||
if c == nil || key == "" {
|
if c == nil || key == "" {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ func (c *AICache) Get(key string) (*decision.FullDecision, bool) {
|
|||||||
return cloneDecision(entry.Decision), true
|
return cloneDecision(entry.Decision), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AICache) Put(key string, variant string, ts int64, decision *decision.FullDecision) error {
|
func (c *AICache) Put(key string, variant string, ts int64, decision *kernel.FullDecision) error {
|
||||||
if c == nil || key == "" || decision == nil {
|
if c == nil || key == "" || decision == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ func (c *AICache) save() error {
|
|||||||
return writeFileAtomic(c.path, data, 0o644)
|
return writeFileAtomic(c.path, data, 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cloneDecision(src *decision.FullDecision) *decision.FullDecision {
|
func cloneDecision(src *kernel.FullDecision) *kernel.FullDecision {
|
||||||
if src == nil {
|
if src == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -117,14 +117,14 @@ func cloneDecision(src *decision.FullDecision) *decision.FullDecision {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var dst decision.FullDecision
|
var dst kernel.FullDecision
|
||||||
if err := json.Unmarshal(data, &dst); err != nil {
|
if err := json.Unmarshal(data, &dst); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &dst
|
return &dst
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeCacheKey(ctx *decision.Context, variant string, ts int64) (string, error) {
|
func computeCacheKey(ctx *kernel.Context, variant string, ts int64) (string, error) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return "", fmt.Errorf("context is nil")
|
return "", fmt.Errorf("context is nil")
|
||||||
}
|
}
|
||||||
@@ -132,9 +132,9 @@ func computeCacheKey(ctx *decision.Context, variant string, ts int64) (string, e
|
|||||||
Variant string `json:"variant"`
|
Variant string `json:"variant"`
|
||||||
Timestamp int64 `json:"ts"`
|
Timestamp int64 `json:"ts"`
|
||||||
CurrentTime string `json:"current_time"`
|
CurrentTime string `json:"current_time"`
|
||||||
Account decision.AccountInfo `json:"account"`
|
Account kernel.AccountInfo `json:"account"`
|
||||||
Positions []decision.PositionInfo `json:"positions"`
|
Positions []kernel.PositionInfo `json:"positions"`
|
||||||
CandidateCoins []decision.CandidateCoin `json:"candidate_coins"`
|
CandidateCoins []kernel.CandidateCoin `json:"candidate_coins"`
|
||||||
MarketData map[string]market.Data `json:"market"`
|
MarketData map[string]market.Data `json:"market"`
|
||||||
MarginUsedPct float64 `json:"margin_used_pct"`
|
MarginUsedPct float64 `json:"margin_used_pct"`
|
||||||
Runtime int `json:"runtime_minutes"`
|
Runtime int `json:"runtime_minutes"`
|
||||||
|
|||||||
+20
-20
@@ -13,7 +13,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/market"
|
"nofx/market"
|
||||||
"nofx/mcp"
|
"nofx/mcp"
|
||||||
"nofx/store"
|
"nofx/store"
|
||||||
@@ -34,7 +34,7 @@ type Runner struct {
|
|||||||
cfg BacktestConfig
|
cfg BacktestConfig
|
||||||
feed *DataFeed
|
feed *DataFeed
|
||||||
account *BacktestAccount
|
account *BacktestAccount
|
||||||
strategyEngine *decision.StrategyEngine
|
strategyEngine *kernel.StrategyEngine
|
||||||
|
|
||||||
decisionLogDir string
|
decisionLogDir string
|
||||||
mcpClient mcp.AIClient
|
mcpClient mcp.AIClient
|
||||||
@@ -118,7 +118,7 @@ func NewRunner(cfg BacktestConfig, mcpClient mcp.AIClient) (*Runner, error) {
|
|||||||
|
|
||||||
// Create strategy engine from backtest config for unified prompt generation
|
// Create strategy engine from backtest config for unified prompt generation
|
||||||
strategyConfig := cfg.ToStrategyConfig()
|
strategyConfig := cfg.ToStrategyConfig()
|
||||||
strategyEngine := decision.NewStrategyEngine(strategyConfig)
|
strategyEngine := kernel.NewStrategyEngine(strategyConfig)
|
||||||
|
|
||||||
r := &Runner{
|
r := &Runner{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@@ -305,7 +305,7 @@ func (r *Runner) stepOnce() error {
|
|||||||
record = rec
|
record = rec
|
||||||
|
|
||||||
var (
|
var (
|
||||||
fullDecision *decision.FullDecision
|
fullDecision *kernel.FullDecision
|
||||||
fromCache bool
|
fromCache bool
|
||||||
cacheKey string
|
cacheKey string
|
||||||
)
|
)
|
||||||
@@ -470,7 +470,7 @@ func (r *Runner) stepOnce() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Data, multiTF map[string]map[string]*market.Data, priceMap map[string]float64, callCount int) (*decision.Context, *store.DecisionRecord, error) {
|
func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Data, multiTF map[string]map[string]*market.Data, priceMap map[string]float64, callCount int) (*kernel.Context, *store.DecisionRecord, error) {
|
||||||
equity, unrealized, _ := r.account.TotalEquity(priceMap)
|
equity, unrealized, _ := r.account.TotalEquity(priceMap)
|
||||||
available := r.account.Cash()
|
available := r.account.Cash()
|
||||||
marginUsed := r.totalMarginUsed()
|
marginUsed := r.totalMarginUsed()
|
||||||
@@ -479,7 +479,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da
|
|||||||
marginPct = (marginUsed / equity) * 100
|
marginPct = (marginUsed / equity) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
accountInfo := decision.AccountInfo{
|
accountInfo := kernel.AccountInfo{
|
||||||
TotalEquity: equity,
|
TotalEquity: equity,
|
||||||
AvailableBalance: available,
|
AvailableBalance: available,
|
||||||
TotalPnL: equity - r.account.InitialBalance(),
|
TotalPnL: equity - r.account.InitialBalance(),
|
||||||
@@ -495,14 +495,14 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da
|
|||||||
candidateCoins, err := r.strategyEngine.GetCandidateCoins()
|
candidateCoins, err := r.strategyEngine.GetCandidateCoins()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Fallback to simple list if strategy engine fails
|
// Fallback to simple list if strategy engine fails
|
||||||
candidateCoins = make([]decision.CandidateCoin, 0, len(r.cfg.Symbols))
|
candidateCoins = make([]kernel.CandidateCoin, 0, len(r.cfg.Symbols))
|
||||||
for _, sym := range r.cfg.Symbols {
|
for _, sym := range r.cfg.Symbols {
|
||||||
candidateCoins = append(candidateCoins, decision.CandidateCoin{Symbol: sym, Sources: []string{"backtest"}})
|
candidateCoins = append(candidateCoins, kernel.CandidateCoin{Symbol: sym, Sources: []string{"backtest"}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime := int((ts - int64(r.cfg.StartTS*1000)) / 60000)
|
runtime := int((ts - int64(r.cfg.StartTS*1000)) / 60000)
|
||||||
ctx := &decision.Context{
|
ctx := &kernel.Context{
|
||||||
CurrentTime: time.UnixMilli(ts).UTC().Format("2006-01-02 15:04:05 UTC"),
|
CurrentTime: time.UnixMilli(ts).UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||||
RuntimeMinutes: runtime,
|
RuntimeMinutes: runtime,
|
||||||
CallCount: callCount,
|
CallCount: callCount,
|
||||||
@@ -566,7 +566,7 @@ func (r *Runner) buildDecisionContext(ts int64, marketData map[string]*market.Da
|
|||||||
return ctx, record, nil
|
return ctx, record, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *decision.FullDecision) {
|
func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *kernel.FullDecision) {
|
||||||
record.InputPrompt = full.UserPrompt
|
record.InputPrompt = full.UserPrompt
|
||||||
record.CoTTrace = full.CoTTrace
|
record.CoTTrace = full.CoTTrace
|
||||||
if len(full.Decisions) > 0 {
|
if len(full.Decisions) > 0 {
|
||||||
@@ -576,12 +576,12 @@ func (r *Runner) fillDecisionRecord(record *store.DecisionRecord, full *decision
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) invokeAIWithRetry(ctx *decision.Context) (*decision.FullDecision, error) {
|
func (r *Runner) invokeAIWithRetry(ctx *kernel.Context) (*kernel.FullDecision, error) {
|
||||||
var lastErr error
|
var lastErr error
|
||||||
for attempt := 0; attempt < aiDecisionMaxRetries; attempt++ {
|
for attempt := 0; attempt < aiDecisionMaxRetries; attempt++ {
|
||||||
// Use GetFullDecisionWithStrategy with the pre-configured strategy engine
|
// Use GetFullDecisionWithStrategy with the pre-configured strategy engine
|
||||||
// This ensures backtest uses the same unified prompt generation as live trading
|
// This ensures backtest uses the same unified prompt generation as live trading
|
||||||
fd, err := decision.GetFullDecisionWithStrategy(
|
fd, err := kernel.GetFullDecisionWithStrategy(
|
||||||
ctx,
|
ctx,
|
||||||
r.mcpClient,
|
r.mcpClient,
|
||||||
r.strategyEngine,
|
r.strategyEngine,
|
||||||
@@ -597,7 +597,7 @@ func (r *Runner) invokeAIWithRetry(ctx *decision.Context) (*decision.FullDecisio
|
|||||||
return nil, lastErr
|
return nil, lastErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) executeDecision(dec decision.Decision, priceMap map[string]float64, ts int64, cycle int) (store.DecisionAction, []TradeEvent, string, error) {
|
func (r *Runner) executeDecision(dec kernel.Decision, priceMap map[string]float64, ts int64, cycle int) (store.DecisionAction, []TradeEvent, string, error) {
|
||||||
symbol := dec.Symbol
|
symbol := dec.Symbol
|
||||||
usedLeverage := r.resolveLeverage(dec.Leverage, symbol)
|
usedLeverage := r.resolveLeverage(dec.Leverage, symbol)
|
||||||
actionRecord := store.DecisionAction{
|
actionRecord := store.DecisionAction{
|
||||||
@@ -739,7 +739,7 @@ func (r *Runner) executeDecision(dec decision.Decision, priceMap map[string]floa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) determineQuantity(dec decision.Decision, price float64) float64 {
|
func (r *Runner) determineQuantity(dec kernel.Decision, price float64) float64 {
|
||||||
snapshot := r.snapshotState()
|
snapshot := r.snapshotState()
|
||||||
equity := snapshot.Equity
|
equity := snapshot.Equity
|
||||||
if equity <= 0 {
|
if equity <= 0 {
|
||||||
@@ -777,7 +777,7 @@ func (r *Runner) determineQuantity(dec decision.Decision, price float64) float64
|
|||||||
return qty
|
return qty
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) determineCloseQuantity(symbol, side string, dec decision.Decision) float64 {
|
func (r *Runner) determineCloseQuantity(symbol, side string, dec kernel.Decision) float64 {
|
||||||
for _, pos := range r.account.Positions() {
|
for _, pos := range r.account.Positions() {
|
||||||
if pos.Symbol == strings.ToUpper(symbol) && pos.Side == side {
|
if pos.Symbol == strings.ToUpper(symbol) && pos.Side == side {
|
||||||
return pos.Quantity
|
return pos.Quantity
|
||||||
@@ -831,12 +831,12 @@ func (r *Runner) snapshotPositions(priceMap map[string]float64) []store.Position
|
|||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) convertPositions(priceMap map[string]float64) []decision.PositionInfo {
|
func (r *Runner) convertPositions(priceMap map[string]float64) []kernel.PositionInfo {
|
||||||
positions := r.account.Positions()
|
positions := r.account.Positions()
|
||||||
list := make([]decision.PositionInfo, 0, len(positions))
|
list := make([]kernel.PositionInfo, 0, len(positions))
|
||||||
for _, pos := range positions {
|
for _, pos := range positions {
|
||||||
price := priceMap[pos.Symbol]
|
price := priceMap[pos.Symbol]
|
||||||
list = append(list, decision.PositionInfo{
|
list = append(list, kernel.PositionInfo{
|
||||||
Symbol: pos.Symbol,
|
Symbol: pos.Symbol,
|
||||||
Side: pos.Side,
|
Side: pos.Side,
|
||||||
EntryPrice: pos.EntryPrice,
|
EntryPrice: pos.EntryPrice,
|
||||||
@@ -1416,7 +1416,7 @@ func snapshotsToMap(snaps []PositionSnapshot) map[string]PositionSnapshot {
|
|||||||
return positions
|
return positions
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision {
|
func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decision {
|
||||||
if len(decisions) <= 1 {
|
if len(decisions) <= 1 {
|
||||||
return decisions
|
return decisions
|
||||||
}
|
}
|
||||||
@@ -1434,7 +1434,7 @@ func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]decision.Decision, len(decisions))
|
result := make([]kernel.Decision, len(decisions))
|
||||||
copy(result, decisions)
|
copy(result, decisions)
|
||||||
|
|
||||||
sort.Slice(result, func(i, j int) bool {
|
sort.Slice(result, func(i, j int) bool {
|
||||||
|
|||||||
+9
-9
@@ -9,7 +9,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/logger"
|
"nofx/logger"
|
||||||
"nofx/market"
|
"nofx/market"
|
||||||
"nofx/mcp"
|
"nofx/mcp"
|
||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
|
|
||||||
// TraderExecutor interface for executing trades
|
// TraderExecutor interface for executing trades
|
||||||
type TraderExecutor interface {
|
type TraderExecutor interface {
|
||||||
ExecuteDecision(decision *decision.Decision) error
|
ExecuteDecision(decision *kernel.Decision) error
|
||||||
GetBalance() (map[string]interface{}, error)
|
GetBalance() (map[string]interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ func (e *DebateEngine) runDebate(session *store.DebateSessionWithDetails, strate
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// Create strategy engine for building context
|
// Create strategy engine for building context
|
||||||
strategyEngine := decision.NewStrategyEngine(strategyConfig)
|
strategyEngine := kernel.NewStrategyEngine(strategyConfig)
|
||||||
|
|
||||||
// Build market context using strategy config
|
// Build market context using strategy config
|
||||||
ctx, err := e.buildMarketContext(session, strategyEngine)
|
ctx, err := e.buildMarketContext(session, strategyEngine)
|
||||||
@@ -289,7 +289,7 @@ func (e *DebateEngine) runDebate(session *store.DebateSessionWithDetails, strate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildMarketContext builds the market context using strategy engine
|
// buildMarketContext builds the market context using strategy engine
|
||||||
func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetails, strategyEngine *decision.StrategyEngine) (*decision.Context, error) {
|
func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetails, strategyEngine *kernel.StrategyEngine) (*kernel.Context, error) {
|
||||||
config := strategyEngine.GetConfig()
|
config := strategyEngine.GetConfig()
|
||||||
|
|
||||||
// Get candidate coins
|
// Get candidate coins
|
||||||
@@ -336,11 +336,11 @@ func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetail
|
|||||||
oiRankingData := strategyEngine.FetchOIRankingData()
|
oiRankingData := strategyEngine.FetchOIRankingData()
|
||||||
|
|
||||||
// Build context
|
// Build context
|
||||||
ctx := &decision.Context{
|
ctx := &kernel.Context{
|
||||||
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||||
RuntimeMinutes: 0,
|
RuntimeMinutes: 0,
|
||||||
CallCount: 1,
|
CallCount: 1,
|
||||||
Account: decision.AccountInfo{
|
Account: kernel.AccountInfo{
|
||||||
TotalEquity: 1000.0, // Simulated for debate
|
TotalEquity: 1000.0, // Simulated for debate
|
||||||
AvailableBalance: 1000.0,
|
AvailableBalance: 1000.0,
|
||||||
UnrealizedPnL: 0,
|
UnrealizedPnL: 0,
|
||||||
@@ -350,7 +350,7 @@ func (e *DebateEngine) buildMarketContext(session *store.DebateSessionWithDetail
|
|||||||
MarginUsedPct: 0,
|
MarginUsedPct: 0,
|
||||||
PositionCount: 0,
|
PositionCount: 0,
|
||||||
},
|
},
|
||||||
Positions: []decision.PositionInfo{},
|
Positions: []kernel.PositionInfo{},
|
||||||
CandidateCoins: candidates,
|
CandidateCoins: candidates,
|
||||||
PromptVariant: session.PromptVariant,
|
PromptVariant: session.PromptVariant,
|
||||||
MarketDataMap: marketDataMap,
|
MarketDataMap: marketDataMap,
|
||||||
@@ -539,7 +539,7 @@ func (e *DebateEngine) getParticipantResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// collectVotes collects final votes from all participants
|
// collectVotes collects final votes from all participants
|
||||||
func (e *DebateEngine) collectVotes(session *store.DebateSessionWithDetails, strategyEngine *decision.StrategyEngine, allMessages []*store.DebateMessage) ([]*store.DebateVote, error) {
|
func (e *DebateEngine) collectVotes(session *store.DebateSessionWithDetails, strategyEngine *kernel.StrategyEngine, allMessages []*store.DebateMessage) ([]*store.DebateVote, error) {
|
||||||
var votes []*store.DebateVote
|
var votes []*store.DebateVote
|
||||||
|
|
||||||
// Build voting context
|
// Build voting context
|
||||||
@@ -1009,7 +1009,7 @@ func (e *DebateEngine) ExecuteConsensus(sessionID string, executor TraderExecuto
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create decision
|
// Create decision
|
||||||
tradeDecision := &decision.Decision{
|
tradeDecision := &kernel.Decision{
|
||||||
Symbol: session.Symbol,
|
Symbol: session.Symbol,
|
||||||
Action: action,
|
Action: action,
|
||||||
Leverage: session.FinalDecision.Leverage,
|
Leverage: session.FinalDecision.Leverage,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package decision
|
package kernel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"nofx/debate"
|
"nofx/debate"
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/logger"
|
"nofx/logger"
|
||||||
"nofx/store"
|
"nofx/store"
|
||||||
"nofx/trader"
|
"nofx/trader"
|
||||||
@@ -19,7 +19,7 @@ type TraderExecutorAdapter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteDecision executes a trading decision
|
// ExecuteDecision executes a trading decision
|
||||||
func (a *TraderExecutorAdapter) ExecuteDecision(d *decision.Decision) error {
|
func (a *TraderExecutorAdapter) ExecuteDecision(d *kernel.Decision) error {
|
||||||
return a.autoTrader.ExecuteDecision(d)
|
return a.autoTrader.ExecuteDecision(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-22
@@ -4,7 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"nofx/decision"
|
"nofx/kernel"
|
||||||
"nofx/experience"
|
"nofx/experience"
|
||||||
"nofx/logger"
|
"nofx/logger"
|
||||||
"nofx/market"
|
"nofx/market"
|
||||||
@@ -104,7 +104,7 @@ type AutoTrader struct {
|
|||||||
trader Trader // Use Trader interface (supports multiple platforms)
|
trader Trader // Use Trader interface (supports multiple platforms)
|
||||||
mcpClient mcp.AIClient
|
mcpClient mcp.AIClient
|
||||||
store *store.Store // Data storage (decision records, etc.)
|
store *store.Store // Data storage (decision records, etc.)
|
||||||
strategyEngine *decision.StrategyEngine // Strategy engine (uses strategy configuration)
|
strategyEngine *kernel.StrategyEngine // Strategy engine (uses strategy configuration)
|
||||||
cycleNumber int // Current cycle number
|
cycleNumber int // Current cycle number
|
||||||
initialBalance float64
|
initialBalance float64
|
||||||
dailyPnL float64
|
dailyPnL float64
|
||||||
@@ -310,7 +310,7 @@ func NewAutoTrader(config AutoTraderConfig, st *store.Store, userID string) (*Au
|
|||||||
if config.StrategyConfig == nil {
|
if config.StrategyConfig == nil {
|
||||||
return nil, fmt.Errorf("[%s] strategy not configured", config.Name)
|
return nil, fmt.Errorf("[%s] strategy not configured", config.Name)
|
||||||
}
|
}
|
||||||
strategyEngine := decision.NewStrategyEngine(config.StrategyConfig)
|
strategyEngine := kernel.NewStrategyEngine(config.StrategyConfig)
|
||||||
logger.Infof("✓ [%s] Using strategy engine (strategy configuration loaded)", config.Name)
|
logger.Infof("✓ [%s] Using strategy engine (strategy configuration loaded)", config.Name)
|
||||||
|
|
||||||
return &AutoTrader{
|
return &AutoTrader{
|
||||||
@@ -524,7 +524,7 @@ func (at *AutoTrader) runCycle() error {
|
|||||||
|
|
||||||
// 5. Use strategy engine to call AI for decision
|
// 5. Use strategy engine to call AI for decision
|
||||||
logger.Infof("🤖 Requesting AI analysis and decision... [Strategy Engine]")
|
logger.Infof("🤖 Requesting AI analysis and decision... [Strategy Engine]")
|
||||||
aiDecision, err := decision.GetFullDecisionWithStrategy(ctx, at.mcpClient, at.strategyEngine, "balanced")
|
aiDecision, err := kernel.GetFullDecisionWithStrategy(ctx, at.mcpClient, at.strategyEngine, "balanced")
|
||||||
|
|
||||||
if aiDecision != nil && aiDecision.AIRequestDurationMs > 0 {
|
if aiDecision != nil && aiDecision.AIRequestDurationMs > 0 {
|
||||||
record.AIRequestDurationMs = aiDecision.AIRequestDurationMs
|
record.AIRequestDurationMs = aiDecision.AIRequestDurationMs
|
||||||
@@ -585,8 +585,8 @@ func (at *AutoTrader) runCycle() error {
|
|||||||
// logger.Infof(strings.Repeat("-", 70) + "\n")
|
// logger.Infof(strings.Repeat("-", 70) + "\n")
|
||||||
|
|
||||||
// 7. Print AI decisions
|
// 7. Print AI decisions
|
||||||
// logger.Infof("📋 AI decision list (%d items):\n", len(decision.Decisions))
|
// logger.Infof("📋 AI decision list (%d items):\n", len(kernel.Decisions))
|
||||||
// for i, d := range decision.Decisions {
|
// for i, d := range kernel.Decisions {
|
||||||
// logger.Infof(" [%d] %s: %s - %s", i+1, d.Symbol, d.Action, d.Reasoning)
|
// logger.Infof(" [%d] %s: %s - %s", i+1, d.Symbol, d.Action, d.Reasoning)
|
||||||
// if d.Action == "open_long" || d.Action == "open_short" {
|
// if d.Action == "open_long" || d.Action == "open_short" {
|
||||||
// logger.Infof(" Leverage: %dx | Position: %.2f USDT | Stop loss: %.4f | Take profit: %.4f",
|
// logger.Infof(" Leverage: %dx | Position: %.2f USDT | Stop loss: %.4f | Take profit: %.4f",
|
||||||
@@ -664,7 +664,7 @@ func (at *AutoTrader) runCycle() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// buildTradingContext builds trading context
|
// buildTradingContext builds trading context
|
||||||
func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
func (at *AutoTrader) buildTradingContext() (*kernel.Context, error) {
|
||||||
// 1. Get account information
|
// 1. Get account information
|
||||||
balance, err := at.trader.GetBalance()
|
balance, err := at.trader.GetBalance()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -701,7 +701,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
return nil, fmt.Errorf("failed to get positions: %w", err)
|
return nil, fmt.Errorf("failed to get positions: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var positionInfos []decision.PositionInfo
|
var positionInfos []kernel.PositionInfo
|
||||||
totalMarginUsed := 0.0
|
totalMarginUsed := 0.0
|
||||||
|
|
||||||
// Current position key set (for cleaning up closed position records)
|
// Current position key set (for cleaning up closed position records)
|
||||||
@@ -768,7 +768,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
peakPnlPct := at.peakPnLCache[posKey]
|
peakPnlPct := at.peakPnLCache[posKey]
|
||||||
at.peakPnLCacheMutex.RUnlock()
|
at.peakPnLCacheMutex.RUnlock()
|
||||||
|
|
||||||
positionInfos = append(positionInfos, decision.PositionInfo{
|
positionInfos = append(positionInfos, kernel.PositionInfo{
|
||||||
Symbol: symbol,
|
Symbol: symbol,
|
||||||
Side: side,
|
Side: side,
|
||||||
EntryPrice: entryPrice,
|
EntryPrice: entryPrice,
|
||||||
@@ -820,13 +820,13 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
logger.Infof("📋 [%s] Strategy leverage config: BTC/ETH=%dx, Altcoin=%dx", at.name, btcEthLeverage, altcoinLeverage)
|
logger.Infof("📋 [%s] Strategy leverage config: BTC/ETH=%dx, Altcoin=%dx", at.name, btcEthLeverage, altcoinLeverage)
|
||||||
|
|
||||||
// 6. Build context
|
// 6. Build context
|
||||||
ctx := &decision.Context{
|
ctx := &kernel.Context{
|
||||||
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
CurrentTime: time.Now().UTC().Format("2006-01-02 15:04:05 UTC"),
|
||||||
RuntimeMinutes: int(time.Since(at.startTime).Minutes()),
|
RuntimeMinutes: int(time.Since(at.startTime).Minutes()),
|
||||||
CallCount: at.callCount,
|
CallCount: at.callCount,
|
||||||
BTCETHLeverage: btcEthLeverage,
|
BTCETHLeverage: btcEthLeverage,
|
||||||
AltcoinLeverage: altcoinLeverage,
|
AltcoinLeverage: altcoinLeverage,
|
||||||
Account: decision.AccountInfo{
|
Account: kernel.AccountInfo{
|
||||||
TotalEquity: totalEquity,
|
TotalEquity: totalEquity,
|
||||||
AvailableBalance: availableBalance,
|
AvailableBalance: availableBalance,
|
||||||
UnrealizedPnL: totalUnrealizedProfit,
|
UnrealizedPnL: totalUnrealizedProfit,
|
||||||
@@ -859,7 +859,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
exitTimeStr = time.Unix(trade.ExitTime, 0).UTC().Format("01-02 15:04 UTC")
|
exitTimeStr = time.Unix(trade.ExitTime, 0).UTC().Format("01-02 15:04 UTC")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.RecentOrders = append(ctx.RecentOrders, decision.RecentOrder{
|
ctx.RecentOrders = append(ctx.RecentOrders, kernel.RecentOrder{
|
||||||
Symbol: trade.Symbol,
|
Symbol: trade.Symbol,
|
||||||
Side: trade.Side,
|
Side: trade.Side,
|
||||||
EntryPrice: trade.EntryPrice,
|
EntryPrice: trade.EntryPrice,
|
||||||
@@ -881,7 +881,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
} else if stats.TotalTrades == 0 {
|
} else if stats.TotalTrades == 0 {
|
||||||
logger.Infof("⚠️ [%s] GetFullStats returned 0 trades (traderID=%s)", at.name, at.id)
|
logger.Infof("⚠️ [%s] GetFullStats returned 0 trades (traderID=%s)", at.name, at.id)
|
||||||
} else {
|
} else {
|
||||||
ctx.TradingStats = &decision.TradingStats{
|
ctx.TradingStats = &kernel.TradingStats{
|
||||||
TotalTrades: stats.TotalTrades,
|
TotalTrades: stats.TotalTrades,
|
||||||
WinRate: stats.WinRate,
|
WinRate: stats.WinRate,
|
||||||
ProfitFactor: stats.ProfitFactor,
|
ProfitFactor: stats.ProfitFactor,
|
||||||
@@ -933,7 +933,7 @@ func (at *AutoTrader) buildTradingContext() (*decision.Context, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeDecisionWithRecord executes AI decision and records detailed information
|
// executeDecisionWithRecord executes AI decision and records detailed information
|
||||||
func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
func (at *AutoTrader) executeDecisionWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error {
|
||||||
switch decision.Action {
|
switch decision.Action {
|
||||||
case "open_long":
|
case "open_long":
|
||||||
return at.executeOpenLongWithRecord(decision, actionRecord)
|
return at.executeOpenLongWithRecord(decision, actionRecord)
|
||||||
@@ -953,7 +953,7 @@ func (at *AutoTrader) executeDecisionWithRecord(decision *decision.Decision, act
|
|||||||
|
|
||||||
// ExecuteDecision executes a trading decision from external sources (e.g., debate consensus)
|
// ExecuteDecision executes a trading decision from external sources (e.g., debate consensus)
|
||||||
// This is a public method that can be called by other modules
|
// This is a public method that can be called by other modules
|
||||||
func (at *AutoTrader) ExecuteDecision(d *decision.Decision) error {
|
func (at *AutoTrader) ExecuteDecision(d *kernel.Decision) error {
|
||||||
logger.Infof("[%s] Executing external decision: %s %s", at.name, d.Action, d.Symbol)
|
logger.Infof("[%s] Executing external decision: %s %s", at.name, d.Action, d.Symbol)
|
||||||
|
|
||||||
// Create a minimal action record for tracking
|
// Create a minimal action record for tracking
|
||||||
@@ -979,7 +979,7 @@ func (at *AutoTrader) ExecuteDecision(d *decision.Decision) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeOpenLongWithRecord executes open long position and records detailed information
|
// executeOpenLongWithRecord executes open long position and records detailed information
|
||||||
func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
func (at *AutoTrader) executeOpenLongWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error {
|
||||||
logger.Infof(" 📈 Open long: %s", decision.Symbol)
|
logger.Infof(" 📈 Open long: %s", decision.Symbol)
|
||||||
|
|
||||||
// ⚠️ Get current positions for multiple checks
|
// ⚠️ Get current positions for multiple checks
|
||||||
@@ -1096,7 +1096,7 @@ func (at *AutoTrader) executeOpenLongWithRecord(decision *decision.Decision, act
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeOpenShortWithRecord executes open short position and records detailed information
|
// executeOpenShortWithRecord executes open short position and records detailed information
|
||||||
func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
func (at *AutoTrader) executeOpenShortWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error {
|
||||||
logger.Infof(" 📉 Open short: %s", decision.Symbol)
|
logger.Infof(" 📉 Open short: %s", decision.Symbol)
|
||||||
|
|
||||||
// ⚠️ Get current positions for multiple checks
|
// ⚠️ Get current positions for multiple checks
|
||||||
@@ -1213,7 +1213,7 @@ func (at *AutoTrader) executeOpenShortWithRecord(decision *decision.Decision, ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeCloseLongWithRecord executes close long position and records detailed information
|
// executeCloseLongWithRecord executes close long position and records detailed information
|
||||||
func (at *AutoTrader) executeCloseLongWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
func (at *AutoTrader) executeCloseLongWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error {
|
||||||
logger.Infof(" 🔄 Close long: %s", decision.Symbol)
|
logger.Infof(" 🔄 Close long: %s", decision.Symbol)
|
||||||
|
|
||||||
// Get current price
|
// Get current price
|
||||||
@@ -1277,7 +1277,7 @@ func (at *AutoTrader) executeCloseLongWithRecord(decision *decision.Decision, ac
|
|||||||
}
|
}
|
||||||
|
|
||||||
// executeCloseShortWithRecord executes close short position and records detailed information
|
// executeCloseShortWithRecord executes close short position and records detailed information
|
||||||
func (at *AutoTrader) executeCloseShortWithRecord(decision *decision.Decision, actionRecord *store.DecisionAction) error {
|
func (at *AutoTrader) executeCloseShortWithRecord(decision *kernel.Decision, actionRecord *store.DecisionAction) error {
|
||||||
logger.Infof(" 🔄 Close short: %s", decision.Symbol)
|
logger.Infof(" 🔄 Close short: %s", decision.Symbol)
|
||||||
|
|
||||||
// Get current price
|
// Get current price
|
||||||
@@ -1392,7 +1392,7 @@ func (at *AutoTrader) GetSystemPromptTemplate() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// saveEquitySnapshot saves equity snapshot independently (for drawing profit curve, decoupled from AI decision)
|
// saveEquitySnapshot saves equity snapshot independently (for drawing profit curve, decoupled from AI decision)
|
||||||
func (at *AutoTrader) saveEquitySnapshot(ctx *decision.Context) {
|
func (at *AutoTrader) saveEquitySnapshot(ctx *kernel.Context) {
|
||||||
if at.store == nil || ctx == nil {
|
if at.store == nil || ctx == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -1624,7 +1624,7 @@ func calculatePnLPercentage(unrealizedPnl, marginUsed float64) float64 {
|
|||||||
|
|
||||||
// sortDecisionsByPriority sorts decisions: close positions first, then open positions, finally hold/wait
|
// sortDecisionsByPriority sorts decisions: close positions first, then open positions, finally hold/wait
|
||||||
// This avoids position stacking overflow when changing positions
|
// This avoids position stacking overflow when changing positions
|
||||||
func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision {
|
func sortDecisionsByPriority(decisions []kernel.Decision) []kernel.Decision {
|
||||||
if len(decisions) <= 1 {
|
if len(decisions) <= 1 {
|
||||||
return decisions
|
return decisions
|
||||||
}
|
}
|
||||||
@@ -1644,7 +1644,7 @@ func sortDecisionsByPriority(decisions []decision.Decision) []decision.Decision
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy decision list
|
// Copy decision list
|
||||||
sorted := make([]decision.Decision, len(decisions))
|
sorted := make([]kernel.Decision, len(decisions))
|
||||||
copy(sorted, decisions)
|
copy(sorted, decisions)
|
||||||
|
|
||||||
// Sort by priority
|
// Sort by priority
|
||||||
|
|||||||
Reference in New Issue
Block a user