From 6736ef4558eb040c118f7c4f45945f10874e1fdc Mon Sep 17 00:00:00 2001 From: cryptooda Date: Tue, 29 Apr 2025 20:23:20 +0700 Subject: [PATCH] Update logs and cache marketinfo --- src/Managing.Application/Bots/TradingBot.cs | 5 +- .../src/plugins/custom/gmx.ts | 98 ++++++++++++++++--- src/Managing.Web3Proxy/src/routes/home.ts | 57 +---------- 3 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/Managing.Application/Bots/TradingBot.cs b/src/Managing.Application/Bots/TradingBot.cs index 9fa4e08..f265309 100644 --- a/src/Managing.Application/Bots/TradingBot.cs +++ b/src/Managing.Application/Bots/TradingBot.cs @@ -553,7 +553,7 @@ public class TradingBot : Bot, ITradingBot } else { - await LogWarning( + await LogInformation( $"A position is already open for signal {previousSignal.Identifier}. Position flipping is currently not enable, the position will not be flipped."); SetSignalStatus(signal.Identifier, SignalStatus.Expired); } @@ -876,8 +876,7 @@ public class TradingBot : Bot, ITradingBot private async Task LogWarning(string message) { - message = $"[{Name}][{Identifier}] {message}"; - Logger.LogWarning(message); + message = $"[{Identifier}] {message}"; SentrySdk.CaptureException(new Exception(message)); await SendTradeMessage(message, true); } diff --git a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts index 435bbd5..0f9e347 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts @@ -28,6 +28,49 @@ import {formatUsd} from '../../generated/gmxsdk/utils/numbers/formatting.js'; import {calculateDisplayDecimals} from '../../generated/gmxsdk/utils/numbers/index.js'; import {handleError} from '../../utils/errorHandler.js'; +// Cache implementation for markets info data +interface CacheEntry { + data: { + marketsInfoData?: MarketsInfoData; + tokensData?: TokensData; + }; + timestamp: number; +} + +const CACHE_TTL = 30 * 60 * 1000; // 30 minutes in milliseconds +const marketsCache = new Map(); + +/** + * Gets markets info data from cache or fetches it from GMX SDK + * @param sdk The GMX SDK client + * @returns Markets info data and tokens data + */ +async function getMarketsInfoWithCache(sdk: GmxSdk): Promise<{ marketsInfoData: MarketsInfoData; tokensData: TokensData }> { + const cacheKey = `markets_${sdk.chainId}`; + const now = Date.now(); + const cached = marketsCache.get(cacheKey); + + if (cached && (now - cached.timestamp) < CACHE_TTL) { + if (!cached.data.marketsInfoData || !cached.data.tokensData) { + throw new Error("Invalid cached data: missing markets or tokens info"); + } + return cached.data as { marketsInfoData: MarketsInfoData; tokensData: TokensData }; + } + + const data = await sdk.markets.getMarketsInfo(); + + if (!data.marketsInfoData || !data.tokensData) { + throw new Error("Invalid response from GMX: missing markets or tokens info"); + } + + marketsCache.set(cacheKey, { + data: data as { marketsInfoData: MarketsInfoData; tokensData: TokensData }, + timestamp: now + }); + + return data as { marketsInfoData: MarketsInfoData; tokensData: TokensData }; +} + /** * GMX Plugin * @@ -99,6 +142,14 @@ export async function getClientForAddress( subgraphUrl: "https://subgraph.satsuma-prod.com/3b2ced13c8d9/gmx/synthetics-arbitrum-stats/api", settings: { uiFeeReceiverAccount: "0xF9f04a745Db54B25bB8B345a1da74D4E3c38c8aB" + }, + markets: { + "0x4D3Eb91efd36C2b74181F34B111bc1E91a0d0cb4": { + isListed: false, + }, + "0xdf034cd3df9a80eABFA0556232a91E03Ca67D5Cb": { + isListed: false, + }, } }; @@ -136,8 +187,8 @@ export const openGmxPositionImpl = async ( takeProfitPrice?: number ): Promise => { try { - // Get markets and tokens data from GMX SDK - const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); + // Get markets and tokens data from GMX SDK with cache + const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); if (!marketsInfoData || !tokensData) { throw new Error("No markets or tokens info data"); @@ -149,7 +200,6 @@ export const openGmxPositionImpl = async ( // Calculate the collateral amount in USDC (quantity * price) const collateralAmount = BigInt(Math.floor((quantity || 0) * (price || 0) * 1e6)); // USDC has 6 decimals - console.log('collateralAmount', collateralAmount); // Calculate leverage in basis points (1x = 10000) const leverageBps = BigInt((leverage || 1) * 10000); @@ -168,8 +218,6 @@ export const openGmxPositionImpl = async ( takeProfitPrice: takeProfitPrice ? numberToBigint(takeProfitPrice, 30) : undefined }; - console.log('params', params) - if (direction === TradeDirection.Long) { await sdk.orders.long(params); } else { @@ -391,8 +439,8 @@ export const closeGmxPositionImpl = async ( direction: TradeDirection ): Promise => { try { - // Get markets and tokens data from GMX SDK - const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); + // Get markets and tokens data from GMX SDK with cache + const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); if (!marketsInfoData || !tokensData) { throw new Error("No markets or tokens info data"); @@ -525,7 +573,7 @@ export const getGmxTradeImpl = async ( ticker: string ): Promise => { - const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); + const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); const orders = await sdk.orders.getOrders({ account: sdk.account, @@ -566,7 +614,6 @@ export const getGmxTradeImpl = async ( if (collateral > 0) { leverage = Math.round(size / collateral); - console.log('Calculated leverage:', leverage, 'from size:', size, 'collateral:', collateral); } } @@ -641,7 +688,7 @@ export async function getGmxTrade( export const getGmxPositionsImpl = async ( sdk: GmxSdk ): Promise => { - const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); + const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk); const positionsInfo = await sdk.positions.getPositionsInfo({ marketsInfoData, @@ -651,7 +698,7 @@ export const getGmxPositionsImpl = async ( const positions = Object.values(positionsInfo).map(async (pos) => { // Fix leverage calculation to avoid exponential notation issues - let leverage = 2; // Default to 2x leverage + let leverage = 1; // Default to 1x leverage if (pos.collateralAmount > 0n) { // Manual calculation of leverage from raw values @@ -783,6 +830,24 @@ export async function getGmxPositions( } } +// Helper to pre-populate and refresh the markets cache +async function getMarketsData() { + // Use a dummy zero address for the account + const account = "0x0000000000000000000000000000000000000000"; + const sdk = await getClientForAddress(account); + await getMarketsInfoWithCache(sdk); +} + +function setupCacheRefresh() { + setInterval(async () => { + try { + await getMarketsData(); + } catch (error) { + console.error('Cache refresh failed:', error); + } + }, CACHE_TTL); +} + /** * The use of fastify-plugin is required to be able * to export the decorators to the outer scope @@ -797,5 +862,16 @@ export default fp(async (fastify) => { fastify.decorateRequest('closeGmxPosition', closeGmxPosition) fastify.decorateRequest('getGmxTrade', getGmxTrade) fastify.decorateRequest('getGmxPositions', getGmxPositions) + + // Pre-populate and refresh the markets cache on startup + fastify.addHook('onReady', async () => { + try { + await getMarketsData(); + setupCacheRefresh(); + } catch (error) { + console.error('Initial cache population failed:', error); + process.exit(1); + } + }); }) diff --git a/src/Managing.Web3Proxy/src/routes/home.ts b/src/Managing.Web3Proxy/src/routes/home.ts index d2f565b..51abdae 100644 --- a/src/Managing.Web3Proxy/src/routes/home.ts +++ b/src/Managing.Web3Proxy/src/routes/home.ts @@ -126,67 +126,16 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => { // Use the existing getClientForAddress function to get a proper GMX SDK instance const sdk = await getClientForAddress(account); - // Get markets info data + // Get the uiFeeFactor - this is a lightweight call const startTime = Date.now(); - const marketsInfo = await sdk.markets.getMarketsInfo(); - const responseTime = Date.now() - startTime; - - // Get the uiFeeFactor const uiFeeFactor = await sdk.utils.getUiFeeFactor(); - - if (!marketsInfo.marketsInfoData || Object.keys(marketsInfo.marketsInfoData).length === 0) { - return { - status: 'degraded', - message: 'GMX SDK returned empty markets info data', - data: { - responseTimeMs: responseTime, - uiFeeFactor: uiFeeFactor.toString() - } - }; - } - - // Check market data for ETH-USD - let foundEthMarket = false; - let marketInfoDetails = []; - - // Collect information about all available markets - for (const [marketAddress, marketInfo] of Object.entries(marketsInfo.marketsInfoData)) { - const marketDetails = { - marketAddress, - indexToken: marketInfo.indexToken?.symbol, - longToken: marketInfo.longToken?.symbol, - shortToken: marketInfo.shortToken?.symbol - }; - - marketInfoDetails.push(marketDetails); - - // Check if this is the ETH market - if (marketInfo.indexToken?.symbol === 'ETH' || - marketInfo.indexToken?.symbol === 'WETH' || - marketInfo.indexToken?.name?.includes('Ethereum')) { - foundEthMarket = true; - } - } - - if (!foundEthMarket) { - return { - status: 'degraded', - message: 'ETH market not found in GMX markets data', - data: { - availableMarkets: marketInfoDetails, - responseTimeMs: responseTime, - uiFeeFactor: uiFeeFactor.toString() - } - }; - } + const responseTime = Date.now() - startTime; return { status: 'healthy', - message: `GMX SDK successfully retrieved markets data (${Object.keys(marketsInfo.marketsInfoData).length} markets)`, + message: 'GMX SDK successfully initialized', data: { - marketCount: Object.keys(marketsInfo.marketsInfoData).length, responseTimeMs: responseTime, - sampleMarkets: marketInfoDetails.slice(0, 3), // Just include first 3 markets for brevity uiFeeFactor: uiFeeFactor.toString() } };