diff --git a/TradingBot/Program.cs b/TradingBot/Program.cs index 211580e..dd8dfb4 100644 --- a/TradingBot/Program.cs +++ b/TradingBot/Program.cs @@ -3,12 +3,15 @@ using TradingBot.Services; var builder = WebApplication.CreateBuilder(args); -// Configure Kestrel per Docker - Listen su tutte le interfacce -builder.WebHost.ConfigureKestrel(serverOptions => +// Configure Kestrel - Solo in Development usa porte da launchSettings +// In Production/Docker usa porta 8080 su tutte le interfacce +if (builder.Environment.IsProduction() || builder.Environment.EnvironmentName == "Docker") { - // Listen su porta 8080 per Docker - serverOptions.ListenAnyIP(8080); -}); + builder.WebHost.ConfigureKestrel(serverOptions => + { + serverOptions.ListenAnyIP(8080); + }); +} // Add services to the container. builder.Services.AddRazorComponents() @@ -29,12 +32,10 @@ var app = builder.Build(); if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error", createScopeForErrors: true); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } -// Disabilita HTTPS redirect in Docker/Production -// Docker gestisce HTTPS tramite reverse proxy se necessario +// HTTPS redirect solo in Development if (app.Environment.IsDevelopment()) { app.UseHttpsRedirection(); @@ -43,7 +44,7 @@ if (app.Environment.IsDevelopment()) app.UseStaticFiles(); app.UseAntiforgery(); -// Health check endpoint for Docker +// Health check endpoint app.MapHealthChecks("/health"); app.MapRazorComponents() diff --git a/TradingBot/Properties/launchSettings.json b/TradingBot/Properties/launchSettings.json index cd7b736..b731a40 100644 --- a/TradingBot/Properties/launchSettings.json +++ b/TradingBot/Properties/launchSettings.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { - "http": { + "TradingBot": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, @@ -10,7 +10,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "https": { + "TradingBot (HTTPS)": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, diff --git a/TradingBot/Services/SimpleMovingAverageStrategy.cs b/TradingBot/Services/SimpleMovingAverageStrategy.cs index 3221d99..25c5cea 100644 --- a/TradingBot/Services/SimpleMovingAverageStrategy.cs +++ b/TradingBot/Services/SimpleMovingAverageStrategy.cs @@ -11,19 +11,38 @@ public class SimpleMovingAverageStrategy : ITradingStrategy public Task AnalyzeAsync(string symbol, List historicalPrices) { - if (historicalPrices.Count < _longPeriod) + // Filtra null e valori invalidi prima di usare la lista + if (historicalPrices == null || historicalPrices.Count < _longPeriod) { return Task.FromResult(new TradingSignal { Symbol = symbol, Type = SignalType.Hold, - Price = historicalPrices.LastOrDefault()?.Price ?? 0, + Price = historicalPrices?.LastOrDefault()?.Price ?? 0, Reason = "Dati insufficienti per l'analisi", Timestamp = DateTime.UtcNow }); } - var recentPrices = historicalPrices.OrderByDescending(p => p.Timestamp).Take(_longPeriod).ToList(); + // Filtra oggetti null e ordina + var recentPrices = historicalPrices + .Where(p => p != null && p.Price > 0) + .OrderByDescending(p => p.Timestamp) + .Take(_longPeriod) + .ToList(); + + // Verifica ancora la count dopo il filtro + if (recentPrices.Count < _longPeriod) + { + return Task.FromResult(new TradingSignal + { + Symbol = symbol, + Type = SignalType.Hold, + Price = recentPrices.LastOrDefault()?.Price ?? 0, + Reason = "Dati insufficienti per l'analisi dopo il filtro", + Timestamp = DateTime.UtcNow + }); + } var shortSMA = recentPrices.Take(_shortPeriod).Average(p => p.Price); var longSMA = recentPrices.Average(p => p.Price); diff --git a/TradingBot/TradingBot.csproj b/TradingBot/TradingBot.csproj index e0979d5..7b3f8c1 100644 --- a/TradingBot/TradingBot.csproj +++ b/TradingBot/TradingBot.csproj @@ -14,11 +14,35 @@ 1.0.0 1.0.0.0 1.0.0.0 - e903ae67-ff0b-46dc-98a0-05de23186f86 - - - + + + + + + + + + + + + + + + + + + + + diff --git a/TradingBot/appsettings.Development.json b/TradingBot/appsettings.Development.json new file mode 100644 index 0000000..3e1a225 --- /dev/null +++ b/TradingBot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Information" + } + } +} diff --git a/TradingBot/appsettings.json b/TradingBot/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/TradingBot/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/TradingBot/docker-compose.yml b/TradingBot/docker-compose.yml new file mode 100644 index 0000000..847e2fd --- /dev/null +++ b/TradingBot/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.8' + +services: + tradingbot: + container_name: tradingbot + image: gitea.encke-hake.ts.net/alby96/encelado/tradingbot:latest + ports: + - "${EXTERNAL_PORT:-8080}:8080" + volumes: + - tradingbot-data:/app/data + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://+:8080 + restart: unless-stopped + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 10s + networks: + - tradingbot-network + deploy: + resources: + limits: + cpus: '2.0' + memory: 1G + reservations: + cpus: '0.5' + memory: 256M + +volumes: + tradingbot-data: + driver: local + +networks: + tradingbot-network: + driver: bridge diff --git a/TradingBot/docs/deployment/UNRAID_NATIVE_INSTALL.md b/TradingBot/docs/deployment/UNRAID_NATIVE_INSTALL.md new file mode 100644 index 0000000..abe1968 --- /dev/null +++ b/TradingBot/docs/deployment/UNRAID_NATIVE_INSTALL.md @@ -0,0 +1,427 @@ +# ?? TradingBot - Installazione su Unraid (Senza Portainer) + +## ? Installazione Diretta da Gitea Registry + +Puoi installare TradingBot direttamente dall'Unraid Docker Manager usando il tuo Gitea Registry! + +--- + +## ?? PREREQUISITI + +### 1. Login Gitea Registry su Unraid + +SSH su Unraid: + +```bash +ssh root@192.168.30.23 # O IP Tailscale + +# Login al Gitea Registry +docker login gitea.encke-hake.ts.net + +# Username: Alby96 +# Password: [Personal Access Token Gitea] +``` + +**Output atteso**: +``` +Login Succeeded ? +``` + +--- + +## ?? METODO 1: Template XML (Consigliato) + +### Step 1: Copia Template su Unraid + +```bash +# Su Unraid +mkdir -p /boot/config/plugins/dockerMan/templates-user + +# Scarica template +wget -O /boot/config/plugins/dockerMan/templates-user/TradingBot.xml \ + https://gitea.encke-hake.ts.net/Alby96/Encelado/raw/branch/main/TradingBot/unraid-template.xml +``` + +### Step 2: Installa Container + +1. Unraid WebUI ? **Docker** tab +2. Click **Add Container** (in fondo) +3. **Template**: Dropdown ? Seleziona **TradingBot** +4. Configura (opzionale): + - **Port**: `8080` (o cambia se occupata) + - **Data Path**: `/mnt/user/appdata/tradingbot/data` +5. Click **Apply** + +Unraid farà: +- ? Pull immagine da Gitea Registry +- ? Crea container +- ? Crea volume per dati +- ? Start automatico + +### Step 3: Accedi WebUI + +``` +http://192.168.30.23:8080 +``` + +Dovresti vedere la Dashboard TradingBot! ?? + +--- + +## ?? METODO 2: Installazione Manuale + +Se preferisci non usare template: + +### Step 1: Unraid Docker Tab + +1. **Docker** ? **Add Container** +2. **Name**: `TradingBot` + +### Step 2: Configurazione Base + +**Repository**: +``` +gitea.encke-hake.ts.net/alby96/encelado/tradingbot:latest +``` + +**Network Type**: `Bridge` + +**Console shell command**: `Shell` + +### Step 3: Port Mapping + +Click **Add another Path, Port, Variable, Label or Device** + +**Config Type**: `Port` +- **Name**: WebUI +- **Container Port**: `8080` +- **Host Port**: `8080` (o altra porta libera) +- **Connection Type**: `TCP` + +### Step 4: Volume Mapping + +Click **Add another Path, Port, Variable, Label or Device** + +**Config Type**: `Path` +- **Name**: AppData +- **Container Path**: `/app/data` +- **Host Path**: `/mnt/user/appdata/tradingbot/data` +- **Access Mode**: `Read/Write` + +### Step 5: Environment Variables + +**ASPNETCORE_ENVIRONMENT**: +- **Name**: `ASPNETCORE_ENVIRONMENT` +- **Value**: `Production` + +**ASPNETCORE_URLS**: +- **Name**: `ASPNETCORE_URLS` +- **Value**: `http://+:8080` + +### Step 6: Health Check (Opzionale) + +**Extra Parameters**: +``` +--health-cmd="wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1" --health-interval=30s --health-timeout=3s --health-retries=3 --health-start-period=10s +``` + +### Step 7: Apply + +Click **Apply** in fondo alla pagina. + +--- + +## ?? AGGIORNAMENTO CONTAINER + +### Via Unraid Docker Tab + +1. **Docker** ? Trova **TradingBot** +2. Click **icona ferma** (stop container) +3. Click **Force Update** (icona update) +4. Click **icona play** (start container) + +Unraid farà: +- ? Pull ultima immagine da Gitea +- ? Ricrea container +- ? Mantiene dati (volume persistente) + +### Automatico con User Scripts Plugin + +Installa **User Scripts** plugin: + +1. **Apps** ? Cerca "User Scripts" +2. Installa + +Crea script update: + +```bash +#!/bin/bash +# Nome: Update TradingBot + +# Stop container +docker stop TradingBot + +# Remove container +docker rm TradingBot + +# Pull latest image +docker pull gitea.encke-hake.ts.net/alby96/encelado/tradingbot:latest + +# Recreate container (Unraid fa automaticamente) +# O riavvia manualmente dalla WebUI + +echo "Update completato! Riavvia TradingBot dalla Docker tab." +``` + +Schedula: Ogni settimana o manualmente. + +--- + +## ?? GESTIONE CONTAINER + +### Start/Stop/Restart + +**Docker tab** ? Container **TradingBot**: +- ?? **Play**: Start +- ?? **Pause**: Pausa (mantiene in memoria) +- ?? **Stop**: Ferma +- ?? **Restart**: Riavvia + +### View Logs + +**Docker tab** ? Container **TradingBot** ? Click **icona logs** + +O via terminal: +```bash +docker logs TradingBot -f +``` + +### Console Access + +**Docker tab** ? Container **TradingBot** ? Click **icona console** + +O via terminal: +```bash +docker exec -it TradingBot bash +``` + +### Statistics + +**Docker tab** ? Container **TradingBot** ? Click **icona stats** + +Mostra: +- CPU usage +- Memory usage +- Network I/O +- Block I/O + +--- + +## ?? ACCESSO WEBUI + +### Locale (Unraid LAN) + +``` +http://192.168.30.23:8080 +``` + +### Via Tailscale + +``` +http://unraid.encke-hake.ts.net:8080 +``` + +(Se hai configurato Tailscale su Unraid) + +### Reverse Proxy (Opzionale) + +Se vuoi accesso HTTPS: + +**Nginx Proxy Manager** (tramite Unraid): +``` +https://tradingbot.encke-hake.ts.net ? http://192.168.30.23:8080 +``` + +--- + +## ?? SICUREZZA + +### Best Practices + +? **Porta non esposta pubblicamente** (solo LAN o Tailscale) +? **Volume dati protetto** (`/mnt/user/appdata/tradingbot/`) +? **Registry privato** (Gitea richiede login) +? **Certificati validi** (Tailscale) +? **User non-root** (già configurato nel Dockerfile) + +### Backup Dati + +```bash +# Backup manuale +tar -czf tradingbot-backup-$(date +%Y%m%d).tar.gz \ + /mnt/user/appdata/tradingbot/data + +# Restore +tar -xzf tradingbot-backup-20241212.tar.gz -C / +``` + +O usa **CA Backup / Restore Appdata** plugin. + +--- + +## ?? TROUBLESHOOTING + +### Container Non Si Avvia + +**Check logs**: +```bash +docker logs TradingBot +``` + +**Problemi comuni**: + +#### Porta occupata +``` +Error: address already in use +``` +**Fix**: Cambia porta in configurazione container + +#### Pull failed +``` +Error: unauthorized: authentication required +``` +**Fix**: `docker login gitea.encke-hake.ts.net` + +#### Image not found +``` +Error: manifest not found +``` +**Fix**: Verifica che l'immagine esista su Gitea Packages + +### WebUI Non Accessibile + +**Checklist**: +- [ ] Container status: **running** (verde) +- [ ] Health check: **healthy** +- [ ] Porta corretta (8080 o custom) +- [ ] Firewall Unraid non blocca +- [ ] Browser su `http://` non `https://` + +**Test**: +```bash +curl http://localhost:8080/health +# Deve rispondere: Healthy +``` + +### Performance Issues + +**Check risorse**: +```bash +docker stats TradingBot +``` + +**Limiti raccomandati**: +- **CPU**: 2 cores max, 0.5 reserved +- **Memory**: 1GB max, 256MB reserved + +Configura in **Extra Parameters**: +``` +--cpus="2.0" --memory="1g" --memory-reservation="256m" +``` + +--- + +## ?? CHECKLIST INSTALLAZIONE + +### Pre-Install +- [ ] Unraid aggiornato +- [ ] Docker service attivo +- [ ] Porta 8080 disponibile +- [ ] `docker login gitea.encke-hake.ts.net` successful + +### Install +- [ ] Template XML copiato (Metodo 1) +- [ ] O container configurato manualmente (Metodo 2) +- [ ] Container creato +- [ ] Volume dati creato + +### Post-Install +- [ ] Container status: running +- [ ] Health check: healthy +- [ ] WebUI accessibile +- [ ] Settings configurati nell'app + +--- + +## ?? VANTAGGI UNRAID NATIVO + +? **Zero dipendenze** (no Portainer, no docker-compose) +? **WebUI Unraid integrata** (gestione familiare) +? **Auto-start** (container parte con Unraid) +? **Backup integrato** (con plugin CA) +? **Update semplice** (1 click) +? **Template riutilizzabile** (facile reinstall) + +--- + +## ?? WORKFLOW COMPLETO + +### Sviluppo (PC) +``` +1. Codice in Visual Studio +2. Build ? Publish (Release) +3. ? Automatico: Push Gitea Registry +4. Commit: git push +``` + +### Deploy (Unraid) +``` +1. Docker tab ? TradingBot ? Stop +2. Force Update +3. Start +4. Done! ? +``` + +**Tempo totale**: ~2 minuti + +--- + +## ?? RISORSE + +### Template XML +``` +https://gitea.encke-hake.ts.net/Alby96/Encelado/raw/branch/main/TradingBot/unraid-template.xml +``` + +### Repository +``` +https://gitea.encke-hake.ts.net/Alby96/Encelado +``` + +### Docker Image +``` +gitea.encke-hake.ts.net/alby96/encelado/tradingbot:latest +``` + +### Support +``` +https://gitea.encke-hake.ts.net/Alby96/Encelado/issues +``` + +--- + +**?? TradingBot pronto per Unraid senza Portainer!** + +**Quick Start**: +```bash +# 1. Login +docker login gitea.encke-hake.ts.net + +# 2. Install Template +wget -O /boot/config/plugins/dockerMan/templates-user/TradingBot.xml \ + https://gitea.encke-hake.ts.net/Alby96/Encelado/raw/branch/main/TradingBot/unraid-template.xml + +# 3. Docker tab ? Add Container ? TradingBot ? Apply +``` + +**Access**: `http://192.168.30.23:8080` ?? diff --git a/TradingBot/unraid-template.xml b/TradingBot/unraid-template.xml new file mode 100644 index 0000000..e449ca2 --- /dev/null +++ b/TradingBot/unraid-template.xml @@ -0,0 +1,29 @@ + + + TradingBot + gitea.encke-hake.ts.net/alby96/encelado/tradingbot:latest + https://gitea.encke-hake.ts.net + bridge + + sh + false + https://gitea.encke-hake.ts.net/Alby96/Encelado + https://gitea.encke-hake.ts.net/Alby96/Encelado + Automated Crypto Trading Bot con Blazor UI. Analisi tecnica, simulazione trading e gestione portfolio automatizzata. + Tools:Productivity Status:Stable + http://[IP]:[PORT:8080] + https://gitea.encke-hake.ts.net/Alby96/Encelado/raw/branch/main/TradingBot/unraid-template.xml + https://raw.githubusercontent.com/docker-library/docs/master/dotnet/logo.png + --health-cmd="wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1" --health-interval=30s --health-timeout=3s --health-retries=3 --health-start-period=10s + + + + + + + 8080 + /mnt/user/appdata/tradingbot/data + Production + http://+:8080 + true +