refactor: Standardize CLP Manager and Hedger modules & cleanup
- **clp_manager.py**: Renamed from 'uniswap_manager.py'. Standardized logic for Uniswap V3 liquidity provision. - **clp_hedger.py**: Renamed from 'unified_hedger.py'. Consolidated hedging logic including Delta Calculation fixes, EAC (Edge Avoidance), and Fishing order implementation. - **Cleanup**: Removed legacy 'aerodrome' folder and tools. - **Monitoring**: Added Telegram monitoring scripts. - **Config**: Updated gitignore to exclude market data CSVs.
This commit is contained in:
142
florida/tools/fetch_real_position_data.py
Normal file
142
florida/tools/fetch_real_position_data.py
Normal file
@ -0,0 +1,142 @@
|
||||
import json
|
||||
import os
|
||||
import math
|
||||
import sys
|
||||
from decimal import Decimal, getcontext
|
||||
from web3 import Web3
|
||||
from web3.middleware import ExtraDataToPOAMiddleware
|
||||
from eth_account import Account
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Add project root to path
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from clp_config import CLP_PROFILES
|
||||
|
||||
# Load Env
|
||||
load_dotenv()
|
||||
|
||||
# Config for PancakeSwap
|
||||
PROFILE = CLP_PROFILES["PANCAKESWAP_BNB"]
|
||||
RPC_URL = os.environ.get(PROFILE["RPC_ENV_VAR"])
|
||||
STATUS_FILE = "PANCAKESWAP_BNB_status.json"
|
||||
|
||||
# Minimal ABI for NPM
|
||||
NPM_ABI = [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
|
||||
def get_price_at_tick(tick):
|
||||
return 1.0001 ** tick
|
||||
|
||||
def fetch_and_fix():
|
||||
if not RPC_URL:
|
||||
print("❌ Missing RPC URL in .env")
|
||||
return
|
||||
|
||||
print(f"Connecting to RPC: {RPC_URL}")
|
||||
w3 = Web3(Web3.HTTPProvider(RPC_URL))
|
||||
w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
|
||||
|
||||
if not w3.is_connected():
|
||||
print("❌ Failed to connect to Web3")
|
||||
return
|
||||
|
||||
npm = w3.eth.contract(address=PROFILE["NPM_ADDRESS"], abi=NPM_ABI)
|
||||
|
||||
with open(STATUS_FILE, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
updated_count = 0
|
||||
|
||||
for entry in data:
|
||||
token_id = entry.get('token_id')
|
||||
status = entry.get('status')
|
||||
|
||||
# We check ALL positions to be safe, or just the problematic ones.
|
||||
# Let's check any that seem to have suspect data or just refresh all active/recently active.
|
||||
# The user mentioned 6164702 specifically.
|
||||
|
||||
print(f"🔍 Checking Token ID: {token_id} ({status})")
|
||||
|
||||
try:
|
||||
pos = npm.functions.positions(token_id).call()
|
||||
# Pos structure:
|
||||
# 0: nonce, 1: operator, 2: token0, 3: token1, 4: fee,
|
||||
# 5: tickLower, 6: tickUpper, 7: liquidity ...
|
||||
|
||||
tick_lower = pos[5]
|
||||
tick_upper = pos[6]
|
||||
liquidity = pos[7]
|
||||
|
||||
# Calculate Ranges
|
||||
price_lower = get_price_at_tick(tick_lower)
|
||||
price_upper = get_price_at_tick(tick_upper)
|
||||
|
||||
# Format to 4 decimals
|
||||
new_lower = round(price_lower, 4)
|
||||
new_upper = round(price_upper, 4)
|
||||
|
||||
old_lower = entry.get('range_lower', 0)
|
||||
old_upper = entry.get('range_upper', 0)
|
||||
|
||||
# Check deviation
|
||||
if abs(new_lower - old_lower) > 0.1 or abs(new_upper - old_upper) > 0.1:
|
||||
print(f" ⚠️ Mismatch Found!")
|
||||
print(f" Old: {old_lower} - {old_upper}")
|
||||
print(f" New: {new_lower} - {new_upper}")
|
||||
|
||||
entry['range_lower'] = new_lower
|
||||
entry['range_upper'] = new_upper
|
||||
entry['liquidity'] = str(liquidity)
|
||||
|
||||
# Fix Entry Price if it looks wrong (e.g. 0 or way off range)
|
||||
# If single sided (e.g. 862-869), and spot is 860.
|
||||
# If we provided only Token0 (BNB), we are selling BNB as it goes UP.
|
||||
# So we entered 'below' the range.
|
||||
# If we assume the user just opened it, the 'entry_price' should roughly match
|
||||
# the current market price or at least be consistent.
|
||||
# Since we don't know the exact historical price, we can't perfectly fix 'entry_price'
|
||||
# without event logs.
|
||||
# HOWEVER, for the bot's logic, 'range_lower' and 'range_upper' are critical for 'in_range' checks.
|
||||
# 'entry_price' is mostly for PnL est.
|
||||
|
||||
# If entry_price is wildly different from range (e.g. 844 vs 862-869), it's confusing.
|
||||
# Let's see if we can infer something.
|
||||
# For now, we update ranges as that's the request.
|
||||
|
||||
updated_count += 1
|
||||
else:
|
||||
print(f" ✅ Data looks solid.")
|
||||
|
||||
except Exception as e:
|
||||
print(f" ❌ Error fetching chain data: {e}")
|
||||
|
||||
if updated_count > 0:
|
||||
with open(STATUS_FILE, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
print(f"💾 Updated {updated_count} entries in {STATUS_FILE}")
|
||||
else:
|
||||
print("No updates needed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
fetch_and_fix()
|
||||
Reference in New Issue
Block a user