Update the gmx for the execution fees
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
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