clp hedge zones

This commit is contained in:
2025-12-14 19:03:50 +01:00
parent 84242f3654
commit e31079cdbb
3 changed files with 742 additions and 190 deletions

View File

@ -12,11 +12,13 @@ def clean_address(addr):
def price_from_sqrt_price_x96(sqrt_price_x96, token0_decimals, token1_decimals):
price = (sqrt_price_x96 / (2**96))**2
# Adjust for token decimals assuming price is Token1 per Token0
price = price * (10**(token0_decimals - token1_decimals))
return price
def price_from_tick(tick, token0_decimals, token1_decimals):
price = 1.0001**tick
# Adjust for token decimals assuming price is Token1 per Token0
price = price * (10**(token0_decimals - token1_decimals))
return price
@ -25,54 +27,71 @@ def from_wei(amount, decimals):
# --- V3 Math Helpers ---
def get_sqrt_ratio_at_tick(tick):
# Returns sqrt(price) as a Q96 number
return int((1.0001 ** (tick / 2)) * (2 ** 96))
def get_liquidity_for_amount0(sqrt_ratio_a, sqrt_ratio_b, amount0):
# This function is not used directly in the current calculate_mint_amounts logic,
# but is a common V3 helper
if sqrt_ratio_a > sqrt_ratio_b:
sqrt_ratio_a, sqrt_ratio_b = sqrt_ratio_b, sqrt_ratio_a
# This formula is for a single-sided deposit when current price is outside the range
return int(amount0 * sqrt_ratio_a * sqrt_ratio_b / (sqrt_ratio_b - sqrt_ratio_a))
def get_liquidity_for_amount1(sqrt_ratio_a, sqrt_ratio_b, amount1):
# This function is not used directly in the current calculate_mint_amounts logic,
# but is a common V3 helper
if sqrt_ratio_a > sqrt_ratio_b:
sqrt_ratio_a, sqrt_ratio_b = sqrt_ratio_b, sqrt_ratio_a
# This formula is for a single-sided deposit when current price is outside the range
return int(amount1 / (sqrt_ratio_b - sqrt_ratio_a))
def get_amounts_for_liquidity(sqrt_ratio_current, sqrt_ratio_a, sqrt_ratio_b, liquidity):
# Calculates the required amount of token0 and token1 for a given liquidity and price range
if sqrt_ratio_a > sqrt_ratio_b:
sqrt_ratio_a, sqrt_ratio_b = sqrt_ratio_b, sqrt_ratio_a
amount0 = 0
amount1 = 0
Q96 = 1 << 96
Q96 = 1 << 96 # 2^96
# Current price below the lower tick boundary
if sqrt_ratio_current <= sqrt_ratio_a:
amount0 = ((liquidity * Q96) // sqrt_ratio_a) - ((liquidity * Q96) // sqrt_ratio_b)
amount1 = 0
# Current price within the range
elif sqrt_ratio_current < sqrt_ratio_b:
amount0 = ((liquidity * Q96) // sqrt_ratio_current) - ((liquidity * Q96) // sqrt_ratio_b)
amount1 = (liquidity * (sqrt_ratio_current - sqrt_ratio_a)) // Q96
# Current price above the upper tick boundary
else:
amount1 = (liquidity * (sqrt_ratio_b - sqrt_ratio_a)) // Q96
amount0 = 0
return amount0, amount1
# --- Configuration ---
# RPC URL and Private Key are loaded from .env
RPC_URL = os.environ.get("MAINNET_RPC_URL")
POSITION_TOKEN_ID = int(os.environ.get("POSITION_TOKEN_ID", "0"))
PRIVATE_KEY = os.environ.get("MAIN_WALLET_PRIVATE_KEY") or os.environ.get("PRIVATE_KEY")
# Script behavior flags
MONITOR_INTERVAL_SECONDS = 30
COLLECT_FEES_ENABLED = False
CLOSE_POSITION_ENABLED = True
CLOSE_IF_OUT_OF_RANGE_ONLY = True
COLLECT_FEES_ENABLED = False # If True, will attempt to collect fees once and exit if no open auto position
CLOSE_POSITION_ENABLED = True # If True, will attempt to close auto position when out of range
CLOSE_IF_OUT_OF_RANGE_ONLY = True # If True, closes only if out of range; if False, closes immediately
OPEN_POSITION_ENABLED = True # If True, will open a new position if no auto position exists
OPEN_POSITION_ENABLED = True
TARGET_INVESTMENT_VALUE_TOKEN1 = 100.0
RANGE_WIDTH_PCT = 0.005
# New Position Parameters
TARGET_INVESTMENT_VALUE_TOKEN1 = 350.0 # Target total investment value in Token1 terms (e.g. 350 USDC)
RANGE_WIDTH_PCT = 0.005 # +/- 2% range for new positions
# JSON File for tracking position state
STATUS_FILE = "hedge_status.json"
# --- JSON State Helpers ---
def get_active_automatic_position():
"""Reads hedge_status.json and returns the first OPEN AUTOMATIC position dict, or None."""
if not os.path.exists(STATUS_FILE):
return None
try:
@ -98,6 +117,11 @@ def get_all_open_positions():
return []
def update_hedge_status_file(action, position_data):
"""
Updates the hedge_status.json file.
action: "OPEN" or "CLOSE"
position_data: Dict containing details (token_id, entry_price, range, etc.)
"""
current_data = []
if os.path.exists(STATUS_FILE):
try:
@ -114,9 +138,9 @@ def update_hedge_status_file(action, position_data):
"entry_price": position_data['entry_price'],
"range_lower": position_data['range_lower'],
"range_upper": position_data['range_upper'],
"target_value": position_data.get('target_value', 0.0),
"amount0_initial": position_data.get('amount0', 0),
"amount1_initial": position_data.get('amount1', 0),
"target_value": position_data.get('target_value', 0.0), # Save Actual Value as Target for hedging accuracy
"amount0_initial": position_data.get('amount0_initial', 0),
"amount1_initial": position_data.get('amount1_initial', 0),
"static_long": 0.0,
"timestamp_open": int(time.time()),
"timestamp_close": None
@ -148,6 +172,8 @@ def update_hedge_status_file(action, position_data):
# Simplified for length, usually loaded from huge string
NONFUNGIBLE_POSITION_MANAGER_ABI = json.loads('''
[
{"anonymous": false, "inputs": [{"indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256"}, {"indexed": false, "internalType": "uint128", "name": "liquidity", "type": "uint128"}, {"indexed": false, "internalType": "uint256", "name": "amount0", "type": "uint256"}, {"indexed": false, "internalType": "uint256", "name": "amount1", "type": "uint256"}], "name": "IncreaseLiquidity", "type": "event"},
{"anonymous": false, "inputs": [{"indexed": true, "internalType": "address", "name": "from", "type": "address"}, {"indexed": true, "internalType": "address", "name": "to", "type": "address"}, {"indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256"}], "name": "Transfer", "type": "event"},
{"inputs": [], "name": "factory", "outputs": [{"internalType": "address", "name": "", "type": "address"}], "stateMutability": "view", "type": "function"},
{"inputs": [{"internalType": "uint256", "name": "tokenId", "type": "uint256"}], "name": "positions", "outputs": [{"internalType": "uint96", "name": "nonce", "type": "uint96"}, {"internalType": "address", "name": "operator", "type": "address"}, {"internalType": "address", "name": "token0", "type": "address"}, {"internalType": "address", "name": "token1", "type": "address"}, {"internalType": "uint24", "name": "fee", "type": "uint24"}, {"internalType": "int24", "name": "tickLower", "type": "int24"}, {"internalType": "int24", "name": "tickUpper", "type": "int24"}, {"internalType": "uint128", "name": "liquidity", "type": "uint128"}, {"internalType": "uint256", "name": "feeGrowthInside0LastX128", "type": "uint256"}, {"internalType": "uint256", "name": "feeGrowthInside1LastX128", "type": "uint256"}, {"internalType": "uint128", "name": "tokensOwed0", "type": "uint128"}, {"internalType": "uint128", "name": "tokensOwed1", "type": "uint128"}], "stateMutability": "view", "type": "function"},
{"inputs": [{"components": [{"internalType": "uint256", "name": "tokenId", "type": "uint256"}, {"internalType": "address", "name": "recipient", "type": "address"}, {"internalType": "uint128", "name": "amount0Max", "type": "uint128"}, {"internalType": "uint128", "name": "amount1Max", "type": "uint128"}], "internalType": "struct INonfungiblePositionManager.CollectParams", "name": "params", "type": "tuple"}], "name": "collect", "outputs": [{"internalType": "uint256", "name": "amount0", "type": "uint256"}, {"internalType": "uint256", "name": "amount1", "type": "uint256"}], "stateMutability": "payable", "type": "function"},
@ -243,27 +269,27 @@ def calculate_mint_amounts(current_tick, tick_lower, tick_upper, investment_valu
sqrt_price_lower = get_sqrt_ratio_at_tick(tick_lower)
sqrt_price_upper = get_sqrt_ratio_at_tick(tick_upper)
# 1. Get Price of Token0 in terms of Token1 (e.g., WETH price in USDC)
# 1. Get Price of Token0 in terms of Token1
price_of_token0_in_token1_units = price_from_sqrt_price_x96(sqrt_price_current_x96, decimals0, decimals1)
# 2. Estimate Amounts for a Test Liquidity (L_test)
# 2. Estimate Amounts
L_test = 1 << 128
amt0_test, amt1_test = get_amounts_for_liquidity(sqrt_price_current, sqrt_price_lower, sqrt_price_upper, L_test)
# 3. Adjust test amounts for decimals to get "Real Units" (e.g., 0.1 WETH, 500 USDC)
# 3. Adjust for decimals
real_amt0_test = amt0_test / (10**decimals0)
real_amt1_test = amt1_test / (10**decimals1)
# 4. Calculate Total Value of Test Position in Token1 terms (e.g., Total in USDC)
# 4. Calculate Total Value of Test Position in Token1 terms
value_test = (real_amt0_test * price_of_token0_in_token1_units) + real_amt1_test
if value_test == 0:
return 0, 0
# 5. Scale to Target Investment Value
# 5. Scale
scale = investment_value_token1 / value_test
# 6. Calculate Final Amounts (raw integer units for contract call)
# 6. Final Amounts
final_amt0 = int(amt0_test * scale)
final_amt1 = int(amt1_test * scale)
@ -275,87 +301,73 @@ def check_and_swap(w3_instance, router_contract, account, token0, token1, amount
bal0 = token0_contract.functions.balanceOf(account.address).call()
bal1 = token1_contract.functions.balanceOf(account.address).call()
# Debug Balances
s0 = token0_contract.functions.symbol().call()
s1 = token1_contract.functions.symbol().call()
d0 = token0_contract.functions.decimals().call()
d1 = token1_contract.functions.decimals().call()
print(f"\n--- WALLET CHECK ---")
print(f"Required: {from_wei(amount0_needed, d0):.6f} {s0} | {from_wei(amount1_needed, d1):.2f} {s1}")
print(f"Balance : {from_wei(bal0, d0):.6f} {s0} | {from_wei(bal1, d1):.2f} {s1}")
deficit0 = max(0, amount0_needed - bal0)
deficit1 = max(0, amount1_needed - bal1)
if deficit0 > 0: print(f"Deficit {s0}: {from_wei(deficit0, d0):.6f}")
if deficit1 > 0: print(f"Deficit {s1}: {from_wei(deficit1, d1):.2f}")
# --- AUTO-WRAP ETH LOGIC ---
# Check if we need WETH and have Native ETH
# WETH Address Check (Case insensitive)
weth_addr_lower = WETH_ADDRESS.lower()
# Check Token0 (Deficit0)
# Wrap for Token0 Deficit
if (deficit0 > 0 or deficit1 > 0) and token0.lower() == weth_addr_lower:
native_bal = w3_instance.eth.get_balance(account.address)
gas_reserve = 2 * 10**16 # 0.02 ETH gas reserve
gas_reserve = 5 * 10**15 # 0.005 ETH (Reduced for L2)
available_native = max(0, native_bal - gas_reserve)
# Determine how much to wrap
# If we have deficit1 (need USDC), we likely need to wrap more WETH to swap it.
# Strategy: If deficit1 > 0, wrap ALL available native ETH (up to reasonable limit?).
# Or just wrap what we have.
amount_to_wrap = 0
if deficit0 > 0:
amount_to_wrap = deficit0
if deficit1 > 0:
# We need to buy Token1. We need surplus Token0.
# Wrap all remaining available native ETH to facilitate swap.
amount_to_wrap = available_native
# Safety clamp
amount_to_wrap = min(amount_to_wrap, available_native)
if amount_to_wrap > 0:
print(f"Auto-Wrapping {from_wei(amount_to_wrap, 18)} ETH to WETH...")
weth_contract = w3_instance.eth.contract(address=token0, abi=WETH9_ABI)
wrap_txn = weth_contract.functions.deposit().build_transaction({
'from': account.address,
'value': amount_to_wrap,
'nonce': w3_instance.eth.get_transaction_count(account.address),
'gas': 100000,
'maxFeePerGas': w3_instance.eth.gas_price * 2,
'maxPriorityFeePerGas': w3_instance.eth.max_priority_fee,
'chainId': w3_instance.eth.chain_id
'from': account.address, 'value': amount_to_wrap, 'nonce': w3_instance.eth.get_transaction_count(account.address), 'gas': 100000, 'maxFeePerGas': w3_instance.eth.gas_price * 2, 'maxPriorityFeePerGas': w3_instance.eth.max_priority_fee, 'chainId': w3_instance.eth.chain_id
})
signed_wrap = w3_instance.eth.account.sign_transaction(wrap_txn, private_key=account.key)
raw_wrap = signed_wrap.rawTransaction if hasattr(signed_wrap, 'rawTransaction') else signed_wrap.raw_transaction
tx_hash = w3_instance.eth.send_raw_transaction(raw_wrap)
print(f"Wrap Sent: {tx_hash.hex()}")
w3_instance.eth.wait_for_transaction_receipt(tx_hash)
# Refresh Balance
bal0 = token0_contract.functions.balanceOf(account.address).call()
deficit0 = max(0, amount0_needed - bal0)
else:
if deficit0 > 0:
print(f"Insufficient Native ETH to wrap. Need: {from_wei(deficit0, 18)}, Have: {from_wei(available_native, 18)}")
print(f"Insufficient Native ETH to wrap. Need: {from_wei(deficit0, 18)}, Available: {from_wei(available_native, 18)}")
# Check Token1 (Deficit1) - Assuming Token1 could be WETH too
# Wrap for Token1 Deficit (if Token1 is WETH)
if deficit1 > 0 and token1.lower() == weth_addr_lower:
native_bal = w3_instance.eth.get_balance(account.address)
gas_reserve = 10**16
gas_reserve = 5 * 10**15 # 0.005 ETH
available_native = max(0, native_bal - gas_reserve)
if available_native >= deficit1:
print(f"Auto-Wrapping {from_wei(deficit1, 18)} ETH to WETH...")
weth_contract = w3_instance.eth.contract(address=token1, abi=WETH9_ABI)
wrap_txn = weth_contract.functions.deposit().build_transaction({
'from': account.address,
'value': deficit1,
'nonce': w3_instance.eth.get_transaction_count(account.address),
'gas': 100000,
'maxFeePerGas': w3_instance.eth.gas_price * 2,
'maxPriorityFeePerGas': w3_instance.eth.max_priority_fee,
'chainId': w3_instance.eth.chain_id
'from': account.address, 'value': deficit1, 'nonce': w3_instance.eth.get_transaction_count(account.address), 'gas': 100000, 'maxFeePerGas': w3_instance.eth.gas_price * 2, 'maxPriorityFeePerGas': w3_instance.eth.max_priority_fee, 'chainId': w3_instance.eth.chain_id
})
signed_wrap = w3_instance.eth.account.sign_transaction(wrap_txn, private_key=account.key)
raw_wrap = signed_wrap.rawTransaction if hasattr(signed_wrap, 'rawTransaction') else signed_wrap.raw_transaction
tx_hash = w3_instance.eth.send_raw_transaction(raw_wrap)
print(f"Wrap Sent: {tx_hash.hex()}")
w3_instance.eth.wait_for_transaction_receipt(tx_hash)
# Refresh Balance
bal1 = token1_contract.functions.balanceOf(account.address).call()
deficit1 = max(0, amount1_needed - bal1)
@ -387,6 +399,12 @@ def check_and_swap(w3_instance, router_contract, account, token0, token1, amount
tx_hash = w3_instance.eth.send_raw_transaction(raw_swap)
print(f"Swap Sent: {tx_hash.hex()}")
w3_instance.eth.wait_for_transaction_receipt(tx_hash)
# Verify Balance After Swap
bal0 = token0_contract.functions.balanceOf(account.address).call()
if bal0 < amount0_needed:
print(f"❌ Swap insufficient. Have {bal0}, Need {amount0_needed}")
return False
return True
elif deficit1 > 0 and bal0 > amount0_needed:
@ -414,6 +432,12 @@ def check_and_swap(w3_instance, router_contract, account, token0, token1, amount
tx_hash = w3_instance.eth.send_raw_transaction(raw_swap)
print(f"Swap Sent: {tx_hash.hex()}")
w3_instance.eth.wait_for_transaction_receipt(tx_hash)
# Verify Balance After Swap
bal1 = token1_contract.functions.balanceOf(account.address).call()
if bal1 < amount1_needed:
print(f"❌ Swap insufficient. Have {bal1}, Need {amount1_needed}")
return False
return True
print("❌ Insufficient funds for required amounts.")
@ -421,8 +445,8 @@ def check_and_swap(w3_instance, router_contract, account, token0, token1, amount
def get_token_balances(w3_instance, account_address, token0_address, token1_address):
try:
token0_contract = w3_instance.eth.contract(address=token0_address, abi=ERC20_ABI)
token1_contract = w3_instance.eth.contract(address=token1_address, abi=ERC20_ABI)
token0_contract = w3_instance.eth.contract(address=token0, abi=ERC20_ABI)
token1_contract = w3_instance.eth.contract(address=token1, abi=ERC20_ABI)
b0 = token0_contract.functions.balanceOf(account_address).call()
b1 = token1_contract.functions.balanceOf(account_address).call()
return b0, b1
@ -486,36 +510,29 @@ def mint_new_position(w3_instance, npm_contract, account, token0, token1, amount
result_data = {'token_id': None, 'liquidity': 0, 'amount0': 0, 'amount1': 0}
# Event Topics
transfer_topic = "ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
increase_liquidity_topic = "3067048beee31b25b2f1681f88dac838c8bba36af25bfb2b7cf7473a5847e35f"
for log in receipt['logs']:
topic0 = log['topics'][0].hex().replace("0x", "")
# Web3.py Event Processing to capture ID and Amounts
try:
# 1. Capture Token ID from Transfer event
transfer_events = npm_contract.events.Transfer().process_receipt(receipt)
for event in transfer_events:
if event['args']['from'] == "0x0000000000000000000000000000000000000000":
result_data['token_id'] = event['args']['tokenId']
break
# Parse Token ID from Transfer
if topic0 == transfer_topic and len(log['topics']) > 3:
result_data['token_id'] = int(log['topics'][3].hex(), 16)
# Parse Amounts from IncreaseLiquidity
# Event: IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)
# Indexed args are in topics. Non-indexed in data.
# tokenId is indexed (Topic 1).
# Data: liquidity (32 bytes), amount0 (32 bytes), amount1 (32 bytes) = 96 bytes total
if topic0 == increase_liquidity_topic:
data_hex = log['data'].hex().replace("0x", "")
if len(data_hex) >= 192: # 3 * 64 chars
# Split data into 3 chunks of 64 chars (32 bytes)
liquidity_hex = data_hex[0:64]
amount0_hex = data_hex[64:128]
amount1_hex = data_hex[128:192]
# 2. Capture Amounts from IncreaseLiquidity event
inc_liq_events = npm_contract.events.IncreaseLiquidity().process_receipt(receipt)
for event in inc_liq_events:
if result_data['token_id'] and event['args']['tokenId'] == result_data['token_id']:
result_data['amount0'] = event['args']['amount0']
result_data['amount1'] = event['args']['amount1']
result_data['liquidity'] = event['args']['liquidity']
break
result_data['liquidity'] = int(liquidity_hex, 16)
result_data['amount0'] = int(amount0_hex, 16)
result_data['amount1'] = int(amount1_hex, 16)
print(f"Captured Actual Mint Amounts: {result_data['amount0']} Token0 / {result_data['amount1']} Token1")
except Exception as e:
print(f"Event Processing Warning: {e}")
if result_data['token_id']:
print(f"Captured: ID {result_data['token_id']}, Amt0 {result_data['amount0']}, Amt1 {result_data['amount1']}")
return result_data
return None
@ -573,7 +590,7 @@ def main():
all_positions = get_all_open_positions()
# Check if we have an active AUTOMATIC position
active_automatic_position = next((p for p in all_positions if p['type'] == 'AUTOMATIC'), None)
active_automatic_position = next((p for p in all_positions if p['type'] == 'AUTOMATIC' and p['status'] == 'OPEN'), None)
if all_positions:
print("\n" + "="*60)
@ -625,8 +642,45 @@ def main():
collect_fees(w3, npm_contract, account, token_id)
update_hedge_status_file("CLOSE", {'token_id': token_id})
print("Position Closed & Status Updated.")
# We don't break loop here, let it finish monitoring others,
# but next main loop iteration will see it closed.
# --- REBALANCE ON CLOSE (If Price Dropped) ---
if status_str == "OUT OF RANGE (BELOW)":
print("📉 Position closed BELOW range (100% ETH). Selling 50% of WETH inventory to USDC...")
try:
# Get WETH Balance
token0_c = w3.eth.contract(address=pos_details['token0_address'], abi=ERC20_ABI)
weth_bal = token0_c.functions.balanceOf(account.address).call()
amount_in = weth_bal // 2
if amount_in > 0:
# Approve Router
approve_txn = token0_c.functions.approve(router_contract.address, amount_in).build_transaction({
'from': account.address, 'nonce': w3.eth.get_transaction_count(account.address),
'gas': 100000, 'maxFeePerGas': w3.eth.gas_price * 2, 'maxPriorityFeePerGas': w3.eth.max_priority_fee,
'chainId': w3.eth.chain_id
})
signed = w3.eth.account.sign_transaction(approve_txn, private_key=account.key)
raw = signed.rawTransaction if hasattr(signed, 'rawTransaction') else signed.raw_transaction
w3.eth.send_raw_transaction(raw)
time.sleep(2)
# Swap WETH -> USDC
params = (pos_details['token0_address'], pos_details['token1_address'], 500, account.address, int(time.time()) + 120, amount_in, 0, 0)
swap_txn = router_contract.functions.exactInputSingle(params).build_transaction({
'from': account.address, 'nonce': w3.eth.get_transaction_count(account.address),
'gas': 300000, 'maxFeePerGas': w3.eth.gas_price * 2, 'maxPriorityFeePerGas': w3.eth.max_priority_fee,
'chainId': w3.eth.chain_id
})
signed_swap = w3.eth.account.sign_transaction(swap_txn, private_key=account.key)
raw_swap = signed_swap.rawTransaction if hasattr(signed_swap, 'rawTransaction') else signed_swap.raw_transaction
tx_hash = w3.eth.send_raw_transaction(raw_swap)
print(f"⚖️ Rebalance Swap Sent: {tx_hash.hex()}")
w3.eth.wait_for_transaction_receipt(tx_hash)
print("✅ Rebalance Complete.")
except Exception as e:
print(f"Error during rebalance swap: {e}")
else:
print("Liquidity 0. Marking closed.")
update_hedge_status_file("CLOSE", {'token_id': token_id})
@ -651,50 +705,47 @@ def main():
upper = (tick + tick_delta) // spacing * spacing
# Amounts
token0_c = w3.eth.contract(address=token0, abi=ERC20_ABI)
token1_c = w3.eth.contract(address=token1, abi=ERC20_ABI)
try:
token0_c = w3.eth.contract(address=token0, abi=ERC20_ABI)
token1_c = w3.eth.contract(address=token1, abi=ERC20_ABI)
d0 = token0_c.functions.decimals().call()
d1 = token1_c.functions.decimals().call()
except:
print("Error fetching decimals")
time.sleep(5)
except Exception as e:
print(f"Error fetching decimals: {e}")
time.sleep(MONITOR_INTERVAL_SECONDS)
continue
amt0, amt1 = calculate_mint_amounts(tick, lower, upper, TARGET_INVESTMENT_VALUE_TOKEN1, d0, d1, pool_data['sqrtPriceX96'])
amt0_buf, amt1_buf = int(amt0 * 1.02), int(amt1 * 1.02)
# 4. Swap & Mint
if check_and_swap(w3, router_contract, account, token0, token1, amt0_buf, amt1_buf):
mint_result = mint_new_position(w3, npm_contract, account, token0, token1, amt0, amt1, lower, upper)
if mint_result:
# Calculate Actual Value
try:
s0 = token0_c.functions.symbol().call()
s1 = token1_c.functions.symbol().call()
except:
s0, s1 = "T0", "T1"
if mint_result: # Calculate Actual Value
try:
s0 = token0_c.functions.symbol().call()
s1 = token1_c.functions.symbol().call()
except:
s0, s1 = "T0", "T1"
real_amt0 = from_wei(mint_result['amount0'], d0)
real_amt1 = from_wei(mint_result['amount1'], d1)
entry_price = price_from_sqrt_price_x96(pool_data['sqrtPriceX96'], d0, d1)
# Value in Token1 terms (e.g. USDC)
actual_value = (real_amt0 * entry_price) + real_amt1
print(f"ACTUAL MINT VALUE: {actual_value:.2f} {s1}/{s0}")
pos_data = {
'token_id': mint_result['token_id'],
'entry_price': entry_price,
'range_lower': price_from_tick(lower, d0, d1),
'range_upper': price_from_tick(upper, d0, d1),
'target_value': actual_value, # Save Actual Value as Target for hedging accuracy
'amount0_initial': mint_result['amount0'],
'amount1_initial': mint_result['amount1']
}
update_hedge_status_file("OPEN", pos_data)
print("Cycle Complete. Monitoring.")
real_amt0 = from_wei(mint_result['amount0'], d0)
real_amt1 = from_wei(mint_result['amount1'], d1)
entry_price = price_from_sqrt_price_x96(pool_data['sqrtPriceX96'], d0, d1)
actual_value = (real_amt0 * entry_price) + real_amt1
print(f"ACTUAL MINT VALUE: {actual_value:.2f} {s1}/{s0}")
pos_data = {
'token_id': mint_result['token_id'],
'entry_price': entry_price,
'range_lower': price_from_tick(lower, d0, d1),
'range_upper': price_from_tick(upper, d0, d1),
'target_value': actual_value,
'amount0_initial': mint_result['amount0'],
'amount1_initial': mint_result['amount1']
}
update_hedge_status_file("OPEN", pos_data)
print("Cycle Complete. Monitoring.")
elif not all_positions:
print("No open positions (Manual or Automatic). Waiting...")
@ -708,4 +759,4 @@ def main():
time.sleep(MONITOR_INTERVAL_SECONDS)
if __name__ == "__main__":
main()
main()