package main import ( "fmt" "math" ) /* DEMONE MANUALE (daemon giornaliero): USDT (ERC-20) <-> BTC/ETH - Scarica klines Binance (symbol configurabile), aggiorna CSV. - Costruisce dataset sui log-return, addestra LSTM(64) con Huber loss, clipping. - Early-stopping quando avgLoss <= firstLoss * EARLY_STOP_PCT (default 0.5), max 100 epoche. - Chiede fee a THORNode (quote) con quantità di riferimento (REF_FROM / REF_TO). - Decide HOLD / SWAP_AB / SWAP_BA. - NON esegue swap: stampa ISTRUZIONI MANUALI e invia notifica Matrix (se configurata). - Gestisce uno stato locale (state/balance.json) per evitare ordini doppi: scala il saldo "from" creando un lock pending. - Parte subito e poi ripete ogni 24 ore. Log in italiano, una sola riga per printf/log.Printf. */ // ================== Dataset ================== type Dataset struct { Seqs [][]float64 Labels []float64 Mean float64 Std float64 } func buildDataset(kl []Kline, lookback int, valSplit float64) (Dataset, Dataset) { if len(kl) < lookback+2 { return Dataset{}, Dataset{} } prices := make([]float64, len(kl)) for i, k := range kl { prices[i] = k.Close } logp := make([]float64, len(prices)) for i, p := range prices { logp[i] = math.Log(p) } lr := make([]float64, len(logp)-1) for i := 1; i < len(logp); i++ { lr[i-1] = logp[i] - logp[i-1] } N := len(lr) - lookback if N <= 0 { return Dataset{}, Dataset{} } X := make([][]float64, 0, N) Y := make([]float64, 0, N) for i := 0; i < N; i++ { win := make([]float64, lookback) copy(win, lr[i:i+lookback]) X = append(X, win) Y = append(Y, lr[i+lookback]) } valN := int(float64(len(X)) * valSplit) if valN < 1 { valN = 1 } trainN := len(X) - valN mean, std := meanStd(flatten(X[:trainN])) if std == 0 { std = 1e-6 } zX := make([][]float64, len(X)) for i := range X { zX[i] = make([]float64, lookback) for j := range X[i] { zX[i][j] = (X[i][j] - mean) / std } } zY := make([]float64, len(Y)) for i := range Y { zY[i] = (Y[i] - mean) / std } train := Dataset{Seqs: zX[:trainN], Labels: zY[:trainN], Mean: mean, Std: std} val := Dataset{Seqs: zX[trainN:], Labels: zY[trainN:], Mean: mean, Std: std} return train, val } func flatten(x [][]float64) []float64 { out := make([]float64, 0, len(x)*max(1, len(x[0]))) for _, r := range x { out = append(out, r...) } return out } func meanStd(x []float64) (float64, float64) { if len(x) == 0 { return 0, 1 } var m float64 for _, v := range x { m += v } m /= float64(len(x)) var s float64 for _, v := range x { d := v - m s += d * d } s = math.Sqrt(s / float64(len(x))) return m, s } func max(a, b int) int { if a > b { return a } return b } // Istruzioni manuali + gestione stato (lock) func printManualInstruction(fromLabel, toLabel string, amountFrom, expectedBps, feeBps, safetyBps float64) string { fromS := assetShort(fromLabel) toS := assetShort(toLabel) maxFeeBps := expectedBps - safetyBps if maxFeeBps < 0 { maxFeeBps = 0 } maxFeeAbs := amountFrom * (maxFeeBps / 1e4) return fmt.Sprintf("ISTRUZIONI MANUALI: esegui swap %s->%s amount=%.8f; procedi solo se feeTotali<=%.3f bps (stima THOR=%.3f bps) e costo<=~%.8f %s", fromS, toS, amountFrom, maxFeeBps, feeBps, maxFeeAbs, fromS) }