From 687aa5d578068ba1491619af931ea42dd9ca53fa Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:03:57 +0300 Subject: [PATCH 1/3] Initial commit with task details for issue #222 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Bot/issues/222 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..db5d94ca --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Bot/issues/222 +Your prepared branch: issue-222-f9494c2f +Your prepared working directory: /tmp/gh-issue-solver-1757581434679 + +Proceed. \ No newline at end of file From 2a90b446664bf0b76b4693ba10e07af2698fe80d Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:04:14 +0300 Subject: [PATCH 2/3] Remove CLAUDE.md - PR created successfully --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index db5d94ca..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Bot/issues/222 -Your prepared branch: issue-222-f9494c2f -Your prepared working directory: /tmp/gh-issue-solver-1757581434679 - -Proceed. \ No newline at end of file From f27cbc568816634156602980dadbd571a6f034ac Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 11 Sep 2025 12:08:39 +0300 Subject: [PATCH 3/3] Add custom buy price functionality to avoid queue waiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add EnableCustomBuyPrice configuration to enable/disable feature - Add CustomBuyPriceSpreadPercentage to control how much of spread to cross - Add MaxCustomBuyPriceSteps to limit maximum price increase from best bid - Update buy order logic to use custom price calculation - Maintain backward compatibility when feature is disabled - Add comprehensive logging for debugging price calculations This allows traders to buy at their own price (e.g., 5.320 instead of 5.300) to avoid waiting in long queues at the best bid price. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- csharp/TraderBot/TradingService.cs | 45 +++++++++++++++++++----- csharp/TraderBot/TradingSettings.cs | 3 ++ csharp/TraderBot/appsettings.TMON.json | 5 ++- csharp/TraderBot/appsettings.TRUR.json | 5 ++- examples/custom_buy_price_test.md | 47 ++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 11 deletions(-) create mode 100644 examples/custom_buy_price_test.md diff --git a/csharp/TraderBot/TradingService.cs b/csharp/TraderBot/TradingService.cs index 0302809b..b8097c00 100644 --- a/csharp/TraderBot/TradingService.cs +++ b/csharp/TraderBot/TradingService.cs @@ -63,6 +63,9 @@ public TradingService(ILogger logger, InvestApiClient investApi, Logger.LogInformation($"EarlySellOwnedLotsDelta: {settings.EarlySellOwnedLotsDelta}"); Logger.LogInformation($"EarlySellOwnedLotsMultiplier: {settings.EarlySellOwnedLotsMultiplier}"); Logger.LogInformation($"LoadOperationsFrom: {settings.LoadOperationsFrom}"); + Logger.LogInformation($"EnableCustomBuyPrice: {settings.EnableCustomBuyPrice}"); + Logger.LogInformation($"CustomBuyPriceSpreadPercentage: {settings.CustomBuyPriceSpreadPercentage}"); + Logger.LogInformation($"MaxCustomBuyPriceSteps: {settings.MaxCustomBuyPriceSteps}"); var currentTime = DateTime.UtcNow.TimeOfDay; Logger.LogInformation($"Current time: {currentTime}"); @@ -472,15 +475,16 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest { // Process potential buy order var (cashBalance, _) = await GetCashBalance(); - var lotPrice = bestBid * LotSize; + var customBuyPrice = GetCustomBuyPrice(bestBid, bestAsk); + var lotPrice = customBuyPrice * LotSize; if (cashBalance > lotPrice) { Logger.LogInformation($"buy activated"); - Logger.LogInformation($"bid: {bestBid}, ask: {bestAsk}."); + Logger.LogInformation($"bid: {bestBid}, ask: {bestAsk}, customBuyPrice: {customBuyPrice}."); var lots = (long)(cashBalance / lotPrice); - var marketLotsAtTargetPrice = orderBook.Bids.FirstOrDefault(o => o.Price == bestBid)?.Quantity ?? 0; + var marketLotsAtTargetPrice = orderBook.Bids.FirstOrDefault(o => o.Price == customBuyPrice)?.Quantity ?? 0; Logger.LogInformation($"marketLotsAtTargetPrice: {marketLotsAtTargetPrice}"); - var response = await PlaceBuyOrder(lots, bestBid); + var response = await PlaceBuyOrder(lots, customBuyPrice); Logger.LogInformation($"buy complete"); areOrdersPlaced = true; } @@ -519,16 +523,17 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest if (IsTimeToBuy()) { var initialOrderPrice = MoneyValueToDecimal(activeBuyOrder.InitialSecurityPrice); + var customBuyPrice = GetCustomBuyPrice(bestBid, bestAsk); if (LotsSets.TryGetValue(initialOrderPrice, out var boughtLots) || LotsSets.Count == 0) { - if (initialOrderPrice != bestBid && bestBidOrder.Quantity > Settings.MinimumMarketOrderSizeToChangeBuyPrice) + if (initialOrderPrice != customBuyPrice && bestBidOrder.Quantity > Settings.MinimumMarketOrderSizeToChangeBuyPrice) { if (boughtLots > 0) { Logger.LogInformation($"buy trades are in progress"); continue; } - Logger.LogInformation($"bid: {bestBid}, ask: {bestAsk}."); + Logger.LogInformation($"bid: {bestBid}, ask: {bestAsk}, customBuyPrice: {customBuyPrice}."); Logger.LogInformation($"initial buy order price: {initialOrderPrice}"); Logger.LogInformation($"buy order price change activated"); // Cancel order @@ -541,13 +546,13 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest SetCashBalance(CashBalanceFree + CashBalanceLocked, 0); // Place new order var (cashBalance, _) = await GetCashBalance(); - var lotPrice = bestBid * LotSize; + var lotPrice = customBuyPrice * LotSize; if (cashBalance > lotPrice) { var lots = (long)(cashBalance / lotPrice); - var marketLotsAtTargetPrice = orderBook.Bids.FirstOrDefault(o => o.Price == bestBid)?.Quantity ?? 0; + var marketLotsAtTargetPrice = orderBook.Bids.FirstOrDefault(o => o.Price == customBuyPrice)?.Quantity ?? 0; Logger.LogInformation($"marketLotsAtTargetPrice: {marketLotsAtTargetPrice}"); - var response = await PlaceBuyOrder(lots, bestBid); + var response = await PlaceBuyOrder(lots, customBuyPrice); } SyncActiveOrders(); Logger.LogInformation($"buy order price change is complete"); @@ -691,6 +696,28 @@ private decimal GetTargetSellPrice(decimal minimumSellPrice, decimal bestAsk) return targetSellPrice; } + private decimal GetCustomBuyPrice(decimal bestBid, decimal bestAsk) + { + if (!Settings.EnableCustomBuyPrice) + { + return bestBid; + } + + var spread = bestAsk - bestBid; + var customBuyPrice = bestBid + (spread * Settings.CustomBuyPriceSpreadPercentage / 100m); + + var maxPriceIncrease = Settings.MaxCustomBuyPriceSteps * PriceStep; + var maxAllowedPrice = bestBid + maxPriceIncrease; + + customBuyPrice = Math.Min(customBuyPrice, maxAllowedPrice); + customBuyPrice = Math.Min(customBuyPrice, bestAsk); + + customBuyPrice = Math.Max(customBuyPrice, bestBid); + + Logger.LogInformation($"CustomBuyPrice calculation: bestBid={bestBid}, bestAsk={bestAsk}, spread={spread}, customBuyPrice={customBuyPrice}"); + return customBuyPrice; + } + protected override async Task ExecuteAsync(CancellationToken cancellationToken) { var tasks = new [] diff --git a/csharp/TraderBot/TradingSettings.cs b/csharp/TraderBot/TradingSettings.cs index 884a25df..d17fdc3b 100644 --- a/csharp/TraderBot/TradingSettings.cs +++ b/csharp/TraderBot/TradingSettings.cs @@ -17,4 +17,7 @@ public class TradingSettings public long EarlySellOwnedLotsDelta { get; set; } public decimal EarlySellOwnedLotsMultiplier { get; set; } public DateTime LoadOperationsFrom { get; set; } + public bool EnableCustomBuyPrice { get; set; } + public decimal CustomBuyPriceSpreadPercentage { get; set; } + public long MaxCustomBuyPriceSteps { get; set; } } \ No newline at end of file diff --git a/csharp/TraderBot/appsettings.TMON.json b/csharp/TraderBot/appsettings.TMON.json index c7b66d7a..e5d8e443 100644 --- a/csharp/TraderBot/appsettings.TMON.json +++ b/csharp/TraderBot/appsettings.TMON.json @@ -24,6 +24,9 @@ "MaximumTimeToBuy": "23:59:59", "EarlySellOwnedLotsDelta": 300000, "EarlySellOwnedLotsMultiplier": 0, - "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z" + "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z", + "EnableCustomBuyPrice": false, + "CustomBuyPriceSpreadPercentage": 50.0, + "MaxCustomBuyPriceSteps": 10 } } diff --git a/csharp/TraderBot/appsettings.TRUR.json b/csharp/TraderBot/appsettings.TRUR.json index 1dc848e6..74e4cfc4 100644 --- a/csharp/TraderBot/appsettings.TRUR.json +++ b/csharp/TraderBot/appsettings.TRUR.json @@ -24,6 +24,9 @@ "MaximumTimeToBuy": "14:45:00", "EarlySellOwnedLotsDelta": 300000, "EarlySellOwnedLotsMultiplier": 0, - "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z" + "LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z", + "EnableCustomBuyPrice": false, + "CustomBuyPriceSpreadPercentage": 50.0, + "MaxCustomBuyPriceSteps": 10 } } diff --git a/examples/custom_buy_price_test.md b/examples/custom_buy_price_test.md new file mode 100644 index 00000000..36ee875a --- /dev/null +++ b/examples/custom_buy_price_test.md @@ -0,0 +1,47 @@ +# Custom Buy Price Test Cases + +## Test Scenario 1: Feature Disabled +- `EnableCustomBuyPrice`: false +- `bestBid`: 5.300 +- `bestAsk`: 5.320 +- **Expected Result**: 5.300 (should return bestBid) + +## Test Scenario 2: Feature Enabled - 50% Spread +- `EnableCustomBuyPrice`: true +- `CustomBuyPriceSpreadPercentage`: 50.0 +- `MaxCustomBuyPriceSteps`: 10 +- `PriceStep`: 0.001 +- `bestBid`: 5.300 +- `bestAsk`: 5.320 +- **Spread**: 0.020 +- **50% of spread**: 0.010 +- **Expected Result**: 5.310 (bestBid + 50% of spread) + +## Test Scenario 3: Feature Enabled - Limited by MaxSteps +- `EnableCustomBuyPrice`: true +- `CustomBuyPriceSpreadPercentage`: 50.0 +- `MaxCustomBuyPriceSteps`: 5 +- `PriceStep`: 0.001 +- `bestBid`: 5.300 +- `bestAsk`: 5.350 +- **Spread**: 0.050 +- **50% of spread**: 0.025 +- **Max allowed increase**: 5 * 0.001 = 0.005 +- **Expected Result**: 5.305 (bestBid + maxSteps, capped) + +## Test Scenario 4: Feature Enabled - Limited by bestAsk +- `EnableCustomBuyPrice`: true +- `CustomBuyPriceSpreadPercentage`: 100.0 +- `MaxCustomBuyPriceSteps`: 100 +- `PriceStep`: 0.001 +- `bestBid`: 5.300 +- `bestAsk`: 5.310 +- **Spread**: 0.010 +- **100% of spread**: 0.010 +- **Expected Result**: 5.310 (limited by bestAsk) + +This feature allows traders to: +1. Avoid long queues at the best bid price +2. Get faster execution by paying a premium (crossing the spread partially) +3. Control the maximum premium they're willing to pay +4. Maintain the existing behavior when disabled \ No newline at end of file