Update SDK (#35)

* Update SDK for swap

* Fix web3proxy build

* Update types

* Fix swap

* Send token test and BASE transfer

* fix cache and hook

* Fix send

* Update health check with uiFeereceiver

* Fix sdk

* Fix get positions

* Fix timeoutloop

* Fix open position

* Fix closes positions

* Review
This commit is contained in:
Oda
2025-09-17 14:28:56 +07:00
committed by GitHub
parent 271dd70ad7
commit cee3902a4d
91 changed files with 21375 additions and 2831 deletions

View File

@@ -133,7 +133,8 @@ public class TradingBotBase : ITradingBot
{
// Add a small delay to ensure grain is fully activated
await Task.Delay(100);
LastCandle = await grain.GetLastCandle();
var lastCandles = await grain.GetLastCandle(1);
LastCandle = lastCandles.FirstOrDefault();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid activation"))
{
@@ -143,7 +144,8 @@ public class TradingBotBase : ITradingBot
await Task.Delay(1000);
try
{
LastCandle = await grain.GetLastCandle();
var lastCandles = await grain.GetLastCandle(1);
LastCandle = lastCandles.FirstOrDefault();
}
catch (Exception retryEx)
{
@@ -1059,13 +1061,30 @@ public class TradingBotBase : ITradingBot
if (currentCandle != null)
{
List<Candle> recentCandles = null;
await ServiceScopeHelpers.WithScopedService<IExchangeService>(_scopeFactory, async exchangeService =>
if (Config.IsForBacktest)
{
recentCandles = Config.IsForBacktest
? (LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>())
: (await exchangeService.GetCandlesInflux(TradingExchanges.Evm, Config.Ticker,
DateTime.UtcNow.AddHours(-4), Config.Timeframe)).ToList();
});
recentCandles = LastCandle != null ? new List<Candle>() { LastCandle } : new List<Candle>();
}
else
{
// Use CandleStoreGrain to get recent candles instead of calling exchange service directly
await ServiceScopeHelpers.WithScopedService<IGrainFactory>(_scopeFactory, async grainFactory =>
{
var grainKey = CandleHelpers.GetCandleStoreGrainKey(Account.Exchange, Config.Ticker, Config.Timeframe);
var grain = grainFactory.GetGrain<ICandleStoreGrain>(grainKey);
try
{
recentCandles = await grain.GetLastCandle(5);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error retrieving recent candles from CandleStoreGrain for {GrainKey}", grainKey);
recentCandles = new List<Candle>();
}
});
}
// Check if we have any candles before proceeding
if (recentCandles == null || !recentCandles.Any())
@@ -1216,7 +1235,8 @@ public class TradingBotBase : ITradingBot
if (!Config.IsForBacktest)
{
await ServiceScopeHelpers.WithScopedService<IMessengerService>(_scopeFactory,
async messengerService => { await messengerService.SendClosingPosition(position); });
messengerService => { messengerService.SendClosingPosition(position);
return Task.CompletedTask; });
}
await CancelAllOrders();

View File

@@ -232,7 +232,7 @@ public class CandleStoreGrain : Grain, ICandleStoreGrain, IAsyncObserver<Candle>
}
}
public Task<Candle> GetLastCandle()
public Task<List<Candle>> GetLastCandle(int count = 1)
{
try
{
@@ -240,15 +240,33 @@ public class CandleStoreGrain : Grain, ICandleStoreGrain, IAsyncObserver<Candle>
if (_state.State.Candles == null || _state.State.Candles.Count == 0)
{
_logger.LogDebug("No candles available for grain {GrainKey}", this.GetPrimaryKeyString());
return Task.FromResult<Candle>(null);
return Task.FromResult(new List<Candle>());
}
return Task.FromResult(_state.State.Candles.LastOrDefault());
// Validate count parameter
if (count <= 0)
{
_logger.LogWarning("Invalid count parameter {Count} for grain {GrainKey}, using default value 1",
count, this.GetPrimaryKeyString());
count = 1;
}
// Get the last X candles, ordered by date
var lastCandles = _state.State.Candles
.OrderBy(c => c.Date)
.TakeLast(count)
.ToList();
_logger.LogDebug("Retrieved {Count} latest candles for grain {GrainKey}",
lastCandles.Count, this.GetPrimaryKeyString());
return Task.FromResult(lastCandles);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving last candle for grain {GrainKey}", this.GetPrimaryKeyString());
return Task.FromResult<Candle>(null);
_logger.LogError(ex, "Error retrieving last {Count} candles for grain {GrainKey}",
count, this.GetPrimaryKeyString());
return Task.FromResult(new List<Candle>());
}
}
}

View File

@@ -25,9 +25,21 @@ public class MessengerService : IMessengerService
await _discordService.SendClosedPosition(address, oldTrade);
}
public async Task SendClosingPosition(Position position)
public void SendClosingPosition(Position position)
{
await _discordService.SendClosingPosition(position);
// Fire-and-forget: Send closing position notification without blocking the thread
_ = Task.Run(async () =>
{
try
{
await _discordService.SendClosingPosition(position);
}
catch (Exception ex)
{
// Log the exception but don't let it affect the main thread
Console.WriteLine($"Failed to send closing position notification: {ex.Message}");
}
});
}
public async Task SendIncreasePosition(string address, Trade trade, string copyAccountName, Trade? oldTrade = null)

View File

@@ -1,4 +1,5 @@
using Managing.Domain.Trades;
using Managing.Common;
using Managing.Domain.Trades;
using Managing.Domain.Users;
using MediatR;
using static Managing.Common.Enums;
@@ -29,7 +30,7 @@ namespace Managing.Application.Trading.Commands
Date = date;
User = user;
if (amountToTrade <= 10)
if (amountToTrade <= Constants.GMX.Config.MinimumPositionAmount)
{
throw new ArgumentException("Bot trading balance must be greater than zero", nameof(amountToTrade));
}
@@ -39,7 +40,9 @@ namespace Managing.Application.Trading.Commands
IsForPaperTrading = isForPaperTrading;
Price = price;
SignalIdentifier = signalIdentifier;
InitiatorIdentifier = initiatorIdentifier ?? throw new ArgumentNullException(nameof(initiatorIdentifier), "InitiatorIdentifier is required");
InitiatorIdentifier = initiatorIdentifier ??
throw new ArgumentNullException(nameof(initiatorIdentifier),
"InitiatorIdentifier is required");
}
public string SignalIdentifier { get; set; }