diff --git a/DiscordClient/Graphing/GraphDrawer.cs b/DiscordClient/Graphing/GraphDrawer.cs index eb8679e..a5dd56d 100644 --- a/DiscordClient/Graphing/GraphDrawer.cs +++ b/DiscordClient/Graphing/GraphDrawer.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Path = System.IO.Path; @@ -12,10 +13,16 @@ namespace DiscordClient.Graphing; public static class GraphDrawer { + private enum GraphTimePeriod + { + DAY, + MONTH + } + private static readonly string GraphBackgroundPath; private static readonly Pen GraphingPenLow; private static readonly Pen GraphingPenHigh; - private static readonly Pen Last6HPen; + private static readonly Pen HighlightingPen; private static readonly Font Font; @@ -23,18 +30,18 @@ static GraphDrawer() { const string fontPath = @"assets\fonts\runescape_uf.ttf"; const string graphBackgroundPath = @"assets\textures\graph_background.png"; - + string executingAssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty; GraphBackgroundPath = Path.Combine(executingAssemblyPath, graphBackgroundPath); - string fontPath1 = Path.Combine(executingAssemblyPath, fontPath); - + string fPath = Path.Combine(executingAssemblyPath, fontPath); + FontCollection fontCollection = new(); - FontFamily family = fontCollection.Add(fontPath1); + FontFamily family = fontCollection.Add(fPath); Font = new Font(family, 14, FontStyle.Regular); - + GraphingPenLow = new SolidPen(new SolidBrush(Color.Chartreuse), 1f); GraphingPenHigh = new SolidPen(new SolidBrush(Color.Orange), 1f); - Last6HPen = new SolidPen(new SolidBrush(Color.Brown), 3f); + HighlightingPen = new SolidPen(new SolidBrush(Color.CadetBlue), 3f); } @@ -42,14 +49,67 @@ static GraphDrawer() /// Draws a price graph from the price history of an item. /// /// Item price history, in 5min intervals. Should contain at least 288x 5min data-points = 24h. - public static async Task DrawGraph(ItemPriceHistory history5MinIntervals) + /// Item price history, in 6h intervals. May be null if not available. + /// Latest buy price. + /// Latest sell price. + public static async Task DrawGraph( + ItemPriceHistory history5MinIntervals, + ItemPriceHistory? history6HourIntervals, + int latestBuy, + int latestSell) { - if (history5MinIntervals.Data.Count < 288) + const int graphPointsPerDay = 288; // Assuming 5min intervals. + + if (history5MinIntervals.Data.Count < graphPointsPerDay) throw new ArgumentException("The price history must contain at least 288x 5min data-points = 24h.", nameof(history5MinIntervals)); - List dataPoints = history5MinIntervals.Data.TakeLast(288).ToList(); + List dataPointsDay = history5MinIntervals.Data.TakeLast(graphPointsPerDay).ToList(); + dataPointsDay.Add( + new ItemPriceHistoryEntry + { + AvgHighPrice = latestBuy, + AvgLowPrice = latestSell + }); + + Image dayGraph = await CreateGraph(dataPointsDay, GraphTimePeriod.DAY); + Image? monthGraph = null; + + if (history6HourIntervals != null) + { + List dataPointsMonth = history6HourIntervals.Data.TakeLast(120).ToList(); + dataPointsMonth.Add( + new ItemPriceHistoryEntry + { + AvgHighPrice = latestBuy, + AvgLowPrice = latestSell + }); + + monthGraph = await CreateGraph(dataPointsMonth, GraphTimePeriod.MONTH); + } + + int width = Math.Max(dayGraph.Width, monthGraph?.Width ?? 0); + int height = dayGraph.Height + (monthGraph?.Height ?? 0); + Image combinedImage = new Image(width, height); + combinedImage.Mutate(ctx => ctx.DrawImage(dayGraph, new Point(0, 0), 1f)); + if (monthGraph != null) + combinedImage.Mutate(ctx => ctx.DrawImage(monthGraph, new Point(0, dayGraph.Height), 1f)); + + MemoryStream ms = new(); + await combinedImage.SaveAsync(ms, new PngEncoder()); + ms.Position = 0; + + combinedImage.Dispose(); + + return ms; + } + + + private static async Task CreateGraph(List dataPoints, GraphTimePeriod timePeriod) + { + const int graphPadding = 15; + // 460x80 pixels. - using Image img = await Image.LoadAsync(GraphBackgroundPath); + Image img = await Image.LoadAsync(GraphBackgroundPath); int imageWidth = img.Width; int imageHeight = img.Height; @@ -62,73 +122,128 @@ public static async Task DrawGraph(ItemPriceHistory history5MinInt continue; if ((int)entry.LowestPrice < minValue) minValue = (int)entry.LowestPrice; - + if (entry.HighestPrice == null) continue; if ((int)entry.HighestPrice > maxValue) maxValue = (int)entry.HighestPrice; } + // Determine required values. + float periodHighlightStartPoint = imageWidth - (imageWidth - graphPadding) / 4f; + int periodCount; + int periodCountForStartPoint; + string periodText; + string lastPeriodText; + switch (timePeriod) + { + case GraphTimePeriod.DAY: + periodCount = 288; + periodCountForStartPoint = 72; + periodText = "last 24h"; + lastPeriodText = "last 6h ->"; + break; + case GraphTimePeriod.MONTH: + periodCount = 120; + periodCountForStartPoint = 4; + periodText = "last 30d"; + lastPeriodText = "last day ->"; + break; + default: + throw new ArgumentOutOfRangeException(nameof(timePeriod), timePeriod, null); + } + + // Construct the graph points. List graphPointsLow = new(); List graphPointsHigh = new(); - - float last6HStartPoint = imageWidth - (imageWidth - 15) / 4f; for (int i = 0; i < dataPoints.Count; i++) { - if (dataPoints[i].LowestPrice != null) + if (dataPoints[i].AvgLowPrice != null) { - PointF point = NormalizedPointFromValues(i, (int)dataPoints[i].LowestPrice!, imageWidth, imageHeight, 15, 15, dataPoints.Count, minValue, maxValue); + PointF point = NormalizedPointFromValues( + i, + (int)dataPoints[i].AvgLowPrice!, + imageWidth, + imageHeight, + graphPadding, + graphPadding, + dataPoints.Count, + minValue, + maxValue); - if (i == 288 - 72) - last6HStartPoint = point.X; + if (i == periodCount - periodCountForStartPoint) + periodHighlightStartPoint = point.X; graphPointsLow.Add(point); } - - if (dataPoints[i].HighestPrice != null) + + if (dataPoints[i].AvgHighPrice != null) { - PointF point = NormalizedPointFromValues(i, (int)dataPoints[i].HighestPrice!, imageWidth, imageHeight, 15, 15, dataPoints.Count, minValue, maxValue); + PointF point = NormalizedPointFromValues( + i, + (int)dataPoints[i].AvgHighPrice!, + imageWidth, + imageHeight, + graphPadding, + graphPadding, + dataPoints.Count, + minValue, + maxValue); + + if (i == periodCount - periodCountForStartPoint) + periodHighlightStartPoint = point.X; - if (i == 288 - 72) - last6HStartPoint = point.X; - graphPointsHigh.Add(point); } } - + + // Construct the graph paths. PathBuilder graphPathLow = new(); PathBuilder graphPathHigh = new(); graphPathLow.AddLines(graphPointsLow.ToArray()); graphPathHigh.AddLines(graphPointsHigh.ToArray()); - + // Draw the graphs. img.Mutate(ctx => ctx.Draw(GraphingPenLow, graphPathLow.Build())); img.Mutate(ctx => ctx.Draw(GraphingPenHigh, graphPathHigh.Build())); - - // Construct points for the 6h helper line. - List last6HPoints = new(); - int targetHeight6H = imageHeight - 15; - int latestPoint = imageWidth - 15; - - last6HPoints.Add(new PointF(last6HStartPoint, targetHeight6H)); - last6HPoints.Add(new PointF(latestPoint, targetHeight6H)); - // Draw the 6h helper line + // Construct points for the helper line. + List highlightedPeriodPoints = new(); + int targetHeightPeriod = imageHeight - graphPadding; + int latestPoint = imageWidth - graphPadding; + + // Add the last period points. + highlightedPeriodPoints.Add(new PointF(periodHighlightStartPoint, targetHeightPeriod)); + highlightedPeriodPoints.Add(new PointF(latestPoint, targetHeightPeriod)); + + // Draw the helper line PathBuilder helperPath = new(); - helperPath.AddLines(last6HPoints.ToArray()); - img.Mutate(ctx => ctx.Draw(Last6HPen, helperPath.Build())); - - // Draw the "last 6h ->" text. - img.Mutate(ctx => ctx.DrawText("last 6h ->", Font, Color.Wheat, new PointF(last6HStartPoint - 60, imageHeight - 20))); + helperPath.AddLines(highlightedPeriodPoints.ToArray()); + img.Mutate(ctx => ctx.Draw(HighlightingPen, helperPath.Build())); - MemoryStream ms = new(); - await img.SaveAsync(ms, new PngEncoder()); - ms.Position = 0; - return ms; + // Draw the "last period ->" text. + img.Mutate(ctx => ctx.DrawText(lastPeriodText, Font, Color.Wheat, new PointF(periodHighlightStartPoint - 60, imageHeight - graphPadding - 5))); + + // Add the "period" text. + img.Mutate(ctx => ctx.DrawText(periodText, Font, Color.Wheat, new PointF(10, 5))); + + return img; } - private static PointF NormalizedPointFromValues(int pointIndex, int pointValue, int imageWidth, int imageHeight, int widthPadding, int heightPadding, int pointsCount, int lowestValue, int highestValue) + /// + /// Normalizes a data point to a point on the graph. + /// + private static PointF NormalizedPointFromValues( + int pointIndex, + int pointValue, + int imageWidth, + int imageHeight, + int widthPadding, + int heightPadding, + int pointsCount, + int lowestValue, + int highestValue) { float normalizedX = pointIndex * (imageWidth - widthPadding - widthPadding) / (float)(pointsCount - 1) + widthPadding; float normalizedY = (pointValue - lowestValue) * (imageHeight - heightPadding - heightPadding) / (float)(highestValue - lowestValue) + heightPadding; diff --git a/DiscordClient/Program.cs b/DiscordClient/Program.cs index 4210fc4..950777a 100644 --- a/DiscordClient/Program.cs +++ b/DiscordClient/Program.cs @@ -89,7 +89,7 @@ private static async void OnDumpsUpdated() foreach (SocketTextChannel channel in channels) { // Get the graph image. - MemoryStream memStream = await GraphDrawer.DrawGraph(dump.PriceHistory); + MemoryStream memStream = await GraphDrawer.DrawGraph(dump.PriceHistory5Min, dump.PriceHistory6Hour, dump.InstaBuyPrice, dump.InstaSellPrice); FileAttachment graphAttachment = new(memStream, "graph.png"); RestUserMessage msg = await channel.SendFileAsync(graphAttachment); string graphUrl = msg.Attachments.First().Url; @@ -97,6 +97,7 @@ private static async void OnDumpsUpdated() Embed embed = DumpEmbedBuilder.BuildEmbed(dump, graphUrl); await channel.SendMessageAsync(embed: embed); await msg.DeleteAsync(); + await Task.Delay(2000); } } catch (Exception e) diff --git a/OsrsFlipper/Api/OsrsApiController.cs b/OsrsFlipper/Api/OsrsApiController.cs index cdf916d..e82514c 100644 --- a/OsrsFlipper/Api/OsrsApiController.cs +++ b/OsrsFlipper/Api/OsrsApiController.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using OsrsFlipper.Data.Mapping; +using OsrsFlipper.Data.Mapping; using OsrsFlipper.Data.Price.Average; using OsrsFlipper.Data.Price.Latest; using OsrsFlipper.Data.TimeSeries; @@ -103,7 +102,7 @@ public async Task GetItemMapping() } - public async Task GetPriceHistory(ItemData item, TimeSeriesApi.TimeSeriesTimeStep timestep) + public async Task GetPriceHistory(ItemData item, TimeSeriesTimeStep timestep) { return await _timeSeriesApi.GetPriceHistory(_client, item, timestep); } diff --git a/OsrsFlipper/Api/TimeSeriesApi.cs b/OsrsFlipper/Api/TimeSeriesApi.cs index 3e53314..4a2b5c1 100644 --- a/OsrsFlipper/Api/TimeSeriesApi.cs +++ b/OsrsFlipper/Api/TimeSeriesApi.cs @@ -6,13 +6,6 @@ namespace OsrsFlipper.Api; public class TimeSeriesApi : OsrsApi { - public enum TimeSeriesTimeStep - { - FiveMinutes, - Hour, - SixHours, - Day - } private readonly RestRequest _request; diff --git a/OsrsFlipper/Api/TimeSeriesTimeStep.cs b/OsrsFlipper/Api/TimeSeriesTimeStep.cs new file mode 100644 index 0000000..acb5e12 --- /dev/null +++ b/OsrsFlipper/Api/TimeSeriesTimeStep.cs @@ -0,0 +1,9 @@ +namespace OsrsFlipper.Api; + +public enum TimeSeriesTimeStep +{ + FiveMinutes, + Hour, + SixHours, + Day +} \ No newline at end of file diff --git a/OsrsFlipper/Caching/CacheEntry.cs b/OsrsFlipper/Caching/CacheEntry.cs index 82bfa73..1536e2e 100644 --- a/OsrsFlipper/Caching/CacheEntry.cs +++ b/OsrsFlipper/Caching/CacheEntry.cs @@ -54,6 +54,12 @@ public class CacheEntry public readonly AveragedPriceData Price24HourAverage = new(); + /// + /// Potential maximum amount that can be bought in 4 hours. + /// + public int MaxBuyAmount => Math.Min(Item.GeBuyLimit, Price24HourAverage.TotalVolume); + + public CacheEntry(ItemData item) { Item = item; diff --git a/OsrsFlipper/Filtering/FilterCollection.cs b/OsrsFlipper/Filtering/FilterCollection.cs index 3da56c8..31a89c8 100644 --- a/OsrsFlipper/Filtering/FilterCollection.cs +++ b/OsrsFlipper/Filtering/FilterCollection.cs @@ -54,4 +54,18 @@ public bool PassesFlipTest(CacheEntry itemData, ItemPriceHistory history) return true; } + + + public void DebugFilters() + { + foreach (PruneFilter filter in _pruneFilters) + { + Logger.Verbose($"Prune filter {filter.GetType().Name} block count: {filter.ItemsFailed} / {filter.ItemsChecked}"); + } + + foreach (FlipFilter filter in _flipFilters) + { + Logger.Verbose($"Flip filter {filter.GetType().Name} block count: {filter.ItemsFailed} / {filter.ItemsChecked}"); + } + } } \ No newline at end of file diff --git a/OsrsFlipper/Filtering/Filters/PruneFilters/InsufficientDataFilter.cs b/OsrsFlipper/Filtering/Filters/PruneFilters/InsufficientDataFilter.cs new file mode 100644 index 0000000..a5f2b03 --- /dev/null +++ b/OsrsFlipper/Filtering/Filters/PruneFilters/InsufficientDataFilter.cs @@ -0,0 +1,50 @@ +using OsrsFlipper.Caching; + +namespace OsrsFlipper.Filtering.Filters.PruneFilters; + +/// +/// Checks if the item has valid price data. +/// If any of the price data is insufficient, the item is not considered for flipping. +/// +internal class InsufficientDataFilter : PruneFilter +{ + protected override bool CanPassFilter(CacheEntry itemData) + { + bool latestValid = itemData.PriceLatest.IsValid; + bool average5MinValid = itemData.Price5MinAverage.IsValid; + bool average5MinOffsetValid = itemData.Price5MinAverageOffset.IsValid; + bool average10MinValid = itemData.Price10MinAverage.IsValid; + bool average30MinValid = itemData.Price30MinAverage.IsValid; + bool average1HValid = itemData.Price1HourAverage.IsValid; + bool average6HValid = itemData.Price6HourAverage.IsValid; + bool average24HValid = itemData.Price24HourAverage.IsValid; + + bool allDataValid = latestValid && + average5MinValid && + average5MinOffsetValid && + average10MinValid && + average30MinValid && + average1HValid && + average6HValid && + average24HValid; + + /*if (!latestValid) + Logger.Verbose($"Item {itemData.Item} has invalid latest data."); + if (!average5MinValid) + Logger.Verbose($"Item {itemData.Item} has invalid 5min average data."); + if (!average5MinOffsetValid) + Logger.Verbose($"Item {itemData.Item} has invalid 5min average offset data."); + if (!average10MinValid) + Logger.Verbose($"Item {itemData.Item} has invalid 10min average data."); + if (!average30MinValid) + Logger.Verbose($"Item {itemData.Item} has invalid 30min average data."); + if (!average1HValid) + Logger.Verbose($"Item {itemData.Item} has invalid 1h average data."); + if (!average6HValid) + Logger.Verbose($"Item {itemData.Item} has invalid 6h average data."); + if (!average24HValid) + Logger.Verbose($"Item {itemData.Item} has invalid 24h average data.");*/ + + return allDataValid; + } +} \ No newline at end of file diff --git a/OsrsFlipper/Filtering/Filters/PruneFilters/PotentialProfitFilter.cs b/OsrsFlipper/Filtering/Filters/PruneFilters/PotentialProfitFilter.cs index d1745e9..33c366c 100644 --- a/OsrsFlipper/Filtering/Filters/PruneFilters/PotentialProfitFilter.cs +++ b/OsrsFlipper/Filtering/Filters/PruneFilters/PotentialProfitFilter.cs @@ -35,7 +35,7 @@ protected override bool CanPassFilter(CacheEntry itemData) // The price the item should be bought at to make a profit. int priceToBuyAt = itemData.PriceLatest.LowestPrice; // The price the item should be sold at to make a profit. - int priceToSellAt = itemData.Price1HourAverage.HighestPrice; // NOTE: Possibly use Price1HourAverage.AveragePrice instead of Price1HourAverage.HighestPrice? + int priceToSellAt = itemData.Price1HourAverage.HighestPrice; // Calculate the margin. int margin = priceToSellAt - priceToBuyAt; @@ -45,7 +45,7 @@ protected override bool CanPassFilter(CacheEntry itemData) margin = (int)(margin * 0.99); // Calculate the potential profit. - int potentialProfit = margin * Math.Min(itemData.Item.GeBuyLimit, itemData.Price24HourAverage.TotalVolume); + int potentialProfit = margin * itemData.MaxBuyAmount; // Check if the potential profit is above the minimum required. return potentialProfit >= _minPotentialProfit; diff --git a/OsrsFlipper/Filtering/Filters/PruneFilters/ValidDataFilter.cs b/OsrsFlipper/Filtering/Filters/PruneFilters/ValidDataFilter.cs deleted file mode 100644 index 8cb061d..0000000 --- a/OsrsFlipper/Filtering/Filters/PruneFilters/ValidDataFilter.cs +++ /dev/null @@ -1,22 +0,0 @@ -using OsrsFlipper.Caching; - -namespace OsrsFlipper.Filtering.Filters.PruneFilters; - -/// -/// Checks if the item has valid price data. -/// If any of the price data is invalid, the item is not considered for flipping. -/// -internal class ValidDataFilter : PruneFilter -{ - protected override bool CanPassFilter(CacheEntry itemData) - { - return itemData.PriceLatest.IsValid && - itemData.Price5MinAverageOffset.IsValid && - itemData.Price5MinAverage.IsValid && - itemData.Price10MinAverage.IsValid && - itemData.Price30MinAverage.IsValid && - itemData.Price1HourAverage.IsValid && - itemData.Price6HourAverage.IsValid && - itemData.Price24HourAverage.IsValid; - } -} \ No newline at end of file diff --git a/OsrsFlipper/Flipper.cs b/OsrsFlipper/Flipper.cs index 2283465..6b39347 100644 --- a/OsrsFlipper/Flipper.cs +++ b/OsrsFlipper/Flipper.cs @@ -5,13 +5,14 @@ using OsrsFlipper.Data.Price.Latest; using OsrsFlipper.Data.TimeSeries; using OsrsFlipper.Filtering; -using OsrsFlipper.Filtering.Filters; using OsrsFlipper.Filtering.Filters.PruneFilters; namespace OsrsFlipper; public sealed class Flipper : IDisposable { + private const bool DEBUG_FILTERS = false; + /// /// The API controller used to fetch data from the OSRS API. /// @@ -47,7 +48,7 @@ private Flipper(OsrsApiController apiController, ItemCache cache, int cooldownMi // Add wanted filters to the filter collection. // Prune filters are used to quickly discard items that are not worth considering, and to avoid fetching additional API data for them. _filterCollection - .AddPruneFilter(new ValidDataFilter()) // Skip items with invalid data. + //.AddPruneFilter(new InsufficientDataFilter()) // Skip items with invalid data. .AddPruneFilter(new ItemCooldownFilter(_cooldownManager)) // Skip items that are on a cooldown. .AddPruneFilter(new Item24HAveragePriceFilter(50, 50_000_000)) // Skip items with a 24-hour average price outside the range X - Y. .AddPruneFilter(new PotentialProfitFilter(500_000, true)) // Skip items with a potential profit less than X. @@ -103,26 +104,36 @@ public async Task> FindDumps() } // Check if the item passes all pruning filters. + // DEBUG-TEST: if (entry.Item.Id != 2) if (!_filterCollection.PassesPruneTest(entry)) continue; itemsPassedPruneCount++; // Get the price history for the item. - ItemPriceHistory? history = await GetPriceHistory(entry.Item, TimeSeriesApi.TimeSeriesTimeStep.FiveMinutes); - if (history == null) + ItemPriceHistory? history5Min = await GetPriceHistory(entry.Item, TimeSeriesTimeStep.FiveMinutes); + if (history5Min == null) continue; // Check if the item passes all flip filters. - if (!_filterCollection.PassesFlipTest(entry, history)) + // DEBUG-TEST: if (entry.Item.Id != 2) + if (!_filterCollection.PassesFlipTest(entry, history5Min)) continue; - ItemDump dump = ConstructDumpObject(entry, history); + // Get the additional 6-hour price history for the item. + ItemPriceHistory? history6Hour = await GetPriceHistory(entry.Item, TimeSeriesTimeStep.SixHours); + + ItemDump dump = ConstructDumpObject(entry, history5Min, history6Hour); // Add the flip to the list and set the item on cooldown. flips.Add(dump); _cooldownManager.SetCooldown(entry.Item.Id, TimeSpan.FromMinutes(_cooldownMinutes)); } Logger.Verbose($"{itemsPassedPruneCount} items passed all pruning filters."); + + if (DEBUG_FILTERS) + { + _filterCollection.DebugFilters(); + } return flips; } @@ -131,7 +142,7 @@ public async Task> FindDumps() /// /// Constructs an from the given . /// - private static ItemDump ConstructDumpObject(CacheEntry entry, ItemPriceHistory history) + private static ItemDump ConstructDumpObject(CacheEntry entry, ItemPriceHistory history5Min, ItemPriceHistory? history6Hour) { // The price the item should be bought at to make a profit. int priceToBuyAt = entry.PriceLatest.LowestPrice; @@ -157,7 +168,8 @@ private static ItemDump ConstructDumpObject(CacheEntry entry, ItemPriceHistory h entry.PriceLatest.LastSellTime, entry.Price30MinAverage.AveragePrice, entry.Price6HourAverage.AveragePrice, - history); + history5Min, + history6Hour); } @@ -222,7 +234,7 @@ public async Task RefreshCache() } - private async Task GetPriceHistory(ItemData item, TimeSeriesApi.TimeSeriesTimeStep timestep) + private async Task GetPriceHistory(ItemData item, TimeSeriesTimeStep timestep) { return await _apiController.GetPriceHistory(item, timestep); } diff --git a/OsrsFlipper/ItemDump.cs b/OsrsFlipper/ItemDump.cs index 71d0db7..cab3757 100644 --- a/OsrsFlipper/ItemDump.cs +++ b/OsrsFlipper/ItemDump.cs @@ -13,7 +13,17 @@ public class ItemDump public readonly DateTime LastInstaSellTime; public readonly int AveragePrice30Min; public readonly int AveragePrice6Hour; - public readonly ItemPriceHistory PriceHistory; + + /// + /// Price history with 5-minute intervals, with up to 365 data points. + /// + public readonly ItemPriceHistory PriceHistory5Min; + + /// + /// Price history with 6-hour intervals, with up to 365 data points. + /// + public readonly ItemPriceHistory? PriceHistory6Hour; + // Calculated values: public readonly int? PotentialProfit; public readonly double RoiPercentage; @@ -29,7 +39,8 @@ public ItemDump(ItemData item, DateTime lastInstaSellTime, int averagePrice30Min, int averagePrice6Hour, - ItemPriceHistory priceHistory) + ItemPriceHistory priceHistory5Min, + ItemPriceHistory? priceHistory6Hour) { Item = item; PotentialProfit = potentialProfit; @@ -41,7 +52,8 @@ public ItemDump(ItemData item, LastInstaSellTime = lastInstaSellTime; AveragePrice30Min = averagePrice30Min; AveragePrice6Hour = averagePrice6Hour; - PriceHistory = priceHistory; + PriceHistory5Min = priceHistory5Min; + PriceHistory6Hour = priceHistory6Hour; } diff --git a/OsrsFlipper/Utils.cs b/OsrsFlipper/Utils.cs index 23abdb6..5082c71 100644 --- a/OsrsFlipper/Utils.cs +++ b/OsrsFlipper/Utils.cs @@ -21,14 +21,14 @@ public static long DateTimeToUnixTime(DateTime dateTime) } - public static string AsString(this TimeSeriesApi.TimeSeriesTimeStep timeStep) + public static string AsString(this TimeSeriesTimeStep timeStep) { return timeStep switch { - TimeSeriesApi.TimeSeriesTimeStep.FiveMinutes => "5m", - TimeSeriesApi.TimeSeriesTimeStep.Hour => "1h", - TimeSeriesApi.TimeSeriesTimeStep.SixHours => "6h", - TimeSeriesApi.TimeSeriesTimeStep.Day => "24h", + TimeSeriesTimeStep.FiveMinutes => "5m", + TimeSeriesTimeStep.Hour => "1h", + TimeSeriesTimeStep.SixHours => "6h", + TimeSeriesTimeStep.Day => "24h", _ => throw new ArgumentOutOfRangeException(nameof(timeStep), timeStep, null) }; }