184 lines
6.8 KiB
C#
184 lines
6.8 KiB
C#
using Managing.Core;
|
|
using static Managing.Common.Enums;
|
|
|
|
namespace Managing.Domain.Candles;
|
|
|
|
public static class CandleHelpers
|
|
{
|
|
public static DateTime GetBotPreloadSinceFromTimeframe(Timeframe timeframe)
|
|
{
|
|
return timeframe switch
|
|
{
|
|
Timeframe.FiveMinutes => DateTime.UtcNow.AddDays(-1),
|
|
Timeframe.FifteenMinutes => DateTime.UtcNow.AddDays(-5),
|
|
Timeframe.ThirtyMinutes => DateTime.UtcNow.AddDays(-10),
|
|
Timeframe.OneHour => DateTime.UtcNow.AddDays(-30),
|
|
Timeframe.FourHour => DateTime.UtcNow.AddDays(-60),
|
|
Timeframe.OneDay => DateTime.UtcNow.AddDays(-360),
|
|
_ => DateTime.Now.AddHours(-15),
|
|
};
|
|
}
|
|
|
|
public static DateTime GetPreloadSinceFromTimeframe(Timeframe timeframe)
|
|
{
|
|
return DateTime.UtcNow.AddDays(GetMinimalDays(timeframe));
|
|
}
|
|
|
|
public static int GetIntervalFromTimeframe(Timeframe timeframe)
|
|
{
|
|
var seconds = GetBaseIntervalInSeconds(timeframe);
|
|
// Run every 1/5th of the candle duration
|
|
return seconds / 5 * 1000; // milliseconds
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the interval in minutes for the given timeframe.
|
|
/// This is useful for cooldown period calculations.
|
|
/// </summary>
|
|
/// <param name="timeframe">The timeframe to get the interval for</param>
|
|
/// <returns>The interval in minutes</returns>
|
|
public static double GetIntervalInMinutes(Timeframe timeframe)
|
|
{
|
|
var seconds = GetBaseIntervalInSeconds(timeframe);
|
|
// Run every 1/5th of the candle duration
|
|
return (seconds / 5.0) / 60.0; // minutes
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the base interval in seconds for the given timeframe.
|
|
/// This is the core interval logic that can be used for various calculations.
|
|
/// </summary>
|
|
/// <param name="timeframe">The timeframe to get the base interval for</param>
|
|
/// <returns>The base interval in seconds</returns>
|
|
public static int GetBaseIntervalInSeconds(Timeframe timeframe)
|
|
{
|
|
return timeframe switch
|
|
{
|
|
Timeframe.OneDay => 86400,
|
|
Timeframe.FourHour => 14400,
|
|
Timeframe.OneHour => 3600,
|
|
Timeframe.ThirtyMinutes => 1800,
|
|
Timeframe.FifteenMinutes => 900,
|
|
Timeframe.FiveMinutes => 300,
|
|
_ => 300,
|
|
};
|
|
}
|
|
|
|
public static int GetUnixInterval(this Timeframe timeframe)
|
|
{
|
|
return timeframe switch
|
|
{
|
|
Timeframe.OneDay => 86400,
|
|
Timeframe.FiveMinutes => 300,
|
|
Timeframe.FifteenMinutes => 900,
|
|
Timeframe.FourHour => 14400,
|
|
Timeframe.OneHour => 3600,
|
|
_ => throw new NotImplementedException()
|
|
};
|
|
}
|
|
|
|
public static double GetMinimalDays(Timeframe timeframe)
|
|
{
|
|
return timeframe switch
|
|
{
|
|
Timeframe.FiveMinutes => -1,
|
|
Timeframe.FifteenMinutes => -5,
|
|
Timeframe.ThirtyMinutes => -10,
|
|
Timeframe.OneHour => -30,
|
|
Timeframe.FourHour => -60,
|
|
Timeframe.OneDay => -360,
|
|
_ => throw new NotImplementedException()
|
|
};
|
|
}
|
|
|
|
public static string GetCandleStoreGrainKey(TradingExchanges exchange, Ticker ticker, Timeframe timeframe)
|
|
{
|
|
return string.Join("-", exchange, ticker, timeframe);
|
|
}
|
|
|
|
public static (TradingExchanges exchange, Ticker ticker, Timeframe timeframe) ParseCandleStoreGrainKey(
|
|
string grainKey)
|
|
{
|
|
var components = grainKey.Split('-');
|
|
return (MiscExtensions.ParseEnum<TradingExchanges>(components[0]),
|
|
MiscExtensions.ParseEnum<Ticker>(components[1]), MiscExtensions.ParseEnum<Timeframe>(components[2]));
|
|
}
|
|
|
|
public static TimeSpan GetDueTimeForTimeframe(Timeframe timeframe, DateTime now)
|
|
{
|
|
var intervalMinutes = GetBaseIntervalInSeconds(timeframe) / 60;
|
|
|
|
// Calculate the next candle boundary
|
|
var nextBoundary = GetNextCandleBoundary(now, intervalMinutes);
|
|
|
|
// Add 1 second to ensure we're after the candle closes
|
|
var targetTime = nextBoundary.AddSeconds(1);
|
|
|
|
// Return the time difference
|
|
return targetTime - now;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the next expected candle time for the given timeframe.
|
|
/// This is useful to determine if a new candle should be available yet.
|
|
/// </summary>
|
|
/// <param name="timeframe">The timeframe to calculate for</param>
|
|
/// <param name="now">The current time (defaults to DateTime.UtcNow)</param>
|
|
/// <returns>The next expected candle time</returns>
|
|
public static DateTime GetNextExpectedCandleTime(Timeframe timeframe, DateTime? now = null)
|
|
{
|
|
var currentTime = now ?? DateTime.UtcNow;
|
|
var intervalMinutes = GetBaseIntervalInSeconds(timeframe) / 60;
|
|
|
|
// Calculate the next candle boundary
|
|
return GetNextCandleBoundary(currentTime, intervalMinutes).AddSeconds(-1);
|
|
}
|
|
|
|
private static DateTime GetNextCandleBoundary(DateTime now, double intervalMinutes)
|
|
{
|
|
// For different timeframes, we need to align to different boundaries
|
|
if (intervalMinutes == 1) // OneMinute
|
|
{
|
|
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0).AddMinutes(1);
|
|
}
|
|
else if (intervalMinutes == 5) // FiveMinutes
|
|
{
|
|
var minute = (now.Minute / 5) * 5;
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, now.Hour, minute, 0);
|
|
return boundary.AddMinutes(5);
|
|
}
|
|
else if (intervalMinutes == 15) // FifteenMinutes
|
|
{
|
|
var minute = (now.Minute / 15) * 15;
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, now.Hour, minute, 0);
|
|
return boundary.AddMinutes(15);
|
|
}
|
|
else if (intervalMinutes == 30) // ThirtyMinutes
|
|
{
|
|
var minute = (now.Minute / 30) * 30;
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, now.Hour, minute, 0);
|
|
return boundary.AddMinutes(30);
|
|
}
|
|
else if (intervalMinutes == 60) // OneHour
|
|
{
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
|
|
return boundary.AddHours(1);
|
|
}
|
|
else if (intervalMinutes == 240) // FourHour
|
|
{
|
|
var hour = (now.Hour / 4) * 4;
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, hour, 0, 0);
|
|
return boundary.AddHours(4);
|
|
}
|
|
else if (intervalMinutes == 1440) // OneDay
|
|
{
|
|
var boundary = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);
|
|
return boundary.AddDays(1);
|
|
}
|
|
|
|
// Fallback to 5-minute intervals
|
|
var fallbackMinute = (now.Minute / 5) * 5;
|
|
var fallbackBoundary = new DateTime(now.Year, now.Month, now.Day, now.Hour, fallbackMinute, 0);
|
|
return fallbackBoundary.AddMinutes(5);
|
|
}
|
|
} |