Sviluppo TradingBot
This commit is contained in:
@@ -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 : Lun–Ven 09:30–16: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: Lun–Ven, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user