Fix worker cancelled on worker + Cache tickers

This commit is contained in:
2025-04-30 13:19:03 +07:00
parent df4392b17e
commit bc1ef78747
11 changed files with 116 additions and 55 deletions

View File

@@ -249,24 +249,6 @@ app.UseEndpoints(endpoints =>
Predicate = r => r.Tags.Contains("live"), Predicate = r => r.Tags.Contains("live"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
}); });
endpoints.MapHealthChecks("/health/candles", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("candles"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/health/gmx", new HealthCheckOptions
{
Predicate = r => r.Name == "gmx-connectivity",
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecks("/health/web3proxy", new HealthCheckOptions
{
Predicate = r => r.Name == "web3proxy",
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
}); });
app.Run(); app.Run();

View File

@@ -20,7 +20,7 @@ public interface IEvmManager
Task<List<EvmBalance>> GetAllBalancesOnAllChain(string publicAddress); Task<List<EvmBalance>> GetAllBalancesOnAllChain(string publicAddress);
Task<List<Candle>> GetCandles(Ticker ticker, DateTime startDate, Task<List<Candle>> GetCandles(Ticker ticker, DateTime startDate,
Timeframe interval); Timeframe interval, bool isFirstCall = false);
decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker); decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker);
Task<List<Ticker>> GetAvailableTicker(); Task<List<Ticker>> GetAvailableTicker();

View File

@@ -27,7 +27,7 @@ public interface IExchangeService
Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false); Task<List<Balance>> GetBalances(Account account, bool isForPaperTrading = false);
decimal GetPrice(Account account, Ticker ticker, DateTime date); decimal GetPrice(Account account, Ticker ticker, DateTime date);
Task<Trade> GetTrade(Account account, string order, Ticker ticker); Task<Trade> GetTrade(Account account, string order, Ticker ticker);
Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval); Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval, bool isFirstCall);
Task<Trade> OpenStopLoss(Account account, Ticker ticker, TradeDirection originalDirection, decimal stopLossPrice, Task<Trade> OpenStopLoss(Account account, Ticker ticker, TradeDirection originalDirection, decimal stopLossPrice,
decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null); decimal quantity, bool isForPaperTrading = false, DateTime? currentDate = null);

View File

@@ -16,4 +16,10 @@
<ProjectReference Include="..\Managing.Application.Abstractions\Managing.Application.Abstractions.csproj"/> <ProjectReference Include="..\Managing.Application.Abstractions\Managing.Application.Abstractions.csproj"/>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Reference Include="Managing.Application">
<HintPath>..\Managing.Api\bin\Debug\net8.0\Managing.Application.dll</HintPath>
</Reference>
</ItemGroup>
</Project> </Project>

View File

@@ -1,6 +1,7 @@
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Application.Workers.Abstractions; using Managing.Application.Workers.Abstractions;
using Managing.Domain.Candles;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -38,7 +39,16 @@ public class PricesService : IPricesService
await _candleRepository.GetCandles(exchange, ticker, timeframe, DateTime.UtcNow.AddDays(-2)); await _candleRepository.GetCandles(exchange, ticker, timeframe, DateTime.UtcNow.AddDays(-2));
var lastCandle = lastCandles.LastOrDefault(); var lastCandle = lastCandles.LastOrDefault();
var startDate = lastCandle != null ? lastCandle.Date : new DateTime(2017, 1, 1); var startDate = lastCandle != null ? lastCandle.Date : new DateTime(2017, 1, 1);
var newCandles = await _exchangeService.GetCandles(account, ticker, startDate, timeframe);
List<Candle> newCandles;
if (!lastCandles.Any())
{
newCandles = await _exchangeService.GetCandles(account, ticker, startDate, timeframe, true);
}
else
{
newCandles = await _exchangeService.GetCandles(account, ticker, startDate, timeframe, false);
}
var candles = !lastCandles.Any() ? newCandles : newCandles.Where(c => c.Date > lastCandle?.Date); var candles = !lastCandles.Any() ? newCandles : newCandles.Where(c => c.Date > lastCandle?.Date);
var candlesInserted = 0; var candlesInserted = 0;

View File

@@ -1,4 +1,5 @@
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Application.Workers.Abstractions; using Managing.Application.Workers.Abstractions;
using Managing.Domain.Accounts; using Managing.Domain.Accounts;
@@ -23,6 +24,7 @@ public class StatisticService : IStatisticService
private readonly IBacktester _backtester; private readonly IBacktester _backtester;
private readonly ITradaoService _tradaoService; private readonly ITradaoService _tradaoService;
private readonly IMessengerService _messengerService; private readonly IMessengerService _messengerService;
private readonly ICacheService _cacheService;
private readonly ILogger<StatisticService> _logger; private readonly ILogger<StatisticService> _logger;
public StatisticService( public StatisticService(
@@ -34,7 +36,8 @@ public class StatisticService : IStatisticService
ITradingService tradingService, ITradingService tradingService,
IBacktester backtester, IBacktester backtester,
ITradaoService tradaoService, ITradaoService tradaoService,
IMessengerService messengerService) IMessengerService messengerService,
ICacheService cacheService)
{ {
_exchangeService = exchangeService; _exchangeService = exchangeService;
_accountService = accountService; _accountService = accountService;
@@ -45,6 +48,7 @@ public class StatisticService : IStatisticService
_backtester = backtester; _backtester = backtester;
_tradaoService = tradaoService; _tradaoService = tradaoService;
_messengerService = messengerService; _messengerService = messengerService;
_cacheService = cacheService;
} }
public async Task UpdateTopVolumeTicker(TradingExchanges exchange, int top) public async Task UpdateTopVolumeTicker(TradingExchanges exchange, int top)
@@ -159,7 +163,16 @@ public class StatisticService : IStatisticService
public async Task<IList<Ticker>> GetTickers() public async Task<IList<Ticker>> GetTickers()
{ {
return await _evmManager.GetAvailableTicker(); var cachedTickers = _cacheService.GetValue<List<Ticker>>("tickers");
if (cachedTickers != null)
{
return cachedTickers;
}
var tickers = await _evmManager.GetAvailableTicker();
_cacheService.SaveValue("tickers", tickers, TimeSpan.FromDays(1));
return tickers;
} }
public async Task UpdateSpotlight() public async Task UpdateSpotlight()

View File

@@ -5,6 +5,7 @@ using Managing.Domain.Candles;
using Managing.Domain.Statistics; using Managing.Domain.Statistics;
using Managing.Domain.Trades; using Managing.Domain.Trades;
using Managing.Infrastructure.Exchanges.Abstractions; using Managing.Infrastructure.Exchanges.Abstractions;
using Managing.Infrastructure.Exchanges.Exchanges;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static Managing.Common.Enums; using static Managing.Common.Enums;
@@ -199,10 +200,15 @@ namespace Managing.Infrastructure.Exchanges
return await processor.GetTrades(account, ticker); return await processor.GetTrades(account, ticker);
} }
public async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, public async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe timeframe, bool isFirstCall)
Timeframe timeframe)
{ {
var processor = GetProcessor(account); var processor = GetProcessor(account);
// Only EvmProcessor supports isFirstCall
if (processor is EvmProcessor evmProcessor)
{
return await evmProcessor.GetCandles(account, ticker, startDate, timeframe, isFirstCall);
}
// Fallback to default behavior for other processors
return await processor.GetCandles(account, ticker, startDate, timeframe); return await processor.GetCandles(account, ticker, startDate, timeframe);
} }

View File

@@ -73,6 +73,11 @@ public class EvmProcessor : BaseProcessor
return await _evmManager.GetCandles(ticker, startDate, interval); return await _evmManager.GetCandles(ticker, startDate, interval);
} }
public async Task<List<Candle>> GetCandles(Account account, Ticker ticker, DateTime startDate, Timeframe interval, bool isFirstCall)
{
return await _evmManager.GetCandles(ticker, startDate, interval, isFirstCall);
}
public override decimal GetFee(Account account, bool isForPaperTrading = false) public override decimal GetFee(Account account, bool isForPaperTrading = false)
{ {
return _evmManager.GetFee(Constants.Chains.Arbitrum).Result; return _evmManager.GetFee(Constants.Chains.Arbitrum).Result;

View File

@@ -336,20 +336,43 @@ public class EvmManager : IEvmManager
return chainBalances; return chainBalances;
} }
public async Task<List<Candle>> GetCandles(Ticker ticker, DateTime startDate, public async Task<List<Candle>> GetCandles(Ticker ticker, DateTime startDate, Timeframe timeframe,
Timeframe timeframe) bool isFirstCall = false)
{ {
string gmxTimeframe = GmxHelpers.GeTimeframe(timeframe); string gmxTimeframe = GmxHelpers.GeTimeframe(timeframe);
var gmxPrices = await _httpClient.GetFromJsonAsync<GmxV2Prices>( int limit = isFirstCall ? 10000 : CalculateCandleLimit(startDate, timeframe);
$"https://arbitrum-api.gmxinfra.io/prices/candles?tokenSymbol={ticker}&period={gmxTimeframe}&limit=10000");
GmxV2Prices? gmxPrices = null;
int maxRetries = 3;
int delayMs = 1000;
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
gmxPrices = await _httpClient.GetFromJsonAsync<GmxV2Prices>(
$"https://arbitrum-api.gmxinfra.io/prices/candles?tokenSymbol={ticker}&period={gmxTimeframe}&limit={limit}");
break;
}
catch (HttpRequestException ex) when (ex.InnerException is IOException)
{
Console.Error.WriteLine($"Attempt {attempt}: Network error while fetching candles: {ex.Message}");
if (attempt == maxRetries)
throw;
await Task.Delay(delayMs * attempt);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Unexpected error: {ex.Message}");
throw;
}
}
if (gmxPrices == null) if (gmxPrices == null)
return null; return null;
var filteredCandles = gmxPrices.Candles.Where(p => p[0] >= startDate.ToUnixTimestamp()).ToList(); var filteredCandles = gmxPrices.Candles.Where(p => p[0] >= startDate.ToUnixTimestamp()).ToList();
var candles = new List<Candle>(); var candles = new List<Candle>();
var timeBetweenCandles = var timeBetweenCandles =
gmxPrices.Candles.Count > 2 ? gmxPrices.Candles[0][0] - gmxPrices.Candles[1][0] : 900; // Default 15 minutes gmxPrices.Candles.Count > 2 ? gmxPrices.Candles[0][0] - gmxPrices.Candles[1][0] : 900; // Default 15 minutes
@@ -362,6 +385,25 @@ public class EvmManager : IEvmManager
return candles.OrderBy(c => c.Date).ToList(); return candles.OrderBy(c => c.Date).ToList();
} }
private int CalculateCandleLimit(DateTime startDate, Timeframe timeframe)
{
var now = DateTime.UtcNow;
var minutesPerCandle = timeframe switch
{
Timeframe.OneMinute => 1,
Timeframe.FiveMinutes => 5,
Timeframe.FifteenMinutes => 15,
Timeframe.ThirtyMinutes => 30,
Timeframe.OneHour => 60,
Timeframe.FourHour => 240,
Timeframe.OneDay => 1440,
_ => 15
};
var totalMinutes = (now - startDate).TotalMinutes;
var candlesNeeded = (int)Math.Ceiling(totalMinutes / minutesPerCandle);
return Math.Min(candlesNeeded + 5, 10000);
}
public decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker) public decimal GetVolume(SubgraphProvider subgraphProvider, Ticker ticker)
{ {
var subgraph = GetSubgraph(subgraphProvider); var subgraph = GetSubgraph(subgraphProvider);
@@ -850,4 +892,10 @@ public class EvmManager : IEvmManager
await _web3ProxyService.CallPrivyServiceAsync<PrivySigningResponse>("sign-message", requestBody); await _web3ProxyService.CallPrivyServiceAsync<PrivySigningResponse>("sign-message", requestBody);
return response.Signature; return response.Signature;
} }
// Overload to match IEvmManager interface
public async Task<List<Candle>> GetCandles(Ticker ticker, DateTime startDate, Timeframe timeframe)
{
return await GetCandles(ticker, startDate, timeframe, false);
}
} }

View File

@@ -139,7 +139,7 @@ internal static class GmxV2Mappers
} }
catch (Exception e) catch (Exception e)
{ {
Console.WriteLine($"Could not parse ticker for symbol {t.Symbol}: {e.Message}"); //Console.WriteLine($"Could not parse ticker for symbol {t.Symbol}: {e.Message}");
} }
} }

View File

@@ -196,7 +196,6 @@ export const openGmxPositionImpl = async (
const marketInfo = getMarketInfoFromTicker(ticker, marketsInfoData); const marketInfo = getMarketInfoFromTicker(ticker, marketsInfoData);
const collateralToken = getTokenDataFromTicker("USDC", tokensData); // Using USDC as collateral const collateralToken = getTokenDataFromTicker("USDC", tokensData); // Using USDC as collateral
console.log('collateralToken', collateralToken)
// Calculate the collateral amount in USDC (quantity * price) // Calculate the collateral amount in USDC (quantity * price)
const collateralAmount = BigInt(Math.floor((quantity || 0) * (price || 0) * 1e6)); // USDC has 6 decimals const collateralAmount = BigInt(Math.floor((quantity || 0) * (price || 0) * 1e6)); // USDC has 6 decimals
@@ -325,8 +324,6 @@ export const cancelGmxOrdersImpl = async (
tokensData tokensData
}); });
console.log('ordersData', ordersData)
// Extract order keys for the specified ticker // Extract order keys for the specified ticker
const orderKeys = Object.values(ordersData.ordersInfoData) const orderKeys = Object.values(ordersData.ordersInfoData)
.filter(order => { .filter(order => {
@@ -341,8 +338,6 @@ export const cancelGmxOrdersImpl = async (
return true; // No orders to cancel return true; // No orders to cancel
} }
console.log('orderKeys', orderKeys)
// Cancel orders using the batch method // Cancel orders using the batch method
await sdk.orders.cancelOrders(orderKeys); await sdk.orders.cancelOrders(orderKeys);
@@ -455,8 +450,6 @@ export const closeGmxPositionImpl = async (
showPnlInLeverage: true showPnlInLeverage: true
}); });
console.log('positionsInfo', positionsInfo)
console.log('direction', direction)
// Find the specific position to close // Find the specific position to close
const positionKey = Object.keys(positionsInfo).find(key => { const positionKey = Object.keys(positionsInfo).find(key => {
const position = positionsInfo[key]; const position = positionsInfo[key];
@@ -503,8 +496,6 @@ export const closeGmxPositionImpl = async (
triggerPrice: position.markPrice, triggerPrice: position.markPrice,
} }
//console.log('params', params)
const params2 = { const params2 = {
marketInfo, marketInfo,
marketsInfoData, marketsInfoData,