Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
8e5a35e
Add multi-timeframe data analysis support
zhouyongyou Oct 31, 2025
396df86
Cancel orphan orders when position is closed
zhouyongyou Oct 31, 2025
cf8bf6e
Merge branch 'dev' of https://github.com/tinkle-community/nofx into dev
zhouyongyou Oct 31, 2025
17ad15c
refactor: unify to simplified Chinese in logs
zhouyongyou Oct 31, 2025
1cc43ce
Merge branch 'tinkle-community:dev' into dev
zhouyongyou Oct 31, 2025
0b9d696
添加震荡交易策略 + 买卖压力分析
zhouyongyou Nov 1, 2025
4fbb4ec
Merge branch 'dev' into dev
zhouyongyou Nov 1, 2025
e708c99
feat: 添加自适应双策略系统(震荡 + 趋势)
zhouyongyou Nov 1, 2025
e51cbf4
Merge branch 'tinkle-community:dev' into dev
zhouyongyou Nov 1, 2025
d2af549
feat(config): 增加新闻源配置与数据库迁移支持
vicnoah Nov 1, 2025
6c03d24
Docker port mapping prompts.
SkywalkerJi Nov 1, 2025
88a7055
feat: 添加技术位优先止盈 + 追踪止损(阶段1)
zhouyongyou Nov 1, 2025
f02c5df
Merge branch 'tinkle-community:dev' into dev
zhouyongyou Nov 1, 2025
a58be23
fix: 修正盈亏百分比计算错误(未考虑杠杆)
zhouyongyou Nov 1, 2025
b1e4a75
fix: 修复中文引号导致的 Go 编译错误
zhouyongyou Nov 1, 2025
6553b81
fix: config.json.example 缺少 coin_pool_api_url 和 oi_top_api_url 字段
zhouyongyou Nov 1, 2025
b908fac
feat: 創建 adaptive.txt 自適應雙策略模板
zhouyongyou Nov 1, 2025
a2411d2
refactor: 優化 engine.go 模板加載邏輯,避免策略重複
zhouyongyou Nov 1, 2025
bac5744
refactor: 移除 engine.go 冗餘硬編碼策略,優化模板系統
zhouyongyou Nov 1, 2025
1fb5ecb
chore: 移除未實現功能的設計文檔
zhouyongyou Nov 1, 2025
8faa2b3
refactor: 恢復模板加載失敗時的簡化版本 fallback
zhouyongyou Nov 1, 2025
04419b4
fix: 修复编辑交易员时「AI模型配置不存在或未启用」错误
zhouyongyou Nov 1, 2025
093d521
Merge branch 'dev' of https://github.com/tinkle-community/nofx into dev
zhouyongyou Nov 1, 2025
82660b1
feat: 添加部分平仓和动态止盈止损功能
zhouyongyou Nov 1, 2025
7f741c4
docs(adaptive): 添加數據解釋和參考示例,採用"解釋+參考"方式
zhouyongyou Nov 1, 2025
b145471
docs(adaptive): 添加數據解釋和參考示例,採用"解釋+參考"方式
zhouyongyou Nov 1, 2025
f15d82b
修復生產錯誤:刪除 adaptive.txt 中不支持的動態調整功能描述
zhouyongyou Nov 1, 2025
498cec0
修復關鍵 BUG:validActions 缺少新動作導致驗證失敗
zhouyongyou Nov 1, 2025
ed8bb94
更新 logger:支持新增的三個動作類型
zhouyongyou Nov 1, 2025
ed34201
修復關鍵缺陷:添加 CancelStopOrders 方法避免多個止損單共存
zhouyongyou Nov 1, 2025
ab3cab9
fix: 統一 handleTraderList 返回完整 AI model ID(保持與 handleGetTraderConfig 一致)
zhouyongyou Nov 1, 2025
dc3bd5f
Merge branch 'dev' of https://github.com/tinkle-community/nofx into dev
zhouyongyou Nov 1, 2025
adb7e55
Merge branch 'dev' into feature/partial-close-dynamic-tpsl
zhouyongyou Nov 1, 2025
37df286
fix: 恢復 adaptive.txt 中被誤刪的動態止盈止損說明
zhouyongyou Nov 1, 2025
e76792c
fix: 修復 Hyperliquid CancelStopOrders 編譯錯誤
zhouyongyou Nov 1, 2025
2d4b14e
fix: 修復初始余額顯示錯誤(使用當前淨值而非配置值)
zhouyongyou Nov 1, 2025
8ca2071
fix: 修復首次運行時數據庫初始化失敗問題
zhouyongyou Nov 2, 2025
45ec645
fix: 補充 System Prompt 中新動作的格式說明
zhouyongyou Nov 2, 2025
46053c5
fix: 添加 HTTP/2 stream error 到可重試錯誤列表
zhouyongyou Nov 2, 2025
5c9b396
fix: detect and record stop-loss/take-profit auto-close trades
Xeron2000 Nov 2, 2025
fed3ce9
fix: 修复部分平仓盈利计算错误
zhouyongyou Nov 2, 2025
0eb05dd
fix: 过滤幽灵持仓 - 跳过 quantity=0 的持仓防止 AI 误判
zhouyongyou Nov 2, 2025
89814b6
Merge branch 'dev' into dev
vicnoah Nov 2, 2025
c5516fc
fix: Update model validation in handleSaveModelConfig to support both…
xqliu Nov 2, 2025
b3b68b2
refactor(prompts): unify action schema & optimize trading discipline
zhouyongyou Nov 3, 2025
34f61af
feat: 使用容器创建初始化所需的config.json和config.db
ljh740 Nov 3, 2025
cec5100
Merge branch 'NoFxAiOS:dev' into dev-fix
ljh740 Nov 3, 2025
9601d43
fix(binance): 同步服务器时间并在 -1021 时自动重试,稳定签名调用
xiehs211 Nov 3, 2025
bead75e
merge: Sync with NoFxAiOS/dev - adopt WebSocket architecture
zhouyongyou Nov 3, 2025
8d12514
feat(market): Add 15m/1h timeframes for comprehensive trend analysis
zhouyongyou Nov 3, 2025
c330e40
fix:当前BTC/ETH与主流币杠杆倍数配置不一致并且开仓只有BTC时,大模型会返回其他币种的开仓倍数与当前持仓BTC倍数一致,导致开仓失败
Nov 3, 2025
221b03b
refactor(prompts): enhance partial_close guidance in adaptive & nof1
zhouyongyou Nov 3, 2025
bb2edfc
feat(market): Add WebSocket stream limit protection with auto-downgrade
zhouyongyou Nov 3, 2025
366ae87
style: format Go code with go fmt
zhouyongyou Nov 3, 2025
563fb0e
fix: resolve go vet warnings for non-constant format strings
zhouyongyou Nov 3, 2025
1e1f4cc
fix: define CardProps interface and fix prop name mismatch
zhouyongyou Nov 3, 2025
041d6d4
fix(prompts): add explicit stop-loss direction logic for long/short p…
zhouyongyou Nov 3, 2025
4e8af52
fix(api): query actual exchange balance when creating trader
zhouyongyou Nov 3, 2025
a29eea2
chore(web): add peer dependency markers to package-lock.json
zhouyongyou Nov 3, 2025
7f87478
refactor: align variable naming and add Binance time sync
zhouyongyou Nov 3, 2025
381240c
Merge branch 'NoFxAiOS:dev' into feature/partial-close-dynamic-tpsl
zhouyongyou Nov 3, 2025
cc9e428
feat(exchange): Add comprehensive Binance time sync (PR #313)
zhouyongyou Nov 3, 2025
a95faf6
fix(trader): Correct leverage ratio in AI prompt (PR #318)
zhouyongyou Nov 3, 2025
b3e9707
fix: Remove merge conflict markers and add WebSocket limit warnings
zhouyongyou Nov 3, 2025
1d78553
fix: Fix AI model validation and 4h kline storage (PR #337)
zhouyongyou Nov 3, 2025
527dcd6
feat: Add position snapshot to track auto-closed positions (PR #231)
zhouyongyou Nov 3, 2025
b0de71b
feat: 支持更新 Trader 的系统提示词模板
xqliu Nov 3, 2025
75c0408
feat: Add Telegram news integration for market sentiment analysis (PR…
zhouyongyou Nov 3, 2025
c1f080f
feat: 添加前端对 SystemPromptTemplate 更新的完整支持
xqliu Nov 3, 2025
cd479d4
feat: Auto-initialize config files in Docker environment (PR #310)
zhouyongyou Nov 3, 2025
0745fdf
feat: Add scan_interval_minutes configuration support (PR #338)
zhouyongyou Nov 3, 2025
aef4cf5
style: fix alignment in server.go after merge
zhouyongyou Nov 3, 2025
5f6ff9d
feat: add system_prompt_template to GetTraderConfig response
zhouyongyou Nov 3, 2025
3d565c9
feat: Add SystemPromptTemplate edit support (PR #339)
zhouyongyou Nov 3, 2025
785a25e
refactor: implement Code Review suggestions for PR #231
zhouyongyou Nov 3, 2025
4edf83b
Merge branch 'NoFxAiOS:dev' into feature/partial-close-dynamic-tpsl
zhouyongyou Nov 3, 2025
16c3745
fix: GetTraderConfig missing critical fields in SELECT/Scan
zhouyongyou Nov 3, 2025
f34efee
merge: Integrate PR #229 UT infrastructure with fallback logic
zhouyongyou Nov 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 160 additions & 94 deletions api/server.go

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ func GenerateOTPSecret() (string, error) {
if err != nil {
return "", err
}

key, err := totp.Generate(totp.GenerateOpts{
Issuer: OTPIssuer,
AccountName: uuid.New().String(),
})
if err != nil {
return "", err
}

return key.Secret(), nil
}

Expand Down Expand Up @@ -118,4 +118,4 @@ func ValidateJWT(tokenString string) (*Claims, error) {
// GetOTPQRCodeURL 获取OTP二维码URL
func GetOTPQRCodeURL(secret, email string) string {
return fmt.Sprintf("otpauth://totp/%s:%s?secret=%s&issuer=%s", OTPIssuer, email, secret, OTPIssuer)
}
}
21 changes: 20 additions & 1 deletion config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,28 @@
"ADAUSDT",
"HYPEUSDT"
],
"coin_pool_api_url": "",
"oi_top_api_url": "",
"api_server_port": 8080,
"max_daily_loss": 10.0,
"max_drawdown": 20.0,
"stop_trading_minutes": 60,
"jwt_secret": "Qk0kAa+d0iIEzXVHXbNbm+UaN3RNabmWtH8rDWZ5OPf+4GX8pBflAHodfpbipVMyrw1fsDanHsNBjhgbDeK9Jg=="
"jwt_secret": "Qk0kAa+d0iIEzXVHXbNbm+UaN3RNabmWtH8rDWZ5OPf+4GX8pBflAHodfpbipVMyrw1fsDanHsNBjhgbDeK9Jg==",
// 建议使用时删除,目前新闻源功能还比较初级
"news": [
{
"provider": "telegram",
"telegram": {
// 国外服务器无需配置
"proxyurl": "http://127.0.0.1:18080"
},
"channels": [
{
// 如t.me/ChannelPANews,id为ChannelPANews
"id": "ChannelPANews",
"name": "PANews"
}
]
}
]
}
43 changes: 40 additions & 3 deletions config/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,25 @@ type UserSignalSource struct {
UpdatedAt time.Time `json:"updated_at"`
}

// NewsConfig 新闻配置
type NewsConfig struct {
Provider string `json:"provider"` // 新闻搜索器名称: telegram
Telegram NewsConfigTelegram `json:"telegram"` // telegram客户端配置
TelegramChannel []NewsConfigTelegramChannel `json:"channels"` // telegram频道配置
}

// NewsConfigTelegram telegram配置
type NewsConfigTelegram struct {
BaseURL string `json:"baseurl"` // 基础url
ProxyURL string `json:"proxyurl"` // 代理url
}

// NewsConfigTelegramChannel 电报频道配置
type NewsConfigTelegramChannel struct {
ID string `json:"id"` // 频道id
Name string `json:"name"` // 频道名称
}

// GenerateOTPSecret 生成OTP密钥
func GenerateOTPSecret() (string, error) {
secret := make([]byte, 20)
Expand Down Expand Up @@ -857,9 +876,22 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM
var exchange ExchangeConfig

err := d.db.QueryRow(`
SELECT
t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running, t.created_at, t.updated_at,
a.id, a.user_id, a.name, a.provider, a.enabled, a.api_key, a.created_at, a.updated_at,
SELECT
t.id, t.user_id, t.name, t.ai_model_id, t.exchange_id, t.initial_balance, t.scan_interval_minutes, t.is_running,
COALESCE(t.btc_eth_leverage, 5) as btc_eth_leverage,
COALESCE(t.altcoin_leverage, 5) as altcoin_leverage,
COALESCE(t.trading_symbols, '') as trading_symbols,
COALESCE(t.use_coin_pool, 0) as use_coin_pool,
COALESCE(t.use_oi_top, 0) as use_oi_top,
COALESCE(t.custom_prompt, '') as custom_prompt,
COALESCE(t.override_base_prompt, 0) as override_base_prompt,
COALESCE(t.system_prompt_template, 'default') as system_prompt_template,
COALESCE(t.is_cross_margin, 1) as is_cross_margin,
t.created_at, t.updated_at,
a.id, a.user_id, a.name, a.provider, a.enabled, a.api_key,
COALESCE(a.custom_api_url, '') as custom_api_url,
COALESCE(a.custom_model_name, '') as custom_model_name,
a.created_at, a.updated_at,
e.id, e.user_id, e.name, e.type, e.enabled, e.api_key, e.secret_key, e.testnet,
COALESCE(e.hyperliquid_wallet_addr, '') as hyperliquid_wallet_addr,
COALESCE(e.aster_user, '') as aster_user,
Expand All @@ -873,8 +905,13 @@ func (d *Database) GetTraderConfig(userID, traderID string) (*TraderRecord, *AIM
`, traderID, userID).Scan(
&trader.ID, &trader.UserID, &trader.Name, &trader.AIModelID, &trader.ExchangeID,
&trader.InitialBalance, &trader.ScanIntervalMinutes, &trader.IsRunning,
&trader.BTCETHLeverage, &trader.AltcoinLeverage, &trader.TradingSymbols,
&trader.UseCoinPool, &trader.UseOITop,
&trader.CustomPrompt, &trader.OverrideBasePrompt, &trader.SystemPromptTemplate,
&trader.IsCrossMargin,
&trader.CreatedAt, &trader.UpdatedAt,
&aiModel.ID, &aiModel.UserID, &aiModel.Name, &aiModel.Provider, &aiModel.Enabled, &aiModel.APIKey,
&aiModel.CustomAPIURL, &aiModel.CustomModelName,
&aiModel.CreatedAt, &aiModel.UpdatedAt,
&exchange.ID, &exchange.UserID, &exchange.Name, &exchange.Type, &exchange.Enabled,
&exchange.APIKey, &exchange.SecretKey, &exchange.Testnet,
Expand Down
124 changes: 91 additions & 33 deletions decision/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import (
"log"
"nofx/market"
"nofx/mcp"
"nofx/news"
"nofx/pool"
"strings"
"time"

"github.com/samber/lo"
)

// PositionInfo 持仓信息
Expand Down Expand Up @@ -55,30 +58,40 @@ type OITopData struct {

// Context 交易上下文(传递给AI的完整信息)
type Context struct {
CurrentTime string `json:"current_time"`
RuntimeMinutes int `json:"runtime_minutes"`
CallCount int `json:"call_count"`
Account AccountInfo `json:"account"`
Positions []PositionInfo `json:"positions"`
CandidateCoins []CandidateCoin `json:"candidate_coins"`
MarketDataMap map[string]*market.Data `json:"-"` // 不序列化,但内部使用
OITopDataMap map[string]*OITopData `json:"-"` // OI Top数据映射
Performance interface{} `json:"-"` // 历史表现分析(logger.PerformanceAnalysis)
BTCETHLeverage int `json:"-"` // BTC/ETH杠杆倍数(从配置读取)
AltcoinLeverage int `json:"-"` // 山寨币杠杆倍数(从配置读取)
CurrentTime string `json:"current_time"`
RuntimeMinutes int `json:"runtime_minutes"`
CallCount int `json:"call_count"`
Account AccountInfo `json:"account"`
Positions []PositionInfo `json:"positions"`
CandidateCoins []CandidateCoin `json:"candidate_coins"`
MarketDataMap map[string]*market.Data `json:"-"` // 不序列化,但内部使用
OITopDataMap map[string]*OITopData `json:"-"` // OI Top数据映射
Performance interface{} `json:"-"` // 历史表现分析(logger.PerformanceAnalysis)
BTCETHLeverage int `json:"-"` // BTC/ETH杠杆倍数(从配置读取)
AltcoinLeverage int `json:"-"` // 山寨币杠杆倍数(从配置读取)
News map[string][]news.NewsItem `json:"news,omitempty"` // 新闻数据(可选)按symbol分组传给AI
}

// Decision AI的交易决策
type Decision struct {
Symbol string `json:"symbol"`
Action string `json:"action"` // "open_long", "open_short", "close_long", "close_short", "hold", "wait"
Symbol string `json:"symbol"`
Action string `json:"action"` // "open_long", "open_short", "close_long", "close_short", "update_stop_loss", "update_take_profit", "partial_close", "hold", "wait"

// 开仓参数
Leverage int `json:"leverage,omitempty"`
PositionSizeUSD float64 `json:"position_size_usd,omitempty"`
StopLoss float64 `json:"stop_loss,omitempty"`
TakeProfit float64 `json:"take_profit,omitempty"`
Confidence int `json:"confidence,omitempty"` // 信心度 (0-100)
RiskUSD float64 `json:"risk_usd,omitempty"` // 最大美元风险
Reasoning string `json:"reasoning"`

// 调整参数(新增)
NewStopLoss float64 `json:"new_stop_loss,omitempty"` // 用于 update_stop_loss
NewTakeProfit float64 `json:"new_take_profit,omitempty"` // 用于 update_take_profit
ClosePercentage float64 `json:"close_percentage,omitempty"` // 用于 partial_close (0-100)

// 通用参数
Confidence int `json:"confidence,omitempty"` // 信心度 (0-100)
RiskUSD float64 `json:"risk_usd,omitempty"` // 最大美元风险
Reasoning string `json:"reasoning"`
}

// FullDecision AI的完整决策(包含思维链)
Expand Down Expand Up @@ -114,13 +127,18 @@ func GetFullDecisionWithCustomPrompt(ctx *Context, mcpClient *mcp.Client, custom

// 4. 解析AI响应
decision, err := parseFullDecisionResponse(aiResponse, ctx.Account.TotalEquity, ctx.BTCETHLeverage, ctx.AltcoinLeverage)

// 无论是否有错误,都要保存 SystemPrompt 和 UserPrompt(用于调试和决策未执行后的问题定位)
if decision != nil {
decision.Timestamp = time.Now()
decision.SystemPrompt = systemPrompt // 保存系统prompt
decision.UserPrompt = userPrompt // 保存输入prompt
}

if err != nil {
return decision, fmt.Errorf("解析AI响应失败: %w", err)
}

decision.Timestamp = time.Now()
decision.SystemPrompt = systemPrompt // 保存系统prompt
decision.UserPrompt = userPrompt // 保存输入prompt
return decision, nil
}

Expand Down Expand Up @@ -248,7 +266,7 @@ func buildSystemPrompt(accountEquity float64, btcEthLeverage, altcoinLeverage in
log.Printf("⚠️ 提示词模板 '%s' 不存在,使用 default: %v", templateName, err)
template, err = GetPromptTemplate("default")
if err != nil {
// 如果连 default 都不存在,使用内置的简化版本
// 如果连 default 都不存在,使用内置的简化版本(最后防线)
log.Printf("❌ 无法加载任何提示词模板,使用内置简化版本")
sb.WriteString("你是专业的加密货币交易AI。请根据市场数据做出交易决策。\n\n")
} else {
Expand All @@ -260,15 +278,16 @@ func buildSystemPrompt(accountEquity float64, btcEthLeverage, altcoinLeverage in
sb.WriteString("\n\n")
}

// 2. 硬约束(风险控制)- 动态生成
// 2. 硬约束(风险控制)- 动态生成(始终追加)
sb.WriteString("# 硬约束(风险控制)\n\n")
sb.WriteString("1. 风险回报比: 必须 ≥ 1:3(冒1%风险,赚3%+收益)\n")
sb.WriteString("2. 最多持仓: 3个币种(质量>数量)\n")
sb.WriteString(fmt.Sprintf("3. 单币仓位: 山寨%.0f-%.0f U(%dx杠杆) | BTC/ETH %.0f-%.0f U(%dx杠杆)\n",
accountEquity*0.8, accountEquity*1.5, altcoinLeverage, accountEquity*5, accountEquity*10, btcEthLeverage))
sb.WriteString("4. 保证金: 总使用率 ≤ 90%\n\n")
sb.WriteString(fmt.Sprintf("3. 单币仓位: 山寨%.0f-%.0f U | BTC/ETH %.0f-%.0f U\n",
accountEquity*0.8, accountEquity*1.5, accountEquity*5, accountEquity*10))
sb.WriteString(fmt.Sprintf("4. 杠杆限制: **山寨币最大%dx杠杆** | **BTC/ETH最大%dx杠杆** (⚠️ 严格执行,不可超过)\n", altcoinLeverage, btcEthLeverage))
sb.WriteString("5. 保证金: 总使用率 ≤ 90%\n\n")

// 3. 输出格式 - 动态生成
// 3. 输出格式 - 动态生成(始终追加)
sb.WriteString("#输出格式\n\n")
sb.WriteString("第一步: 思维链(纯文本)\n")
sb.WriteString("简洁分析你的思考过程\n\n")
Expand All @@ -278,9 +297,12 @@ func buildSystemPrompt(accountEquity float64, btcEthLeverage, altcoinLeverage in
sb.WriteString(" {\"symbol\": \"ETHUSDT\", \"action\": \"close_long\", \"reasoning\": \"止盈离场\"}\n")
sb.WriteString("]\n```\n\n")
sb.WriteString("字段说明:\n")
sb.WriteString("- `action`: open_long | open_short | close_long | close_short | hold | wait\n")
sb.WriteString("- `action`: open_long | open_short | close_long | close_short | update_stop_loss | update_take_profit | partial_close | hold | wait\n")
sb.WriteString("- `confidence`: 0-100(开仓建议≥75)\n")
sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd, reasoning\n\n")
sb.WriteString("- 开仓时必填: leverage, position_size_usd, stop_loss, take_profit, confidence, risk_usd, reasoning\n")
sb.WriteString("- update_stop_loss 必填: new_stop_loss, reasoning\n")
sb.WriteString("- update_take_profit 必填: new_take_profit, reasoning\n")
sb.WriteString("- partial_close 必填: close_percentage (0-100), reasoning\n\n")

return sb.String()
}
Expand Down Expand Up @@ -380,6 +402,18 @@ func buildUserPrompt(ctx *Context) string {
}
}

// 新闻内容
newsItem := make([]news.NewsItem, 0, 100)
for _, symbol := range lo.Keys(ctx.News) {
newsItem = append(newsItem, ctx.News[symbol]...)
}
if len(newsItem) > 0 {
sb.WriteString("\n## 相关新闻\n")
for _, item := range newsItem {
sb.WriteString(item.String())
}
}

sb.WriteString("---\n\n")
sb.WriteString("现在请分析并输出决策(思维链 + JSON)\n")

Expand Down Expand Up @@ -504,12 +538,15 @@ func findMatchingBracket(s string, start int) int {
func validateDecision(d *Decision, accountEquity float64, btcEthLeverage, altcoinLeverage int) error {
// 验证action
validActions := map[string]bool{
"open_long": true,
"open_short": true,
"close_long": true,
"close_short": true,
"hold": true,
"wait": true,
"open_long": true,
"open_short": true,
"close_long": true,
"close_short": true,
"update_stop_loss": true,
"update_take_profit": true,
"partial_close": true,
"hold": true,
"wait": true,
}

if !validActions[d.Action] {
Expand Down Expand Up @@ -589,5 +626,26 @@ func validateDecision(d *Decision, accountEquity float64, btcEthLeverage, altcoi
}
}

// 动态调整止损验证
if d.Action == "update_stop_loss" {
if d.NewStopLoss <= 0 {
return fmt.Errorf("新止损价格必须大于0: %.2f", d.NewStopLoss)
}
}

// 动态调整止盈验证
if d.Action == "update_take_profit" {
if d.NewTakeProfit <= 0 {
return fmt.Errorf("新止盈价格必须大于0: %.2f", d.NewTakeProfit)
}
}

// 部分平仓验证
if d.Action == "partial_close" {
if d.ClosePercentage <= 0 || d.ClosePercentage > 100 {
return fmt.Errorf("平仓百分比必须在0-100之间: %.1f", d.ClosePercentage)
}
}

return nil
}
20 changes: 19 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
services:
# Init service: ensure config.db file exists before backend starts
config-init:
image: busybox:1.36
command:
- sh
- -c
- >
touch /mnt/config.db &&
if [ ! -f /mnt/config.json ] && [ -f /mnt/config.json.example ]; then
cp /mnt/config.json.example /mnt/config.json;
fi
volumes:
- ./:/mnt
restart: "no"

# Backend service (API and core logic)
nofx:
build:
Expand All @@ -18,6 +33,9 @@ services:
- TZ=${NOFX_TIMEZONE:-Asia/Shanghai} # Set timezone
networks:
- nofx-network
depends_on:
config-init:
condition: service_completed_successfully
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"]
interval: 30s
Expand Down Expand Up @@ -47,4 +65,4 @@ services:

networks:
nofx-network:
driver: bridge
driver: bridge
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module nofx
go 1.25.0

require (
github.com/PuerkitoBio/goquery v1.10.3
github.com/adshao/go-binance/v2 v2.8.7
github.com/ethereum/go-ethereum v1.16.5
github.com/gin-gonic/gin v1.11.0
Expand All @@ -11,11 +12,13 @@ require (
github.com/gorilla/websocket v1.5.3
github.com/mattn/go-sqlite3 v1.14.16
github.com/pquerna/otp v1.4.0
github.com/samber/lo v1.52.0
github.com/sonirico/go-hyperliquid v0.17.0
golang.org/x/crypto v0.42.0
)

require (
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/bitly/go-simplejson v0.5.0 // indirect
github.com/bits-and-blooms/bitset v1.24.0 // indirect
Expand Down
Loading
Loading