From 0a052ca840added7a831ceeba6bb5a8be2bdb33a Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:37:15 +0200 Subject: [PATCH 1/6] Fork basics --- .gitignore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bcfa3b4..ee1dbc9 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,8 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store -_NCrunch* \ No newline at end of file +_NCrunch* +/Bots/AleixBot/.vs +/Bots/ExampleTraderBot/.vs/ExampleTraderBot +/Source/.vs +/Build/Results From 2f969588bdc7adfc1e766abd277b157154e3940e Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:49:29 +0200 Subject: [PATCH 2/6] First fork Test --- Bots/AleixBot/AleixBot.cs | 18 ++++++++++++++++++ Bots/AleixBot/AleixBot.csproj | 23 +++++++++++++++++++++++ Bots/AleixBot/AleixBot.sln | 22 ++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 Bots/AleixBot/AleixBot.cs create mode 100644 Bots/AleixBot/AleixBot.csproj create mode 100644 Bots/AleixBot/AleixBot.sln diff --git a/Bots/AleixBot/AleixBot.cs b/Bots/AleixBot/AleixBot.cs new file mode 100644 index 0000000..e031fbc --- /dev/null +++ b/Bots/AleixBot/AleixBot.cs @@ -0,0 +1,18 @@ +using NasdaqTrader.Bot.Core; + +namespace AleixBot; + +public class AleixBot : ITraderBot +{ + public string CompanyName => "Beginner Investments"; + + public void DoTurn(ITraderSystemContext systemContext) + { + var listings = systemContext.GetListings(); + var cash = systemContext.GetCurrentCash(this); + var currentDate = systemContext.CurrentDate; + var tradesLeft = systemContext.GetTradesLeftForToday(this); + + systemContext.BuyStock(this, listings[0], 1); + } +} \ No newline at end of file diff --git a/Bots/AleixBot/AleixBot.csproj b/Bots/AleixBot/AleixBot.csproj new file mode 100644 index 0000000..dda88a4 --- /dev/null +++ b/Bots/AleixBot/AleixBot.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + + + ..\..\Build\Bots + false + + + ..\..\Build\Bots + false + + + + ..\..\Build\NasdaqTrader.Bot.Core.dll + + + + diff --git a/Bots/AleixBot/AleixBot.sln b/Bots/AleixBot/AleixBot.sln new file mode 100644 index 0000000..9fb532a --- /dev/null +++ b/Bots/AleixBot/AleixBot.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35527.113 d17.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AleixBot", "AleixBot.csproj", "{4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From b1d189f8ada6a3f6a386d083483bb82580ca4ed2 Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:52:07 +0200 Subject: [PATCH 3/6] update basic bot --- Bots/AleixBot/AleixBot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bots/AleixBot/AleixBot.cs b/Bots/AleixBot/AleixBot.cs index e031fbc..28c230f 100644 --- a/Bots/AleixBot/AleixBot.cs +++ b/Bots/AleixBot/AleixBot.cs @@ -6,7 +6,7 @@ public class AleixBot : ITraderBot { public string CompanyName => "Beginner Investments"; - public void DoTurn(ITraderSystemContext systemContext) + public async Task DoTurn(ITraderSystemContext systemContext) { var listings = systemContext.GetListings(); var cash = systemContext.GetCurrentCash(this); From 00c509b8cd42b83c30d9c8a1cd99536f689511a4 Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Thu, 19 Jun 2025 16:45:27 +0200 Subject: [PATCH 4/6] No gitignore Whoops --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index f9e890f..c4c704b 100644 --- a/.gitignore +++ b/.gitignore @@ -132,7 +132,3 @@ $RECYCLE.BIN/ .DS_Store _NCrunch* -/Bots/AleixBot/.vs -/Bots/ExampleTraderBot/.vs/ExampleTraderBot -/Source/.vs -/Build/Results From c94c680e0b234415eb79f524e1b3667ba3718dc3 Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:01:57 +0200 Subject: [PATCH 5/6] =?UTF-8?q?Nieuwe=20bot=20die=20w=C3=A9l=20iets=20kan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bots/AleixBot.Tests/AleixBot.Tests.csproj | 27 ++++++ Bots/AleixBot.Tests/ListingPickerTests.cs | 24 +++++ Bots/AleixBot/AleixBot.cs | 69 +++++++++++++- Bots/AleixBot/AleixBot.sln | 11 ++- Bots/AleixBot/ListingPicker.cs | 89 +++++++++++++++++++ Bots/AleixBot/Models/IntermediateListing.cs | 5 ++ Bots/AleixBot/Models/Period.cs | 3 + .../Models/WindowedSumPerListingPerDate.cs | 3 + Bots/AleixBot/Properties/launchSettings.json | 11 +++ 9 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 Bots/AleixBot.Tests/AleixBot.Tests.csproj create mode 100644 Bots/AleixBot.Tests/ListingPickerTests.cs create mode 100644 Bots/AleixBot/ListingPicker.cs create mode 100644 Bots/AleixBot/Models/IntermediateListing.cs create mode 100644 Bots/AleixBot/Models/Period.cs create mode 100644 Bots/AleixBot/Models/WindowedSumPerListingPerDate.cs create mode 100644 Bots/AleixBot/Properties/launchSettings.json diff --git a/Bots/AleixBot.Tests/AleixBot.Tests.csproj b/Bots/AleixBot.Tests/AleixBot.Tests.csproj new file mode 100644 index 0000000..037d1e7 --- /dev/null +++ b/Bots/AleixBot.Tests/AleixBot.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + latest + enable + enable + false + + + + + + + + + + + + + + + + + + + diff --git a/Bots/AleixBot.Tests/ListingPickerTests.cs b/Bots/AleixBot.Tests/ListingPickerTests.cs new file mode 100644 index 0000000..40fc9b8 --- /dev/null +++ b/Bots/AleixBot.Tests/ListingPickerTests.cs @@ -0,0 +1,24 @@ +namespace AleixBot.Tests +{ + public class Tests + { + private ListingPicker _sut; + + [SetUp] + public void Setup() + { + + } + + //[Test] + //public void ListingPicker_CalculateBestListingForDate(DateOnly date) + //{ + // Assert.Pass(); + //} + + + //Initializes + //Returns best listing available for a specific date + + } +} diff --git a/Bots/AleixBot/AleixBot.cs b/Bots/AleixBot/AleixBot.cs index 28c230f..c543aa5 100644 --- a/Bots/AleixBot/AleixBot.cs +++ b/Bots/AleixBot/AleixBot.cs @@ -1,10 +1,15 @@ using NasdaqTrader.Bot.Core; +using System.Diagnostics; +using System.Diagnostics.Metrics; namespace AleixBot; public class AleixBot : ITraderBot { public string CompanyName => "Beginner Investments"; + public static ListingPicker _listingPicker = null; + private static int _counter = 0; + private const int WINDOWSIZE = 3; public async Task DoTurn(ITraderSystemContext systemContext) { @@ -13,6 +18,68 @@ public async Task DoTurn(ITraderSystemContext systemContext) var currentDate = systemContext.CurrentDate; var tradesLeft = systemContext.GetTradesLeftForToday(this); - systemContext.BuyStock(this, listings[0], 1); + if (_listingPicker == null) + { + InitializeListingPicker(listings, WINDOWSIZE); + } + + if (_counter % WINDOWSIZE == 0 || _counter == 0) + { + //TODO implement while loop for trading + if (tradesLeft <= 0) + { + _counter++; + return; + } + + if (systemContext.GetHoldings(this).Any() && tradesLeft > 0) + { + tradesLeft = Sell(systemContext, tradesLeft); + } + + cash = systemContext.GetCurrentCash(this); + + if (tradesLeft > 0 && cash > 0) + { + tradesLeft = Buy(systemContext, cash, currentDate, tradesLeft); + } + } + + _counter++; + } + + private int Buy(ITraderSystemContext systemContext, decimal cash, DateOnly currentDate, int tradesLeft) + { + var listing = _listingPicker.GetXBestListingForDate(1, currentDate, cash); + var pricePoint = listing?.PricePoints.FirstOrDefault(l => l.Date == currentDate); + + if (pricePoint is not null && cash >= pricePoint.Price) + { + systemContext.BuyStock(this, listing, (int)(cash / pricePoint.Price)); + tradesLeft--; + } + + return tradesLeft; + } + + private int Sell(ITraderSystemContext systemContext, int tradesLeft) + { + var holding = systemContext.GetHoldings(this).OrderByDescending(h => h.Amount).FirstOrDefault(); + if (holding != null) + { + systemContext.SellStock(this, holding.Listing, holding.Amount); + tradesLeft--; + } + + return tradesLeft; + } + + private static void InitializeListingPicker(System.Collections.ObjectModel.ReadOnlyCollection listings, int windowSize) + { + var sw = new Stopwatch(); + sw.Start(); + _listingPicker = new ListingPicker(listings, windowSize); + sw.Stop(); + Debug.WriteLine($"Building ListingPicker took {sw.ElapsedMilliseconds} milliseconds"); } } \ No newline at end of file diff --git a/Bots/AleixBot/AleixBot.sln b/Bots/AleixBot/AleixBot.sln index 9fb532a..8f2ed5d 100644 --- a/Bots/AleixBot/AleixBot.sln +++ b/Bots/AleixBot/AleixBot.sln @@ -1,10 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 -VisualStudioVersion = 17.12.35527.113 d17.12 +VisualStudioVersion = 17.12.35527.113 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AleixBot", "AleixBot.csproj", "{4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AleixBot.Tests", "..\AleixBot.Tests\AleixBot.Tests.csproj", "{716FB5B8-EB7F-4FC1-90C7-7FD2FAE3DBA4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +17,15 @@ Global {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Debug|Any CPU.Build.0 = Debug|Any CPU {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Release|Any CPU.ActiveCfg = Release|Any CPU {4EED0EB0-3BBC-41E5-A1F1-AB1C2A9DDB36}.Release|Any CPU.Build.0 = Release|Any CPU + {716FB5B8-EB7F-4FC1-90C7-7FD2FAE3DBA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {716FB5B8-EB7F-4FC1-90C7-7FD2FAE3DBA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {716FB5B8-EB7F-4FC1-90C7-7FD2FAE3DBA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {716FB5B8-EB7F-4FC1-90C7-7FD2FAE3DBA4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C00082EE-EABB-41B9-A550-6A1DEB725E5E} + EndGlobalSection EndGlobal diff --git a/Bots/AleixBot/ListingPicker.cs b/Bots/AleixBot/ListingPicker.cs new file mode 100644 index 0000000..fa8abf4 --- /dev/null +++ b/Bots/AleixBot/ListingPicker.cs @@ -0,0 +1,89 @@ +using AleixBot.Models; +using NasdaqTrader.Bot.Core; +using System.Collections.ObjectModel; + +namespace AleixBot; + +public class ListingPicker +{ + public ReadOnlyCollection Listings { get; } + public int WindowSize { get; } + public Collection IntermediateListings { get; } = []; + public List WindowedListingSumPerDate = []; + + private List _periods = []; + + public ListingPicker(ReadOnlyCollection listings, int windowSize) + { + Listings = listings; + WindowSize = windowSize; + ConstructIntermediateDictionary(); + } + + public void ConstructIntermediateDictionary() + { + + foreach (var listing in Listings) + { + for (int i = 0; i < listing.PricePoints.Length - 2; i++) + { + var date = listing.PricePoints[i].Date; + var priceDifference = listing.PricePoints[i + 1].Price - listing.PricePoints[i].Price; + IntermediateListings.Add(new IntermediateListing(listing.Name, date, priceDifference, listing.PricePoints[i].Price)); + } + } + + var allDates = IntermediateListings.Select(il => il.Date); + if (!allDates.Any()) return; + + DateOnly begindate = allDates.Min(); + DateOnly endDate = allDates.Max(); + + var groupedIntermediateListings = IntermediateListings + .GroupBy(il => il.Name) + .ToDictionary(g => g.Key, g => g.ToList()); + + DateOnly j = begindate; + while (j.AddDays(WindowSize) < endDate) + { + _periods.Add(new Period(j, j.AddDays(WindowSize))); + j = j.AddDays(WindowSize); + } + + foreach (var (name, listings) in groupedIntermediateListings) + { + foreach (var period in _periods) + { + // Filter once per group and period + var windowSum = listings + .Where(il => il.Date >= period.Start && il.Date <= period.End) + .Sum(s => s.PriceDifference); + + var priceOnStartDate = listings + .Where(il => il.Date == period.Start) + .Select(il => il.Price) + .FirstOrDefault(); + + WindowedListingSumPerDate.Add(new WindowedSumPerListingPerDate(period.Start, name, windowSum, priceOnStartDate)); + } + } + } + + public IStockListing? GetXBestListingForDate(int number, DateOnly date, decimal cash) + { + var periodForDate = _periods.Where(p => p.Start <= date && p.End >= date).FirstOrDefault(); + + if (periodForDate == null) + { + return null; + } + + var bestListingName = WindowedListingSumPerDate + .Where(wl => wl.Date == periodForDate.Start && wl.PriceOnStartDate < cash) + .OrderByDescending(wl => wl.Sum) + .Select(wl => wl.Name) + .FirstOrDefault(); + + return Listings.FirstOrDefault(l => l.Name.Equals(bestListingName)); + } +} \ No newline at end of file diff --git a/Bots/AleixBot/Models/IntermediateListing.cs b/Bots/AleixBot/Models/IntermediateListing.cs new file mode 100644 index 0000000..1321ee4 --- /dev/null +++ b/Bots/AleixBot/Models/IntermediateListing.cs @@ -0,0 +1,5 @@ +using NasdaqTrader.Bot.Core; + +namespace AleixBot.Models; + +public record IntermediateListing(string Name, DateOnly Date, decimal PriceDifference, decimal Price); \ No newline at end of file diff --git a/Bots/AleixBot/Models/Period.cs b/Bots/AleixBot/Models/Period.cs new file mode 100644 index 0000000..8cdfc81 --- /dev/null +++ b/Bots/AleixBot/Models/Period.cs @@ -0,0 +1,3 @@ +namespace AleixBot.Models; + +internal record Period(DateOnly Start, DateOnly End); \ No newline at end of file diff --git a/Bots/AleixBot/Models/WindowedSumPerListingPerDate.cs b/Bots/AleixBot/Models/WindowedSumPerListingPerDate.cs new file mode 100644 index 0000000..7538119 --- /dev/null +++ b/Bots/AleixBot/Models/WindowedSumPerListingPerDate.cs @@ -0,0 +1,3 @@ +namespace AleixBot.Models; + +public record WindowedSumPerListingPerDate(DateOnly Date, string Name, decimal Sum, decimal PriceOnStartDate); \ No newline at end of file diff --git a/Bots/AleixBot/Properties/launchSettings.json b/Bots/AleixBot/Properties/launchSettings.json new file mode 100644 index 0000000..452bd0f --- /dev/null +++ b/Bots/AleixBot/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "AleixBot": { + "commandName": "Project" + }, + "Run Game": { + "commandName": "Executable", + "executablePath": "C:\\Users\\AleixH\\Documents\\GitHub\\NasdaqTradeSystem\\Build\\NasdaqTrader.CLI.exe" + } + } +} \ No newline at end of file From 60fff409697bf1e72b2428c605e1daa89623e7db Mon Sep 17 00:00:00 2001 From: Lance-88 <129681882+Lance-88@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:52:21 +0200 Subject: [PATCH 6/6] Downgrade to .NET8 --- Bots/AleixBot.Tests/AleixBot.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bots/AleixBot.Tests/AleixBot.Tests.csproj b/Bots/AleixBot.Tests/AleixBot.Tests.csproj index 037d1e7..4d0eea2 100644 --- a/Bots/AleixBot.Tests/AleixBot.Tests.csproj +++ b/Bots/AleixBot.Tests/AleixBot.Tests.csproj @@ -1,7 +1,7 @@  - net9.0 + net8.0 latest enable enable