diff --git a/src/Managing.Application/Bots/TradingBotBase.cs b/src/Managing.Application/Bots/TradingBotBase.cs index 0d3eca4c..d9b47b16 100644 --- a/src/Managing.Application/Bots/TradingBotBase.cs +++ b/src/Managing.Application/Bots/TradingBotBase.cs @@ -170,7 +170,8 @@ public class TradingBotBase : ITradingBot { ExecutionCount++; - Logger.LogInformation("Bot Status - ServerDate: {ServerDate}, LastCandleDate: {LastCandleDate}, Signals: {SignalCount}, Executions: {ExecutionCount}, Positions: {PositionCount}", + Logger.LogInformation( + "Bot Status - ServerDate: {ServerDate}, LastCandleDate: {LastCandleDate}, Signals: {SignalCount}, Executions: {ExecutionCount}, Positions: {PositionCount}", DateTime.UtcNow, LastCandle.Date, Signals.Count, ExecutionCount, Positions.Count); } } @@ -1444,6 +1445,8 @@ public class TradingBotBase : ITradingBot TradingExchanges.GmxV2, IndicatorType.Stc, SignalType.Signal, "Manual Signal"); signal.Status = SignalStatus.WaitingForPosition; // Ensure status is correct + signal.Identifier = + signal.Identifier + "-manual" + Guid.NewGuid(); // Ensure unique identifier for manual signals // Add the signal to our collection await AddSignal(signal); diff --git a/src/Managing.Domain/Indicators/LightSignal.cs b/src/Managing.Domain/Indicators/LightSignal.cs index 9c92cf43..33692c38 100644 --- a/src/Managing.Domain/Indicators/LightSignal.cs +++ b/src/Managing.Domain/Indicators/LightSignal.cs @@ -52,7 +52,7 @@ public class LightSignal [Id(5)] [Required] public Candle Candle { get; } - [Id(6)] [Required] public string Identifier { get; } + [Id(6)] [Required] public string Identifier { get; set; } [Id(7)] [Required] public Ticker Ticker { get; } diff --git a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts index 53d0a430..3e280bd0 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/gmx.ts @@ -294,8 +294,19 @@ export const openGmxPositionImpl = async ( const marketInfo = getMarketInfoFromTicker(ticker, marketsInfoData); const collateralToken = getTokenDataFromTicker("USDC", tokensData); // Using USDC as collateral + // For market orders, we need to get the current market price + let currentPrice = price; + if (!currentPrice) { + // Get current market price from market info + currentPrice = Number(marketInfo.indexToken.prices.minPrice) / 1e30; // Convert from 30 decimals + console.log(`No price provided, using current market price: ${currentPrice}`); + } + // 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) * currentPrice * 1e6)); // USDC has 6 decimals + + // Note: Wallet initialization should be done separately before opening positions + // This reduces position opening time and allows for better error handling // Calculate leverage in basis points (1x = 10000) const leverageBps = BigInt((leverage || 1) * 10000); @@ -316,11 +327,23 @@ export const openGmxPositionImpl = async ( limitPrice: limitPrice, }; + console.log('📋 Position params:', { + payAmount: collateralAmount.toString(), + marketAddress: marketInfo.marketTokenAddress, + payTokenAddress: collateralToken.address, + collateralTokenAddress: collateralToken.address, + leverage: leverageBps.toString(), + direction: direction + }); + + console.log('🚀 Executing position order...'); + if (direction === TradeDirection.Long) { await sdk.orders.long(params); } else { await sdk.orders.short(params); } + console.log('✅ Position order executed successfully'); return ""; } catch (error) { @@ -485,12 +508,23 @@ export async function cancelGmxOrders( } function getMarketInfoFromTicker(ticker: string, marketsInfoData: MarketsInfoData): MarketInfo { + if (ticker === "ETH") { + ticker = "WETH"; + } + const token = getTokenBySymbol(arbitrum.id, ticker); const marketInfo = getMarketByIndexToken(token.address); - const marketInfoData = marketsInfoData[marketInfo.marketTokenAddress]; + if (!marketInfo) { throw new Error(`Market info not found for ticker: ${ticker}`); } + + const marketInfoData = marketsInfoData[marketInfo.marketTokenAddress]; + + if (!marketInfoData) { + throw new Error(`Market data not found for market: ${marketInfo.marketTokenAddress}`); + } + return marketInfoData; } diff --git a/src/Managing.Web3Proxy/src/plugins/custom/privy.ts b/src/Managing.Web3Proxy/src/plugins/custom/privy.ts index 6d232dd5..2991a900 100644 --- a/src/Managing.Web3Proxy/src/plugins/custom/privy.ts +++ b/src/Managing.Web3Proxy/src/plugins/custom/privy.ts @@ -501,30 +501,31 @@ export const getTokenAllowance = async ( */ export const initAddressImpl = async ( address: string, -): Promise<{ usdcHash: string, orderVaultHash: string, exchangeRouterHash: string }> => { +): Promise<{ usdcHash: string, orderVaultHash: string, exchangeRouterHash: string, wethSyntheticsRouterHash: string }> => { try { const sdk = await getClientForAddress(address); const {tokensData} = await sdk.tokens.getTokensData(); const usdcTokenData = getTokenDataFromTicker(Ticker.USDC, tokensData); const wrapperEtherData = getTokenDataFromTicker("WETH", tokensData); - let approveAmount = usdcTokenData.prices.maxPrice; // Large enough amount for trading + + // Use max uint256 for unlimited approvals (more gas efficient than frequent approvals) + let approveAmount = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935"); - // Check approval for USDC + // Check approval for USDC (this first check is for general token approval, keeping original logic) const usdcToken = GetToken('USDC'); const usdcAllowance = await getTokenAllowance(address, usdcToken.address, address); let usdcHash = ""; if (usdcAllowance < approveAmount) { // First approve USDC token for GMX trading - const usdcToken = GetToken('USDC'); usdcHash = await approveTokenImpl( address, usdcToken.symbol, ARBITRUM, usdcTokenData.prices.maxPrice ); - }else{ + } else { usdcHash = "Already allowed :" + usdcAllowance; } @@ -612,14 +613,34 @@ export const initAddressImpl = async ( console.log('usdcSyntheticRouterAllowance', usdcSyntheticRouterAllowance) + // Also approve WETH for SyntheticsRouter (required for ETH positions) + const wethSyntheticsRouterAllowance = await getTokenAllowance(address, wrapperEtherData.address, CONTRACTS[ARBITRUM].SyntheticsRouter); + + let wethSyntheticsRouterHash = ""; + if (wethSyntheticsRouterAllowance < approveAmount) { + wethSyntheticsRouterHash = await approveContractImpl( + address, + wrapperEtherData.address, + CONTRACTS[ARBITRUM].SyntheticsRouter, + ARBITRUM, + approveAmount + ); + } else { + wethSyntheticsRouterHash = "Already allowed :" + wethSyntheticsRouterAllowance; + } + + console.log('wethSyntheticsRouterAllowance', wethSyntheticsRouterAllowance) + console.log('usdcHash', usdcHash) console.log('orderVaultHash', orderVaultHash) console.log('exchangeRouterHash', exchangeRouterHash) + console.log('wethSyntheticsRouterHash', wethSyntheticsRouterHash) return { usdcHash, orderVaultHash, - exchangeRouterHash + exchangeRouterHash, + wethSyntheticsRouterHash }; } catch (error) { console.error('Error initializing address:', error); @@ -644,12 +665,14 @@ export async function initAddress( throw new Error('Wallet address is required for initialization'); } - const { usdcHash, orderVaultHash } = await initAddressImpl(address); + const { usdcHash, orderVaultHash, exchangeRouterHash, wethSyntheticsRouterHash } = await initAddressImpl(address); return { success: true, usdcHash, - orderVaultHash + orderVaultHash, + exchangeRouterHash, + wethSyntheticsRouterHash }; } catch (error) { this.log.error(error); diff --git a/src/Managing.Web3Proxy/test/plugins/open-position.test.ts b/src/Managing.Web3Proxy/test/plugins/open-position.test.ts index cdcd8c84..9b5d5dd5 100644 --- a/src/Managing.Web3Proxy/test/plugins/open-position.test.ts +++ b/src/Managing.Web3Proxy/test/plugins/open-position.test.ts @@ -1,7 +1,7 @@ -import { test } from 'node:test' +import {test} from 'node:test' import assert from 'node:assert' -import { getClientForAddress, openGmxPositionImpl } from '../../src/plugins/custom/gmx' -import { Ticker, TradeDirection } from '../../src/generated/ManagingApiTypes' +import {getClientForAddress, openGmxPositionImpl} from '../../src/plugins/custom/gmx' +import {Ticker, TradeDirection} from '../../src/generated/ManagingApiTypes' test('GMX Position Opening', async (t) => { await t.test('should open a long position for BTC', async () => { @@ -9,13 +9,13 @@ test('GMX Position Opening', async (t) => { const result = await openGmxPositionImpl( sdk, - Ticker.BTC, + Ticker.ETH, TradeDirection.Long, - 0.0002, + 0.00678, 2, - 50003, - 96001, - 85002 + 4400, + 6000, + 3500 ) console.log('Position opening result:', result) assert.ok(result, 'Position opening result should be defined')