Update logs and cache marketinfo

This commit is contained in:
2025-04-29 20:23:20 +07:00
parent 74c448de30
commit 6736ef4558
3 changed files with 92 additions and 68 deletions

View File

@@ -553,7 +553,7 @@ public class TradingBot : Bot, ITradingBot
} }
else 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."); $"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); SetSignalStatus(signal.Identifier, SignalStatus.Expired);
} }
@@ -876,8 +876,7 @@ public class TradingBot : Bot, ITradingBot
private async Task LogWarning(string message) private async Task LogWarning(string message)
{ {
message = $"[{Name}][{Identifier}] {message}"; message = $"[{Identifier}] {message}";
Logger.LogWarning(message);
SentrySdk.CaptureException(new Exception(message)); SentrySdk.CaptureException(new Exception(message));
await SendTradeMessage(message, true); await SendTradeMessage(message, true);
} }

View File

@@ -28,6 +28,49 @@ import {formatUsd} from '../../generated/gmxsdk/utils/numbers/formatting.js';
import {calculateDisplayDecimals} from '../../generated/gmxsdk/utils/numbers/index.js'; import {calculateDisplayDecimals} from '../../generated/gmxsdk/utils/numbers/index.js';
import {handleError} from '../../utils/errorHandler.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<string, CacheEntry>();
/**
* 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 * GMX Plugin
* *
@@ -99,6 +142,14 @@ export async function getClientForAddress(
subgraphUrl: "https://subgraph.satsuma-prod.com/3b2ced13c8d9/gmx/synthetics-arbitrum-stats/api", subgraphUrl: "https://subgraph.satsuma-prod.com/3b2ced13c8d9/gmx/synthetics-arbitrum-stats/api",
settings: { settings: {
uiFeeReceiverAccount: "0xF9f04a745Db54B25bB8B345a1da74D4E3c38c8aB" uiFeeReceiverAccount: "0xF9f04a745Db54B25bB8B345a1da74D4E3c38c8aB"
},
markets: {
"0x4D3Eb91efd36C2b74181F34B111bc1E91a0d0cb4": {
isListed: false,
},
"0xdf034cd3df9a80eABFA0556232a91E03Ca67D5Cb": {
isListed: false,
},
} }
}; };
@@ -136,8 +187,8 @@ export const openGmxPositionImpl = async (
takeProfitPrice?: number takeProfitPrice?: number
): Promise<string> => { ): Promise<string> => {
try { try {
// Get markets and tokens data from GMX SDK // Get markets and tokens data from GMX SDK with cache
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
if (!marketsInfoData || !tokensData) { if (!marketsInfoData || !tokensData) {
throw new Error("No markets or tokens info data"); 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) // 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
console.log('collateralAmount', collateralAmount);
// Calculate leverage in basis points (1x = 10000) // Calculate leverage in basis points (1x = 10000)
const leverageBps = BigInt((leverage || 1) * 10000); const leverageBps = BigInt((leverage || 1) * 10000);
@@ -168,8 +218,6 @@ export const openGmxPositionImpl = async (
takeProfitPrice: takeProfitPrice ? numberToBigint(takeProfitPrice, 30) : undefined takeProfitPrice: takeProfitPrice ? numberToBigint(takeProfitPrice, 30) : undefined
}; };
console.log('params', params)
if (direction === TradeDirection.Long) { if (direction === TradeDirection.Long) {
await sdk.orders.long(params); await sdk.orders.long(params);
} else { } else {
@@ -391,8 +439,8 @@ export const closeGmxPositionImpl = async (
direction: TradeDirection direction: TradeDirection
): Promise<string> => { ): Promise<string> => {
try { try {
// Get markets and tokens data from GMX SDK // Get markets and tokens data from GMX SDK with cache
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
if (!marketsInfoData || !tokensData) { if (!marketsInfoData || !tokensData) {
throw new Error("No markets or tokens info data"); throw new Error("No markets or tokens info data");
@@ -525,7 +573,7 @@ export const getGmxTradeImpl = async (
ticker: string ticker: string
): Promise<Trade[]> => { ): Promise<Trade[]> => {
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
const orders = await sdk.orders.getOrders({ const orders = await sdk.orders.getOrders({
account: sdk.account, account: sdk.account,
@@ -566,7 +614,6 @@ export const getGmxTradeImpl = async (
if (collateral > 0) { if (collateral > 0) {
leverage = Math.round(size / collateral); 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 ( export const getGmxPositionsImpl = async (
sdk: GmxSdk sdk: GmxSdk
): Promise<Position[]> => { ): Promise<Position[]> => {
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo(); const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
const positionsInfo = await sdk.positions.getPositionsInfo({ const positionsInfo = await sdk.positions.getPositionsInfo({
marketsInfoData, marketsInfoData,
@@ -651,7 +698,7 @@ export const getGmxPositionsImpl = async (
const positions = Object.values(positionsInfo).map(async (pos) => { const positions = Object.values(positionsInfo).map(async (pos) => {
// Fix leverage calculation to avoid exponential notation issues // 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) { if (pos.collateralAmount > 0n) {
// Manual calculation of leverage from raw values // 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 * The use of fastify-plugin is required to be able
* to export the decorators to the outer scope * to export the decorators to the outer scope
@@ -797,5 +862,16 @@ export default fp(async (fastify) => {
fastify.decorateRequest('closeGmxPosition', closeGmxPosition) fastify.decorateRequest('closeGmxPosition', closeGmxPosition)
fastify.decorateRequest('getGmxTrade', getGmxTrade) fastify.decorateRequest('getGmxTrade', getGmxTrade)
fastify.decorateRequest('getGmxPositions', getGmxPositions) 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);
}
});
}) })

View File

@@ -126,67 +126,16 @@ const plugin: FastifyPluginAsyncTypebox = async (fastify) => {
// Use the existing getClientForAddress function to get a proper GMX SDK instance // Use the existing getClientForAddress function to get a proper GMX SDK instance
const sdk = await getClientForAddress(account); const sdk = await getClientForAddress(account);
// Get markets info data // Get the uiFeeFactor - this is a lightweight call
const startTime = Date.now(); const startTime = Date.now();
const marketsInfo = await sdk.markets.getMarketsInfo();
const responseTime = Date.now() - startTime;
// Get the uiFeeFactor
const uiFeeFactor = await sdk.utils.getUiFeeFactor(); const uiFeeFactor = await sdk.utils.getUiFeeFactor();
const responseTime = Date.now() - startTime;
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()
}
};
}
return { return {
status: 'healthy', status: 'healthy',
message: `GMX SDK successfully retrieved markets data (${Object.keys(marketsInfo.marketsInfoData).length} markets)`, message: 'GMX SDK successfully initialized',
data: { data: {
marketCount: Object.keys(marketsInfo.marketsInfoData).length,
responseTimeMs: responseTime, responseTimeMs: responseTime,
sampleMarkets: marketInfoDetails.slice(0, 3), // Just include first 3 markets for brevity
uiFeeFactor: uiFeeFactor.toString() uiFeeFactor: uiFeeFactor.toString()
} }
}; };