Sviluppo TradingBot

This commit is contained in:
2026-06-09 18:29:41 +02:00
parent 61f1e59964
commit e3c0bd51b2
133 changed files with 24903 additions and 1 deletions
+113
View File
@@ -0,0 +1,113 @@
using System;
namespace DesktopBot.Services
{
/// <summary>
/// Helper per determinare se un mercato è aperto in base all'asset class e all'orario locale.
///
/// Regole approssimative (senza chiamata API aggiuntiva):
/// - US Equity : LunVen 09:3016:00 ET (UTC-5 standard / UTC-4 daylight)
/// - Crypto : sempre aperto (24/7)
/// - Altro : assume sempre aperto
///
/// Per una verifica precisa (pre-market, post-market, festivi) usare
/// IAlpacaTradingClient.GetClockAsync() — disponibile ma costa una chiamata API.
/// </summary>
public static class MarketHoursService
{
// ── Orari NYSE/NASDAQ in Eastern Time ────────────────────────────────
private static readonly TimeSpan MarketOpen = new TimeSpan(9, 30, 0);
private static readonly TimeSpan MarketClose = new TimeSpan(16, 0, 0);
// Fuso Eastern (tiene conto automaticamente del DST di .NET)
private static readonly TimeZoneInfo EasternTz =
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
/// <summary>
/// Ritorna true se il mercato relativo all'asset class indicata è
/// presumibilmente aperto adesso.
/// </summary>
public static bool IsMarketOpen(string assetClass)
{
if (string.IsNullOrEmpty(assetClass))
return true;
var cls = assetClass.ToLowerInvariant();
// Crypto: 24/7
if (cls.Contains("crypto"))
return true;
// US Equity: LunVen, orario NYSE
var nowEt = TimeZoneInfo.ConvertTime(DateTime.UtcNow, EasternTz);
if (nowEt.DayOfWeek == DayOfWeek.Saturday || nowEt.DayOfWeek == DayOfWeek.Sunday)
return false;
var tod = nowEt.TimeOfDay;
return tod >= MarketOpen && tod < MarketClose;
}
/// <summary>
/// Testo descrittivo dello stato mercato per la UI.
/// </summary>
public static string GetMarketStatusLabel(string assetClass)
{
if (string.IsNullOrEmpty(assetClass))
return "";
var cls = assetClass.ToLowerInvariant();
if (cls.Contains("crypto"))
return "Mercato 24/7 — sempre aperto";
var nowEt = TimeZoneInfo.ConvertTime(DateTime.UtcNow, EasternTz);
if (nowEt.DayOfWeek == DayOfWeek.Saturday || nowEt.DayOfWeek == DayOfWeek.Sunday)
return $"Mercato chiuso (weekend) — riapertura lunedì {MarketOpen:hh\\:mm} ET";
var tod = nowEt.TimeOfDay;
if (tod < MarketOpen)
return $"Mercato chiuso — apertura alle {MarketOpen:hh\\:mm} ET";
if (tod >= MarketClose)
return $"Mercato chiuso — riapertura domani {MarketOpen:hh\\:mm} ET";
var remaining = MarketClose - tod;
return $"Mercato aperto — chiude tra {(int)remaining.TotalHours}h {remaining.Minutes}m ET";
}
/// <summary>
/// Colore suggerito per il badge stato mercato.
/// </summary>
public static string GetMarketStatusColor(string assetClass)
=> IsMarketOpen(assetClass) ? "#00E676" : "#FFC107";
/// <summary>
/// Secondi da attendere prima della prossima apertura (utile per sleep nel loop).
/// Ritorna 0 se il mercato è già aperto.
/// </summary>
public static double SecondsUntilOpen(string assetClass)
{
if (IsMarketOpen(assetClass)) return 0;
var cls = (assetClass ?? "").ToLowerInvariant();
if (cls.Contains("crypto")) return 0;
var nowEt = TimeZoneInfo.ConvertTime(DateTime.UtcNow, EasternTz);
DateTime nextOpen;
if (nowEt.TimeOfDay < MarketOpen)
{
nextOpen = nowEt.Date + MarketOpen;
}
else
{
// Dopo la chiusura → prossimo giorno lavorativo
var candidate = nowEt.Date.AddDays(1) + MarketOpen;
while (candidate.DayOfWeek == DayOfWeek.Saturday || candidate.DayOfWeek == DayOfWeek.Sunday)
candidate = candidate.AddDays(1);
nextOpen = candidate;
}
var diff = nextOpen - nowEt;
return diff.TotalSeconds > 0 ? diff.TotalSeconds : 0;
}
}
}