diff --git a/Bots/DennisBot/DennisMoneyBot.csproj b/Bots/DennisBot/DennisMoneyBot.csproj
new file mode 100644
index 0000000..dda88a4
--- /dev/null
+++ b/Bots/DennisBot/DennisMoneyBot.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+ ..\..\Build\Bots
+ false
+
+
+ ..\..\Build\Bots
+ false
+
+
+
+ ..\..\Build\NasdaqTrader.Bot.Core.dll
+
+
+
+
diff --git a/Bots/DennisBot/DennisMoneyBot.sln b/Bots/DennisBot/DennisMoneyBot.sln
new file mode 100644
index 0000000..0e0c5e5
--- /dev/null
+++ b/Bots/DennisBot/DennisMoneyBot.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36221.1 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DennisMoneyBot", "DennisMoneyBot.csproj", "{D477DB95-F8AC-0D1D-C1CF-1B7D73E0C2BA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D477DB95-F8AC-0D1D-C1CF-1B7D73E0C2BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D477DB95-F8AC-0D1D-C1CF-1B7D73E0C2BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D477DB95-F8AC-0D1D-C1CF-1B7D73E0C2BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D477DB95-F8AC-0D1D-C1CF-1B7D73E0C2BA}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {CC4A2F4E-7123-498D-83E9-1206EC1B00EF}
+ EndGlobalSection
+EndGlobal
diff --git a/Bots/DennisBot/ITrader.cs b/Bots/DennisBot/ITrader.cs
new file mode 100644
index 0000000..d3dcf5b
--- /dev/null
+++ b/Bots/DennisBot/ITrader.cs
@@ -0,0 +1,7 @@
+using NasdaqTrader.Bot.Core;
+
+namespace DennisMoneyBot;
+internal interface ITrader
+{
+ void DoTrades(ITraderSystemContext systemContext, ITraderBot bot);
+}
diff --git a/Bots/DennisBot/MoneyBot.cs b/Bots/DennisBot/MoneyBot.cs
new file mode 100644
index 0000000..a481412
--- /dev/null
+++ b/Bots/DennisBot/MoneyBot.cs
@@ -0,0 +1,18 @@
+using DennisMoneyBot.Traders;
+
+using NasdaqTrader.Bot.Core;
+
+namespace DennisMoneyBot;
+
+public class MoneyBot : ITraderBot
+{
+ public string CompanyName => "Dennis Makes It Rain";
+
+ //ITrader _selectedTrader = new DayTrader();
+ ITrader _selectedTrader = new ExperimentalTrader();
+
+ public async Task DoTurn(ITraderSystemContext systemContext)
+ {
+ _selectedTrader.DoTrades(systemContext, this);
+ }
+}
\ No newline at end of file
diff --git a/Bots/DennisBot/StolenDateExtensions.cs b/Bots/DennisBot/StolenDateExtensions.cs
new file mode 100644
index 0000000..2d448d7
--- /dev/null
+++ b/Bots/DennisBot/StolenDateExtensions.cs
@@ -0,0 +1,87 @@
+namespace DennisMoneyBot;
+internal static class StolenDateExtensions
+{
+ ///
+ /// Determines if this date is a federal holiday.
+ ///
+ /// This date
+ /// True if this date is a federal holiday
+ public static bool IsFederalHoliday(this DateOnly date)
+ {
+ // to ease typing
+ int nthWeekDay = (int)(Math.Ceiling((double)date.Day / 7.0d));
+ DayOfWeek dayName = date.DayOfWeek;
+ bool isThursday = dayName == DayOfWeek.Thursday;
+ bool isFriday = dayName == DayOfWeek.Friday;
+ bool isMonday = dayName == DayOfWeek.Monday;
+ bool isWeekend = dayName == DayOfWeek.Saturday || dayName == DayOfWeek.Sunday;
+
+
+ //Junteeth
+ if (new DateOnly(date.Year, 6, 19) == date) return true;
+ //good friday
+ if (DateOnly.FromDateTime(EasterSunday(date.Year)).AddDays(-2) == date) return true;
+
+ // New Years Day (Jan 1, or preceding Friday/following Monday if weekend)
+ if ((date.Month == 12 && date.Day == 31 && isFriday) ||
+ (date.Month == 1 && date.Day == 1 && !isWeekend) ||
+ (date.Month == 1 && date.Day == 2 && isMonday)) return true;
+
+ // MLK day (3rd monday in January)
+ if (date.Month == 1 && isMonday && nthWeekDay == 3) return true;
+
+ // President’s Day (3rd Monday in February)
+ if (date.Month == 2 && isMonday && nthWeekDay == 3) return true;
+
+ // Memorial Day (Last Monday in May)
+ if (date.Month == 5 && isMonday && date.AddDays(7).Month == 6) return true;
+
+ // Independence Day (July 4, or preceding Friday/following Monday if weekend)
+ if ((date.Month == 7 && date.Day == 3 && isFriday) ||
+ (date.Month == 7 && date.Day == 4 && !isWeekend) ||
+ (date.Month == 7 && date.Day == 5 && isMonday)) return true;
+
+ // Labor Day (1st Monday in September)
+ if (date.Month == 9 && isMonday && nthWeekDay == 1) return true;
+
+ // Columbus Day (2nd Monday in October)
+ if (date.Month == 10 && isMonday && nthWeekDay == 2) return true;
+
+ // Veteran’s Day (November 11, or preceding Friday/following Monday if weekend))
+ if ((date.Month == 11 && date.Day == 10 && isFriday) ||
+ (date.Month == 11 && date.Day == 11 && !isWeekend) ||
+ (date.Month == 11 && date.Day == 12 && isMonday)) return true;
+
+ // Thanksgiving Day (4th Thursday in November)
+ if (date.Month == 11 && isThursday && nthWeekDay == 4) return true;
+
+ // Christmas Day (December 25, or preceding Friday/following Monday if weekend))
+ if ((date.Month == 12 && date.Day == 24 && isFriday) ||
+ (date.Month == 12 && date.Day == 25 && !isWeekend) ||
+ (date.Month == 12 && date.Day == 26 && isMonday)) return true;
+
+ return false;
+ }
+
+ public static DateTime EasterSunday(int year)
+ {
+ int day = 0;
+ int month = 0;
+
+ int g = year % 19;
+ int c = year / 100;
+ int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
+ int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));
+
+ day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
+ month = 3;
+
+ if (day > 31)
+ {
+ month++;
+ day -= 31;
+ }
+
+ return new DateTime(year, month, day);
+ }
+}
diff --git a/Bots/DennisBot/Traders/DayTrader.cs b/Bots/DennisBot/Traders/DayTrader.cs
new file mode 100644
index 0000000..0ec4d11
--- /dev/null
+++ b/Bots/DennisBot/Traders/DayTrader.cs
@@ -0,0 +1,45 @@
+using DennisMoneyBot.Utility;
+
+using NasdaqTrader.Bot.Core;
+
+namespace DennisMoneyBot.Traders;
+internal class DayTrader : ITrader
+{
+ public void DoTrades(ITraderSystemContext systemContext, ITraderBot bot)
+ {
+ var today = systemContext.CurrentDate;
+ var nextTradingDay = DateCalculator.GetNextValidDate(today);
+ var currentHoldings = systemContext.GetHoldings(bot);
+
+ foreach (var holding in currentHoldings)
+ {
+ systemContext.SellStock(bot, holding.Listing, holding.Amount);
+ }
+
+ if (nextTradingDay.Year != today.Year)
+ {
+ return;
+ }
+
+ var bestOneDayTrades = systemContext.GetListings()
+ .OrderByDescending(listing =>
+ listing.PricePoints.FirstOrDefault(p => p.Date == nextTradingDay)?.Price - listing.PricePoints.FirstOrDefault(p => p.Date == today)?.Price);
+
+ int tradesLeft = 2;
+ foreach (var listing in bestOneDayTrades)
+ {
+ int amountToBuy = (int)Math.Min(1000, Math.Floor(systemContext.GetCurrentCash(bot) / listing.PricePoints.First(p => p.Date == today).Price));
+ if (amountToBuy == 0)
+ {
+ continue;
+ }
+
+ systemContext.BuyStock(bot, listing, amountToBuy);
+
+ if (--tradesLeft < 1)
+ {
+ break;
+ }
+ }
+ }
+}
diff --git a/Bots/DennisBot/Traders/ExperimentalTrader.cs b/Bots/DennisBot/Traders/ExperimentalTrader.cs
new file mode 100644
index 0000000..6f7770e
--- /dev/null
+++ b/Bots/DennisBot/Traders/ExperimentalTrader.cs
@@ -0,0 +1,52 @@
+using DennisMoneyBot.Utility;
+
+using NasdaqTrader.Bot.Core;
+
+namespace DennisMoneyBot.Traders;
+internal class ExperimentalTrader : ITrader
+{
+ public void DoTrades(ITraderSystemContext systemContext, ITraderBot bot)
+ {
+ int windowSize = 1;
+ decimal saleCutoffRatio = 1.15M;
+ int daysToIgnoreSaleRules = 14;
+ int minimalSaleValue = 800;
+
+ var today = systemContext.CurrentDate;
+ var nextTradingDay = DateCalculator.GetBusinessDaysInTheFuture(today, windowSize);
+ IHolding[]? currentHoldings = systemContext.GetHoldings(bot);
+
+ foreach (var holding in currentHoldings)
+ {
+ if ((PriceCalculator.GetValueOfHoldingOnDate(holding, today) > minimalSaleValue)
+ &&
+ (PriceCalculator.GetPricePointRatio(holding.Listing, today, nextTradingDay) < saleCutoffRatio
+ || (systemContext.StartDate.AddDays(daysToIgnoreSaleRules) > systemContext.CurrentDate)))
+ {
+ systemContext.SellStock(bot, holding.Listing, holding.Amount);
+ }
+ }
+
+ var bestOneDayTrades = systemContext.GetListings()
+ .OrderByDescending(listing =>
+ PriceCalculator.CalculateProfitMargin(systemContext, bot, listing, today, nextTradingDay));
+
+ int tradesLeft = systemContext.GetTradesLeftForToday(bot);
+ foreach (var listing in bestOneDayTrades)
+ {
+ int amountToBuy = (int)Math.Min(1000, Math.Floor(systemContext.GetCurrentCash(bot) / PriceCalculator.GetPriceForListingOnDate(listing, today)));
+ if (amountToBuy == 0)
+ {
+ continue;
+ }
+
+ systemContext.BuyStock(bot, listing, amountToBuy);
+
+ if (--tradesLeft < 1)
+ {
+ break;
+ }
+ }
+ // nog iets doen met voorrang geven aan dingen die gewoon veel waard zijn en niet meer gaan dalen, zodat dat kan accumuleren
+ }
+}
diff --git a/Bots/DennisBot/Utility/DateCalculator.cs b/Bots/DennisBot/Utility/DateCalculator.cs
new file mode 100644
index 0000000..d4925d1
--- /dev/null
+++ b/Bots/DennisBot/Utility/DateCalculator.cs
@@ -0,0 +1,23 @@
+namespace DennisMoneyBot.Utility;
+internal static class DateCalculator
+{
+ public static DateOnly GetNextValidDate(DateOnly currentDate)
+ {
+ DateOnly nextDate = currentDate.AddDays(1);
+ while (nextDate.DayOfWeek == DayOfWeek.Saturday || nextDate.DayOfWeek == DayOfWeek.Sunday || nextDate.IsFederalHoliday())
+ {
+ nextDate = nextDate.AddDays(1);
+ }
+ return nextDate;
+ }
+
+ public static DateOnly GetBusinessDaysInTheFuture(DateOnly currentDate, int amountOfDays)
+ {
+ var nextDay = currentDate;
+ for (int i = 0; i < amountOfDays; i++)
+ {
+ nextDay = GetNextValidDate(nextDay);
+ }
+ return nextDay;
+ }
+}
diff --git a/Bots/DennisBot/Utility/PriceCalculator.cs b/Bots/DennisBot/Utility/PriceCalculator.cs
new file mode 100644
index 0000000..5e7256b
--- /dev/null
+++ b/Bots/DennisBot/Utility/PriceCalculator.cs
@@ -0,0 +1,33 @@
+using NasdaqTrader.Bot.Core;
+
+namespace DennisMoneyBot.Utility;
+internal static class PriceCalculator
+{
+ public static decimal GetPriceForListingOnDate(IStockListing listing, DateOnly referenceDate)
+ {
+ return listing.PricePoints.FirstOrDefault(p => p.Date == referenceDate)?.Price ?? 0;
+ }
+
+ public static decimal GetPricePointRatio(IStockListing listing, DateOnly today, DateOnly nextReferenceDay)
+ {
+ return GetPriceForListingOnDate(listing, nextReferenceDay) / GetPriceForListingOnDate(listing, today);
+ }
+
+ public static decimal GetValueOfHoldingOnDate(IHolding holding, DateOnly referenceDate)
+ {
+ return holding.Amount * GetPriceForListingOnDate(holding.Listing, referenceDate);
+ }
+
+ public static decimal CalculateProfitMargin(ITraderSystemContext systemContext, ITraderBot bot, IStockListing listing, DateOnly today, DateOnly nextReferenceDay)
+ {
+ var nextPrice = GetPriceForListingOnDate(listing, nextReferenceDay);
+ var currentPrice = GetPriceForListingOnDate(listing, today);
+ var priceDifference = nextPrice - currentPrice;
+
+ var amountToBuy =
+ currentPrice == 0
+ ? 0
+ : Math.Min(1000, Math.Floor(systemContext.GetCurrentCash(bot) / currentPrice));
+ return amountToBuy * priceDifference;
+ }
+}