Skip to content

Commit e92e804

Browse files
konardclaude
andcommitted
Implement buy prevention logic for trader bot
- Add MaxSpreadPercentToBuy and MinimumCombinedLiquidityToBuy settings - Add IsMarketConditionsSuitableForBuying method to check market conditions - Prevent buying when bid-ask spread is too wide (configurable threshold) - Prevent buying when market liquidity is too low (configurable threshold) - Apply conditions to both initial buy orders and buy order price changes - Update configuration files with sensible default values (1.0% max spread, 1M minimum liquidity) Fixes #224 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 34366c7 commit e92e804

File tree

4 files changed

+37
-5
lines changed

4 files changed

+37
-5
lines changed

csharp/TraderBot/TradingService.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ public TradingService(ILogger<TradingService> logger, InvestApiClient investApi,
6363
Logger.LogInformation($"EarlySellOwnedLotsDelta: {settings.EarlySellOwnedLotsDelta}");
6464
Logger.LogInformation($"EarlySellOwnedLotsMultiplier: {settings.EarlySellOwnedLotsMultiplier}");
6565
Logger.LogInformation($"LoadOperationsFrom: {settings.LoadOperationsFrom}");
66+
Logger.LogInformation($"MaxSpreadPercentToBuy: {settings.MaxSpreadPercentToBuy}");
67+
Logger.LogInformation($"MinimumCombinedLiquidityToBuy: {settings.MinimumCombinedLiquidityToBuy}");
6668

6769
var currentTime = DateTime.UtcNow.TimeOfDay;
6870
Logger.LogInformation($"Current time: {currentTime}");
@@ -468,7 +470,7 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest
468470
}
469471
if (!areOrdersPlaced)
470472
{
471-
if (IsTimeToBuy())
473+
if (IsTimeToBuy() && IsMarketConditionsSuitableForBuying(bestBid, bestAsk, orderBook))
472474
{
473475
// Process potential buy order
474476
var (cashBalance, _) = await GetCashBalance();
@@ -516,7 +518,7 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest
516518
else if (ActiveBuyOrders.Count == 1)
517519
{
518520
var activeBuyOrder = ActiveBuyOrders.Single().Value;
519-
if (IsTimeToBuy())
521+
if (IsTimeToBuy() && IsMarketConditionsSuitableForBuying(bestBid, bestAsk, orderBook))
520522
{
521523
var initialOrderPrice = MoneyValueToDecimal(activeBuyOrder.InitialSecurityPrice);
522524
if (LotsSets.TryGetValue(initialOrderPrice, out var boughtLots) || LotsSets.Count == 0)
@@ -569,7 +571,7 @@ await marketDataStream.RequestStream.WriteAsync(new MarketDataRequest
569571
}
570572
else
571573
{
572-
Logger.LogInformation($"It is not time to buy, cancelling buy order");
574+
Logger.LogInformation($"Conditions not suitable for buying, cancelling buy order");
573575
// Cancel order
574576
if (!await TryCancelOrder(activeBuyOrder.OrderId))
575577
{
@@ -652,6 +654,30 @@ private bool IsTimeToBuy()
652654
{
653655
var currentTime = DateTime.UtcNow.TimeOfDay;
654656
return currentTime > MinimumTimeToBuy && currentTime < MaximumTimeToBuy;
657+
}
658+
659+
private bool IsMarketConditionsSuitableForBuying(decimal bestBid, decimal bestAsk, OrderBook orderBook)
660+
{
661+
// Check if spread is not too wide
662+
var spread = bestAsk - bestBid;
663+
var spreadPercent = (spread / bestBid) * 100;
664+
if (spreadPercent > Settings.MaxSpreadPercentToBuy)
665+
{
666+
Logger.LogInformation($"Spread too wide for buying: {spreadPercent:F2}% (max allowed: {Settings.MaxSpreadPercentToBuy}%)");
667+
return false;
668+
}
669+
670+
// Check combined liquidity at best bid and ask
671+
var bestBidOrder = orderBook.Bids.FirstOrDefault();
672+
var bestAskOrder = orderBook.Asks.FirstOrDefault();
673+
var combinedLiquidity = (bestBidOrder?.Quantity ?? 0) + (bestAskOrder?.Quantity ?? 0);
674+
if (combinedLiquidity < Settings.MinimumCombinedLiquidityToBuy)
675+
{
676+
Logger.LogInformation($"Insufficient liquidity for buying: {combinedLiquidity} (minimum required: {Settings.MinimumCombinedLiquidityToBuy})");
677+
return false;
678+
}
679+
680+
return true;
655681
}
656682

657683
private async Task<(decimal, decimal)> GetCashBalance(bool forceRemote = false)

csharp/TraderBot/TradingSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ public class TradingSettings
1717
public long EarlySellOwnedLotsDelta { get; set; }
1818
public decimal EarlySellOwnedLotsMultiplier { get; set; }
1919
public DateTime LoadOperationsFrom { get; set; }
20+
public decimal MaxSpreadPercentToBuy { get; set; }
21+
public long MinimumCombinedLiquidityToBuy { get; set; }
2022
}

csharp/TraderBot/appsettings.TMON.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"MaximumTimeToBuy": "23:59:59",
2525
"EarlySellOwnedLotsDelta": 300000,
2626
"EarlySellOwnedLotsMultiplier": 0,
27-
"LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z"
27+
"LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z",
28+
"MaxSpreadPercentToBuy": 1.0,
29+
"MinimumCombinedLiquidityToBuy": 1000000
2830
}
2931
}

csharp/TraderBot/appsettings.TRUR.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"MaximumTimeToBuy": "14:45:00",
2525
"EarlySellOwnedLotsDelta": 300000,
2626
"EarlySellOwnedLotsMultiplier": 0,
27-
"LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z"
27+
"LoadOperationsFrom": "2025-03-01T00:00:01.3389860Z",
28+
"MaxSpreadPercentToBuy": 1.0,
29+
"MinimumCombinedLiquidityToBuy": 1000000
2830
}
2931
}

0 commit comments

Comments
 (0)