package main import ( "fmt" "math" ) // ================== Dataset ================== // // - Seqs: finestre di log-return (dimensione lookback) in z-score. // - Labels: log-return successivo (t+1) in z-score. // - Mean/Std: media e dev std calcolate SOLO sul training (sulle feature X), // riutilizzate per normalizzare sia X che Y. In inferenza denormalizziamo // con predLR = predZ*Std + Mean (vedi decision.go). type Dataset struct { Seqs [][]float64 Labels []float64 Mean float64 Std float64 } // buildDataset costruisce (train, val) a partire dalle Kline: // 1) prezzi di chiusura -> log(p) // 2) differenze -> log-return (lr[t] = log(p[t]) - log(p[t-1])) // 3) finestre X = lr[i : i+lookback], label Y = lr[i+lookback] // 4) split train/val e z-score usando Mean/Std del SOLO training (sulle X) func buildDataset(kl []Kline, lookback int, valSplit float64) (Dataset, Dataset) { if len(kl) < lookback+2 { return Dataset{}, Dataset{} } // 1) Close -> log price 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) } // 2) log-return lr := make([]float64, len(logp)-1) for i := 1; i < len(logp); i++ { lr[i-1] = logp[i] - logp[i-1] } // 3) finestre X e label Y (prossimo step) 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]) } // 4) split train/val valN := int(float64(len(X)) * valSplit) if valN < 1 { valN = 1 } trainN := len(X) - valN if trainN < 1 { // fallback minimo: tutto train meno 1 per val trainN = max(1, len(X)-1) valN = len(X) - trainN } // 5) mean/std SOLO sul training (sulle feature) mean, std := meanStd(flatten(X[:trainN])) if std == 0 { std = 1e-6 } // 6) z-score per X e Y con la stessa mean/std 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, ) }