Update the gmx for the execution fees
This commit is contained in:
@@ -312,34 +312,6 @@ public class TradingBotBase : ITradingBot
|
||||
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
|
||||
// But first, check if we already have a position for any of these signals
|
||||
var signalsWaitingForPosition = Signals.Values.Where(s => s.Status == SignalStatus.WaitingForPosition);
|
||||
|
||||
@@ -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 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
|
||||
*/
|
||||
export async function estimatePositionGasFee(
|
||||
sdk: GmxSdk,
|
||||
ticker: string = 'BTC'
|
||||
): Promise<bigint> {
|
||||
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
|
||||
// This is a simplified estimation - in practice, you might want to use
|
||||
// the actual transaction simulation or a more sophisticated gas estimation
|
||||
@@ -172,7 +301,7 @@ export async function estimatePositionGasFee(
|
||||
const estimatedGas = (baseGasLimit * 120n) / 100n;
|
||||
const estimatedGasFee = estimatedGas * gasPrice;
|
||||
|
||||
console.log(`⛽ Gas estimation:`, {
|
||||
console.log(`⛽ Simplified gas estimation:`, {
|
||||
baseGasLimit: baseGasLimit.toString(),
|
||||
gasPrice: gasPrice.toString(),
|
||||
estimatedGas: estimatedGas.toString(),
|
||||
|
||||
157
src/Managing.Web3Proxy/test/plugins/get-estimated-gas.test.ts
Normal file
157
src/Managing.Web3Proxy/test/plugins/get-estimated-gas.test.ts
Normal 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')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user