Update the gmx for the execution fees

This commit is contained in:
2025-10-17 00:49:20 +07:00
parent d6122aeb27
commit 3f1b5f09e0
3 changed files with 289 additions and 31 deletions

View File

@@ -312,34 +312,6 @@ public class TradingBotBase : ITradingBot
await UpdatePosition(signalForPosition, position); await UpdatePosition(signalForPosition, position);
} }
// Second, process all finished positions to ensure they are updated in the database
// TODO : This should be removed in the future, when we have a better way to handle positions
if (!Config.IsForBacktest)
foreach (var position in Positions.Values.Where(p => p.IsFinished()))
{
try
{
var positionInDatabase = await ServiceScopeHelpers.WithScopedService<ITradingService, Position>(
_scopeFactory,
async tradingService =>
{
return await tradingService.GetPositionByIdentifierAsync(position.Identifier);
});
if (positionInDatabase != null && positionInDatabase.Status != position.Status)
{
await UpdatePositionDatabase(position);
await LogInformation(
$"💾 Database Update\nPosition: `{position.Identifier}`\nStatus: `{position.Status}`\nUpdated in database");
}
}
catch (Exception ex)
{
await LogWarning(
$"Failed to update finished position {position.Identifier} in database: {ex.Message}");
}
}
// Then, open positions for signals waiting for a position open // Then, open positions for signals waiting for a position open
// But first, check if we already have a position for any of these signals // But first, check if we already have a position for any of these signals
var signalsWaitingForPosition = Signals.Values.Where(s => s.Status == SignalStatus.WaitingForPosition); var signalsWaitingForPosition = Signals.Values.Where(s => s.Status == SignalStatus.WaitingForPosition);

View File

@@ -147,15 +147,144 @@ export async function checkGasFeeBalance(
} }
/** /**
* Estimates gas fee for a position opening transaction * Estimates gas fee for a position opening transaction using GMX SDK proper execution fee calculation
* @param sdk The GMX SDK client * @param sdk The GMX SDK client
* @param params The position increase parameters * @param ticker The ticker symbol for the market (e.g., 'BTC', 'ETH', 'ARB')
* @param positionSizeUsd Optional position size in USD for more accurate estimation (default: $1000)
* @param leverage Optional leverage multiplier (default: 10x)
* @returns Object with execution fee details
*/
export async function estimatePositionExecutionFee(
sdk: GmxSdk,
ticker: string,
positionSizeUsd: number = 1000,
leverage: number = 10
): Promise<{
feeUsd: bigint;
feeTokenAmount: bigint;
gasLimit: bigint;
isFeeHigh: boolean;
isFeeVeryHigh: boolean;
feeToken: any;
marketInfo?: any;
} | null> {
try {
// Get markets and tokens data
const {marketsInfoData, tokensData} = await getMarketsInfoWithCache(sdk);
// Find the market for the specific ticker
const marketInfo = Object.values(marketsInfoData).find(market =>
market.indexToken.symbol === ticker.toUpperCase()
);
if (!marketInfo) {
console.warn(`Market not found for ticker: ${ticker}`);
return null;
}
console.log(`📊 Found market for ${ticker}:`, {
marketAddress: marketInfo.marketTokenAddress,
indexTokenSymbol: marketInfo.indexToken.symbol,
indexTokenAddress: marketInfo.indexToken.address,
longTokenSymbol: marketInfo.longToken.symbol,
shortTokenSymbol: marketInfo.shortToken.symbol
});
// Calculate position amounts based on real market data
const collateralAmount = BigInt(Math.floor(positionSizeUsd / leverage)) * BigInt(1e18); // Collateral in USDC
const sizeDeltaUsd = BigInt(positionSizeUsd) * BigInt(1e30); // Position size in USD
const leverageBps = BigInt(leverage * 10000); // Convert to basis points
// Get current index price
const indexPrice = marketInfo.indexToken.prices?.minPrice || BigInt(1e30);
// Calculate size delta in tokens
const sizeDeltaInTokens = sizeDeltaUsd * BigInt(1e30) / indexPrice;
// Create realistic increase amounts using actual market data
const increaseAmounts = {
sizeDeltaUsd: sizeDeltaUsd,
collateralDeltaAmount: collateralAmount,
sizeDeltaInTokens: sizeDeltaInTokens,
initialCollateralAmount: collateralAmount,
initialCollateralUsd: BigInt(Math.floor(positionSizeUsd / leverage)) * BigInt(1e30),
collateralDeltaUsd: BigInt(Math.floor(positionSizeUsd / leverage)) * BigInt(1e30),
swapStrategy: { type: 'none' } as any,
indexTokenAmount: sizeDeltaInTokens,
indexPrice: indexPrice,
initialCollateralPrice: BigInt(1e30), // USDC price (1 USD)
collateralPrice: BigInt(1e30), // USDC price (1 USD)
estimatedLeverage: leverageBps,
acceptablePrice: indexPrice,
acceptablePriceDeltaBps: BigInt(50), // 0.5% slippage
recommendedAcceptablePriceDeltaBps: BigInt(50),
positionFeeUsd: 0n, // Will be calculated by GMX
positionFeeAmount: 0n, // Will be calculated by GMX
positionFeeAmountForCollateral: 0n, // Will be calculated by GMX
swapPathStats: [],
triggerPrice: undefined,
limitOrderType: undefined,
triggerThresholdType: undefined
} as any;
console.log(`📋 Position parameters for ${ticker}:`, {
positionSizeUsd: positionSizeUsd,
leverage: leverage,
collateralAmount: collateralAmount.toString(),
sizeDeltaUsd: sizeDeltaUsd.toString(),
sizeDeltaInTokens: sizeDeltaInTokens.toString(),
indexPrice: indexPrice.toString()
});
const executionFee = await sdk.utils.getExecutionFee('increase', tokensData, {
increaseAmounts: increaseAmounts
});
if (!executionFee) {
console.warn('Failed to get execution fee from GMX SDK');
return null;
}
console.log(`⛽ GMX Execution Fee estimation for ${ticker}:`, {
feeUsd: `$${(Number(executionFee.feeUsd) / 1e30).toFixed(2)}`,
feeTokenAmount: `${(Number(executionFee.feeTokenAmount) / 1e18).toFixed(6)} ETH`,
gasLimit: executionFee.gasLimit.toString(),
isFeeHigh: executionFee.isFeeHigh,
isFeeVeryHigh: executionFee.isFeeVeryHigh,
feeTokenSymbol: executionFee.feeToken.symbol
});
return {
...executionFee,
marketInfo: marketInfo
};
} catch (error) {
console.error(`Error estimating GMX execution fee for ${ticker}:`, error);
return null;
}
}
/**
* Estimates gas fee for a position opening transaction (legacy simplified method)
* @param sdk The GMX SDK client
* @param ticker Optional ticker for more accurate estimation (default: 'BTC')
* @returns Estimated gas fee in wei * @returns Estimated gas fee in wei
*/ */
export async function estimatePositionGasFee( export async function estimatePositionGasFee(
sdk: GmxSdk, sdk: GmxSdk,
ticker: string = 'BTC'
): Promise<bigint> { ): Promise<bigint> {
try { try {
// First try to get proper GMX execution fee
const executionFee = await estimatePositionExecutionFee(sdk, ticker);
if (executionFee) {
return executionFee.feeTokenAmount;
}
// Fallback to simplified estimation
console.log('⚠️ Falling back to simplified gas estimation');
// Estimate gas for the position opening transaction // Estimate gas for the position opening transaction
// This is a simplified estimation - in practice, you might want to use // This is a simplified estimation - in practice, you might want to use
// the actual transaction simulation or a more sophisticated gas estimation // the actual transaction simulation or a more sophisticated gas estimation
@@ -172,7 +301,7 @@ export async function estimatePositionGasFee(
const estimatedGas = (baseGasLimit * 120n) / 100n; const estimatedGas = (baseGasLimit * 120n) / 100n;
const estimatedGasFee = estimatedGas * gasPrice; const estimatedGasFee = estimatedGas * gasPrice;
console.log(`Gas estimation:`, { console.log(`Simplified gas estimation:`, {
baseGasLimit: baseGasLimit.toString(), baseGasLimit: baseGasLimit.toString(),
gasPrice: gasPrice.toString(), gasPrice: gasPrice.toString(),
estimatedGas: estimatedGas.toString(), estimatedGas: estimatedGas.toString(),

View File

@@ -0,0 +1,157 @@
import {test} from 'node:test'
import assert from 'node:assert'
import {
checkGasFeeBalance,
estimatePositionExecutionFee,
estimatePositionGasFee,
getClientForAddress
} from '../../src/plugins/custom/gmx'
/**
* Get GMX execution fee using the SDK's proper fee calculation
* This uses the same logic as GMX frontend for accurate fee estimation
*/
async function getGmxExecutionFee(sdk: any) {
try {
// Get markets and tokens data
const {marketsInfoData, tokensData} = await sdk.markets.getMarketsInfo()
// Get gas limits and gas price from SDK utils
const gasLimits = await sdk.utils.getGasLimits()
const gasPrice = await sdk.utils.getGasPrice()
console.log('📊 Gas limits:', {
increaseOrder: gasLimits.increaseOrder.toString(),
estimatedGasFeeBaseAmount: gasLimits.estimatedGasFeeBaseAmount.toString(),
estimatedGasFeePerOraclePrice: gasLimits.estimatedGasFeePerOraclePrice.toString(),
estimatedFeeMultiplierFactor: gasLimits.estimatedFeeMultiplierFactor.toString()
})
console.log('⛽ Gas price:', gasPrice.toString())
// Get execution fee for increase position (opening a position)
const mockIncreaseAmounts = {
sizeDeltaUsd: 1000n * BigInt(1e30), // $1000 position size
collateralDeltaAmount: 100n * BigInt(1e18), // 100 USDC collateral
sizeDeltaInTokens: 0n,
initialCollateralAmount: 100n * BigInt(1e18),
initialCollateralUsd: 100n * BigInt(1e30),
collateralDeltaUsd: 100n * BigInt(1e30),
swapStrategy: { type: 'none' } as any,
indexTokenAmount: 0n,
indexPrice: BigInt(1e30),
initialCollateralPrice: BigInt(1e30),
collateralPrice: BigInt(1e30),
estimatedLeverage: BigInt(10),
acceptablePrice: BigInt(1e30),
acceptablePriceDeltaBps: 0n,
recommendedAcceptablePriceDeltaBps: 0n,
positionFeeUsd: 0n,
positionFeeAmount: 0n,
positionFeeAmountForCollateral: 0n,
swapPathStats: [],
triggerPrice: undefined,
limitOrderType: undefined,
triggerThresholdType: undefined
} as any;
const executionFee = await sdk.utils.getExecutionFee('increase', tokensData, {
increaseAmounts: mockIncreaseAmounts
})
if (!executionFee) {
throw new Error('Failed to get execution fee from GMX SDK')
}
console.log('💰 GMX Execution Fee:', {
feeUsd: executionFee.feeUsd.toString(),
feeTokenAmount: executionFee.feeTokenAmount.toString(),
gasLimit: executionFee.gasLimit.toString(),
isFeeHigh: executionFee.isFeeHigh,
isFeeVeryHigh: executionFee.isFeeVeryHigh,
feeTokenSymbol: executionFee.feeToken.symbol
})
return executionFee
} catch (error) {
console.error('Error getting GMX execution fee:', error)
throw error
}
}
test('GMX Gas Fee Estimation', async (t) => {
await t.test('should estimate gas fee using GMX SDK proper execution fee calculation with real ticker data', async () => {
// Use a test account address
const testAccount = '0x0b4A132cb6ed8fa66953bf61a53D0B91DaCaAd78'
// Get GMX SDK client
const sdk = await getClientForAddress(testAccount)
// Test different tickers to see how gas fees vary
const testTickers = ['BTC', 'ETH', 'ARB', 'SOL']
for (const ticker of testTickers) {
console.log(`\n🔍 Testing execution fee for ${ticker}...`)
// Test GMX SDK execution fee calculation using our helper function with real ticker
const gmxExecutionFeeHelper = await estimatePositionExecutionFee(sdk, ticker, 1000, 10)
if (gmxExecutionFeeHelper) {
console.log(`${ticker} execution fee:`, {
feeUsd: `$${(Number(gmxExecutionFeeHelper.feeUsd) / 1e30).toFixed(2)}`,
feeTokenAmount: `${(Number(gmxExecutionFeeHelper.feeTokenAmount) / 1e18).toFixed(6)} ETH`,
gasLimit: gmxExecutionFeeHelper.gasLimit.toString(),
isFeeHigh: gmxExecutionFeeHelper.isFeeHigh,
isFeeVeryHigh: gmxExecutionFeeHelper.isFeeVeryHigh,
marketAddress: gmxExecutionFeeHelper.marketInfo?.marketTokenAddress,
indexTokenSymbol: gmxExecutionFeeHelper.marketInfo?.indexToken.symbol
})
// Assertions for helper function execution fee
assert.ok(gmxExecutionFeeHelper.feeUsd > 0n, `${ticker} execution fee USD should be greater than 0`)
assert.ok(gmxExecutionFeeHelper.feeTokenAmount > 0n, `${ticker} execution fee token amount should be greater than 0`)
assert.ok(gmxExecutionFeeHelper.gasLimit > 0n, `${ticker} gas limit should be greater than 0`)
assert.ok(gmxExecutionFeeHelper.marketInfo, `${ticker} market info should be available`)
assert.ok(gmxExecutionFeeHelper.marketInfo.indexToken.symbol === ticker, `${ticker} market should match ticker`)
// Test checkGasFeeBalance function with GMX execution fee
console.log(`💰 Checking gas fee balance for ${ticker}...`)
const gasFeeCheck = await checkGasFeeBalance(sdk, gmxExecutionFeeHelper.feeTokenAmount)
console.log(`${ticker} gas fee check result:`, {
hasSufficientBalance: gasFeeCheck.hasSufficientBalance,
estimatedGasFeeUsd: `$${gasFeeCheck.estimatedGasFeeUsd.toFixed(2)}`,
ethBalance: `${gasFeeCheck.ethBalance} ETH`
})
// Assertions for gas fee check
assert.ok(typeof gasFeeCheck.hasSufficientBalance === 'boolean', `${ticker} hasSufficientBalance should be a boolean`)
assert.ok(typeof gasFeeCheck.ethBalance === 'string', `${ticker} ethBalance should be a string`)
assert.ok(typeof gasFeeCheck.estimatedGasFeeUsd === 'number', `${ticker} estimatedGasFeeUsd should be a number`)
assert.ok(gasFeeCheck.ethPrice > 0, `${ticker} ETH price should be greater than 0`)
assert.ok(gasFeeCheck.estimatedGasFeeUsd >= 0, `${ticker} estimated gas fee USD should be non-negative`)
console.log(`📈 ${ticker} insights:`)
console.log(` - Execution fee: $${(Number(gmxExecutionFeeHelper.feeUsd) / 1e30).toFixed(2)}`)
console.log(` - ETH amount needed: ${(Number(gmxExecutionFeeHelper.feeTokenAmount) / 1e18).toFixed(6)} ETH`)
console.log(` - Gas limit: ${gmxExecutionFeeHelper.gasLimit}`)
console.log(` - Fee is ${gmxExecutionFeeHelper.isFeeHigh ? 'HIGH' : 'normal'}`)
console.log(` - Fee is ${gmxExecutionFeeHelper.isFeeVeryHigh ? 'VERY HIGH' : 'not excessive'}`)
} else {
console.log(`⚠️ No execution fee data available for ${ticker}`)
}
}
// Test our simplified estimation for comparison
console.log('\n⛽ Testing simplified gas fee estimation...')
const estimatedGasFee = await estimatePositionGasFee(sdk, 'BTC')
console.log('📊 Simplified estimation:', {
estimatedGasFeeEth: `${(Number(estimatedGasFee) / 1e18).toFixed(6)} ETH`
})
assert.ok(estimatedGasFee > 0n, 'Simplified estimated gas fee should be greater than 0')
console.log('✅ GMX execution fee test with real ticker data completed successfully')
})
})