money/dataset.go

127 lines
3.2 KiB
Go

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)
}