optimalized parameters
This commit is contained in:
@ -30,7 +30,7 @@ setup_logging("normal", "SCALPER_HEDGER")
|
|||||||
|
|
||||||
# --- CONFIGURATION ---
|
# --- CONFIGURATION ---
|
||||||
COIN_SYMBOL = "ETH"
|
COIN_SYMBOL = "ETH"
|
||||||
CHECK_INTERVAL = 1 # Faster check for scalper
|
CHECK_INTERVAL = 5 # Optimized for cost/noise reduction (was 1)
|
||||||
LEVERAGE = 5 # 3x Leverage
|
LEVERAGE = 5 # 3x Leverage
|
||||||
STATUS_FILE = "hedge_status.json"
|
STATUS_FILE = "hedge_status.json"
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ STATUS_FILE = "hedge_status.json"
|
|||||||
ZONE_BOTTOM_HEDGE_LIMIT = 0.5
|
ZONE_BOTTOM_HEDGE_LIMIT = 0.5
|
||||||
|
|
||||||
# Close Zone: 15% to 20% -> Close All Hedges (Flatten)
|
# Close Zone: 15% to 20% -> Close All Hedges (Flatten)
|
||||||
ZONE_CLOSE_START = 0.51
|
ZONE_CLOSE_START = 0.52
|
||||||
ZONE_CLOSE_END = 0.52
|
ZONE_CLOSE_END = 0.54
|
||||||
|
|
||||||
# Middle Zone: 20% to 85% -> Idle (No new orders, keep existing)
|
# Middle Zone: 20% to 85% -> Idle (No new orders, keep existing)
|
||||||
# Implied by gaps between other zones.
|
# Implied by gaps between other zones.
|
||||||
@ -49,8 +49,8 @@ ZONE_CLOSE_END = 0.52
|
|||||||
ZONE_TOP_HEDGE_START = 0.8
|
ZONE_TOP_HEDGE_START = 0.8
|
||||||
|
|
||||||
# --- ORDER SETTINGS ---
|
# --- ORDER SETTINGS ---
|
||||||
PRICE_BUFFER_PCT = 0.0005 # 0.05% price move triggers order update
|
PRICE_BUFFER_PCT = 0.002 # 0.2% price move triggers order update (Relaxed for cost)
|
||||||
MIN_THRESHOLD_ETH = 0.01 # Minimum trade size in ETH
|
MIN_THRESHOLD_ETH = 0.02 # Minimum trade size in ETH (~$60, Reduced frequency)
|
||||||
MIN_ORDER_VALUE_USD = 10.0 # Minimum order value for API safety
|
MIN_ORDER_VALUE_USD = 10.0 # Minimum order value for API safety
|
||||||
|
|
||||||
def get_active_automatic_position():
|
def get_active_automatic_position():
|
||||||
@ -286,6 +286,24 @@ class ScalperHedger:
|
|||||||
return 4
|
return 4
|
||||||
except: return 4
|
except: return 4
|
||||||
|
|
||||||
|
def get_order_book_levels(self, coin):
|
||||||
|
try:
|
||||||
|
l2_snapshot = self.info.l2_snapshot(coin)
|
||||||
|
if l2_snapshot and 'levels' in l2_snapshot:
|
||||||
|
bids = l2_snapshot['levels'][0]
|
||||||
|
asks = l2_snapshot['levels'][1]
|
||||||
|
if bids and asks:
|
||||||
|
best_bid = float(bids[0]['px'])
|
||||||
|
best_ask = float(asks[0]['px'])
|
||||||
|
mid = (best_bid + best_ask) / 2
|
||||||
|
return {'bid': best_bid, 'ask': best_ask, 'mid': mid}
|
||||||
|
# Fallback
|
||||||
|
px = self.get_market_price(coin)
|
||||||
|
return {'bid': px, 'ask': px, 'mid': px}
|
||||||
|
except:
|
||||||
|
px = self.get_market_price(coin)
|
||||||
|
return {'bid': px, 'ask': px, 'mid': px}
|
||||||
|
|
||||||
def get_market_price(self, coin):
|
def get_market_price(self, coin):
|
||||||
try:
|
try:
|
||||||
mids = self.info.all_mids()
|
mids = self.info.all_mids()
|
||||||
@ -321,9 +339,12 @@ class ScalperHedger:
|
|||||||
user_state = self.info.user_state(self.vault_address or self.account.address)
|
user_state = self.info.user_state(self.vault_address or self.account.address)
|
||||||
for pos in user_state["assetPositions"]:
|
for pos in user_state["assetPositions"]:
|
||||||
if pos["position"]["coin"] == coin:
|
if pos["position"]["coin"] == coin:
|
||||||
return float(pos["position"]["szi"])
|
return {
|
||||||
return 0.0
|
'size': float(pos["position"]["szi"]),
|
||||||
except: return 0.0
|
'pnl': float(pos["position"]["unrealizedPnl"])
|
||||||
|
}
|
||||||
|
return {'size': 0.0, 'pnl': 0.0}
|
||||||
|
except: return {'size': 0.0, 'pnl': 0.0}
|
||||||
|
|
||||||
def get_open_orders(self):
|
def get_open_orders(self):
|
||||||
try:
|
try:
|
||||||
@ -341,10 +362,12 @@ class ScalperHedger:
|
|||||||
logging.info(f"🕒 PLACING LIMIT: {coin} {'BUY' if is_buy else 'SELL'} {size} @ {price:.2f}")
|
logging.info(f"🕒 PLACING LIMIT: {coin} {'BUY' if is_buy else 'SELL'} {size} @ {price:.2f}")
|
||||||
reduce_only = is_buy
|
reduce_only = is_buy
|
||||||
try:
|
try:
|
||||||
# Gtc order (Maker)
|
# Gtc order (Maker) -> Changed to Alo to force Maker
|
||||||
limit_px = round_to_sig_figs(price, 5)
|
limit_px = round_to_sig_figs(price, 5)
|
||||||
|
|
||||||
order_result = self.exchange.order(coin, is_buy, size, limit_px, {"limit": {"tif": "Gtc"}}, reduce_only=reduce_only)
|
# Use 'Alo' (Add Liquidity Only) to ensure Maker rebate.
|
||||||
|
# If price crosses spread, order is rejected (safe cost-wise).
|
||||||
|
order_result = self.exchange.order(coin, is_buy, size, limit_px, {"limit": {"tif": "Alo"}}, reduce_only=reduce_only)
|
||||||
status = order_result["status"]
|
status = order_result["status"]
|
||||||
if status == "ok":
|
if status == "ok":
|
||||||
response_data = order_result["response"]["data"]
|
response_data = order_result["response"]["data"]
|
||||||
@ -421,18 +444,69 @@ class ScalperHedger:
|
|||||||
self.cancel_order(COIN_SYMBOL, o['oid'])
|
self.cancel_order(COIN_SYMBOL, o['oid'])
|
||||||
|
|
||||||
price = self.get_market_price(COIN_SYMBOL)
|
price = self.get_market_price(COIN_SYMBOL)
|
||||||
current_pos = self.get_current_position(COIN_SYMBOL)
|
pos_data = self.get_current_position(COIN_SYMBOL)
|
||||||
|
current_pos = pos_data['size']
|
||||||
|
|
||||||
if current_pos == 0: return
|
if current_pos == 0: return
|
||||||
|
|
||||||
is_buy = current_pos < 0
|
is_buy = current_pos < 0
|
||||||
final_size = round_to_sz_decimals(abs(current_pos), self.sz_decimals)
|
final_size = round_to_sz_decimals(abs(current_pos), self.sz_decimals)
|
||||||
if final_size == 0: return
|
if final_size == 0: return
|
||||||
|
|
||||||
# Market order for closing
|
price = self.get_market_price(COIN_SYMBOL) # Get mid price for safety fallback
|
||||||
self.exchange.order(COIN_SYMBOL, is_buy, final_size, round_to_sig_figs(price * (1.05 if is_buy else 0.95), 5), {"limit": {"tif": "Ioc"}}, reduce_only=True)
|
pos_data = self.get_current_position(COIN_SYMBOL)
|
||||||
|
current_pos = pos_data['size']
|
||||||
|
|
||||||
|
if current_pos == 0: return
|
||||||
|
|
||||||
|
is_buy_to_close = current_pos < 0
|
||||||
|
final_size = round_to_sz_decimals(abs(current_pos), self.sz_decimals)
|
||||||
|
if final_size == 0: return
|
||||||
|
|
||||||
|
# --- ATTEMPT MAKER CLOSE (Alo) ---
|
||||||
|
try:
|
||||||
|
book_levels = self.get_order_book_levels(COIN_SYMBOL)
|
||||||
|
TICK_SIZE = 0.1
|
||||||
|
|
||||||
|
if is_buy_to_close: # We are short, need to buy to close
|
||||||
|
maker_price = book_levels['bid'] - TICK_SIZE
|
||||||
|
else: # We are long, need to sell to close
|
||||||
|
maker_price = book_levels['ask'] + TICK_SIZE
|
||||||
|
|
||||||
|
logging.info(f"Attempting MAKER CLOSE (Alo): {COIN_SYMBOL} {'BUY' if is_buy_to_close else 'SELL'} {final_size} @ {maker_price:.2f}")
|
||||||
|
order_result = self.exchange.order(COIN_SYMBOL, is_buy_to_close, final_size, round_to_sig_figs(maker_price, 5), {"limit": {"tif": "Alo"}}, reduce_only=True)
|
||||||
|
|
||||||
|
status = order_result["status"]
|
||||||
|
if status == "ok":
|
||||||
|
response_data = order_result["response"]["data"]
|
||||||
|
if "statuses" in response_data and "resting" in response_data["statuses"][0]:
|
||||||
|
logging.info(f"✅ MAKER CLOSE Order Placed (Alo). OID: {response_data['statuses'][0]['resting']['oid']}")
|
||||||
|
return
|
||||||
|
elif "statuses" in response_data and "filled" in response_data["statuses"][0]:
|
||||||
|
logging.info(f"✅ MAKER CLOSE Order Filled (Alo). OID: {response_data['statuses'][0]['filled']['oid']}")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# Fallback if Alo didn't rest or fill immediately in an expected way
|
||||||
|
logging.warning(f"Alo order result unclear: {order_result}. Falling back to Market Close.")
|
||||||
|
|
||||||
|
elif status == "error":
|
||||||
|
if "Post only order would have immediately matched" in order_result["response"]["data"]["statuses"][0].get("error", ""):
|
||||||
|
logging.warning("Alo order would have immediately matched. Falling back to Market Close for guaranteed fill.")
|
||||||
|
else:
|
||||||
|
logging.error(f"Alo order failed with unknown error: {order_result}. Falling back to Market Close.")
|
||||||
|
else:
|
||||||
|
logging.warning(f"Alo order failed with status {status}. Falling back to Market Close.")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Exception during Alo close attempt: {e}. Falling back to Market Close.", exc_info=True)
|
||||||
|
|
||||||
|
# --- FALLBACK TO MARKET CLOSE (Ioc) for guaranteed fill ---
|
||||||
|
logging.info(f"Falling back to MARKET CLOSE (Ioc): {COIN_SYMBOL} {'BUY' if is_buy_to_close else 'SELL'} {final_size} @ {price:.2f} (guaranteed)")
|
||||||
|
self.exchange.order(COIN_SYMBOL, is_buy_to_close, final_size, round_to_sig_figs(price * (1.05 if is_buy_to_close else 0.95), 5), {"limit": {"tif": "Ioc"}}, reduce_only=True)
|
||||||
self.active_position_id = None
|
self.active_position_id = None
|
||||||
|
logging.info("✅ MARKET CLOSE Order Placed (Ioc).")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error closing: {e}")
|
logging.error(f"Error closing positions: {e}", exc_info=True)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
logging.info(f"Starting Scalper Monitor Loop. Interval: {CHECK_INTERVAL}s")
|
logging.info(f"Starting Scalper Monitor Loop. Interval: {CHECK_INTERVAL}s")
|
||||||
@ -467,13 +541,17 @@ class ScalperHedger:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# 2. Market Data
|
# 2. Market Data
|
||||||
price = self.get_order_book_mid(COIN_SYMBOL)
|
book_levels = self.get_order_book_levels(COIN_SYMBOL)
|
||||||
|
price = book_levels['mid']
|
||||||
|
|
||||||
if price is None:
|
if price is None:
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
funding_rate = self.get_funding_rate(COIN_SYMBOL)
|
funding_rate = self.get_funding_rate(COIN_SYMBOL)
|
||||||
current_pos_size = self.get_current_position(COIN_SYMBOL)
|
pos_data = self.get_current_position(COIN_SYMBOL)
|
||||||
|
current_pos_size = pos_data['size']
|
||||||
|
current_pnl = pos_data['pnl']
|
||||||
|
|
||||||
# 3. Calculate Logic
|
# 3. Calculate Logic
|
||||||
calc = self.strategy.calculate_rebalance(price, current_pos_size)
|
calc = self.strategy.calculate_rebalance(price, current_pos_size)
|
||||||
@ -498,8 +576,8 @@ class ScalperHedger:
|
|||||||
zone_close_top_price = clp_low_range + (range_width * ZONE_CLOSE_END)
|
zone_close_top_price = clp_low_range + (range_width * ZONE_CLOSE_END)
|
||||||
zone_top_start_price = clp_low_range + (range_width * ZONE_TOP_HEDGE_START)
|
zone_top_start_price = clp_low_range + (range_width * ZONE_TOP_HEDGE_START)
|
||||||
|
|
||||||
# Update JSON with zone prices if missing
|
# Update JSON with zone prices if they are None (initially set by uniswap_manager.py)
|
||||||
if 'zone_bottom_limit_price' not in active_pos:
|
if active_pos.get('zone_bottom_limit_price') is None:
|
||||||
update_position_zones_in_json(active_pos['token_id'], {
|
update_position_zones_in_json(active_pos['token_id'], {
|
||||||
'zone_top_start_price': round(zone_top_start_price, 2),
|
'zone_top_start_price': round(zone_top_start_price, 2),
|
||||||
'zone_close_top_price': round(zone_close_top_price, 2),
|
'zone_close_top_price': round(zone_close_top_price, 2),
|
||||||
@ -513,7 +591,7 @@ class ScalperHedger:
|
|||||||
|
|
||||||
# --- Execute Logic ---
|
# --- Execute Logic ---
|
||||||
if in_close_zone:
|
if in_close_zone:
|
||||||
logging.info(f"ZONE: CLOSE ({price:.2f} in {zone_close_bottom_price:.2f}-{zone_close_top_price:.2f}). Closing all hedge positions.")
|
logging.info(f"ZONE: CLOSE ({price:.2f} in {zone_close_bottom_price:.2f}-{zone_close_top_price:.2f}). PNL: ${current_pnl:.2f}. Closing all hedge positions.")
|
||||||
self.close_all_positions()
|
self.close_all_positions()
|
||||||
time.sleep(CHECK_INTERVAL)
|
time.sleep(CHECK_INTERVAL)
|
||||||
continue
|
continue
|
||||||
@ -532,20 +610,31 @@ class ScalperHedger:
|
|||||||
min_trade_size = MIN_ORDER_VALUE_USD / price
|
min_trade_size = MIN_ORDER_VALUE_USD / price
|
||||||
|
|
||||||
if trade_size < min_trade_size:
|
if trade_size < min_trade_size:
|
||||||
logging.info(f"Idle. Trade size {trade_size} < Min Order Size {min_trade_size:.4f} (${MIN_ORDER_VALUE_USD:.2f})")
|
logging.info(f"Idle. Trade size {trade_size} < Min Order Size {min_trade_size:.4f} (${MIN_ORDER_VALUE_USD:.2f}). PNL: ${current_pnl:.2f}")
|
||||||
elif trade_size > 0:
|
elif trade_size > 0:
|
||||||
logging.info(f"⚡ THRESHOLD TRIGGERED ({diff_abs:.4f} >= {rebalance_threshold:.4f}). In Hedge Zone.")
|
logging.info(f"⚡ THRESHOLD TRIGGERED ({diff_abs:.4f} >= {rebalance_threshold:.4f}). In Hedge Zone. PNL: ${current_pnl:.2f}")
|
||||||
|
# Execute Passively for Alo
|
||||||
|
# Force 1 tick offset (0.1) away from BBO to ensure rounding doesn't cause cross
|
||||||
|
# Sell at Ask + 0.1, Buy at Bid - 0.1
|
||||||
|
TICK_SIZE = 0.1
|
||||||
|
|
||||||
is_buy = (calc['action'] == "BUY")
|
is_buy = (calc['action'] == "BUY")
|
||||||
self.place_limit_order(COIN_SYMBOL, is_buy, trade_size, price)
|
|
||||||
|
if is_buy:
|
||||||
|
exec_price = book_levels['bid'] - TICK_SIZE
|
||||||
|
else:
|
||||||
|
exec_price = book_levels['ask'] + TICK_SIZE
|
||||||
|
|
||||||
|
self.place_limit_order(COIN_SYMBOL, is_buy, trade_size, exec_price)
|
||||||
else:
|
else:
|
||||||
logging.info("Trade size rounds to 0. Skipping.")
|
logging.info(f"Trade size rounds to 0. Skipping. PNL: ${current_pnl:.2f}")
|
||||||
else:
|
else:
|
||||||
logging.info(f"Idle. Diff {diff_abs:.4f} < Threshold {rebalance_threshold:.4f}. In Hedge Zone.")
|
logging.info(f"Idle. Diff {diff_abs:.4f} < Threshold {rebalance_threshold:.4f}. In Hedge Zone. PNL: ${current_pnl:.2f}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# MIDDLE ZONE (IDLE)
|
# MIDDLE ZONE (IDLE)
|
||||||
pct_position = (price - clp_low_range) / range_width
|
pct_position = (price - clp_low_range) / range_width
|
||||||
logging.info(f"Idle. In Middle Zone ({pct_position*100:.1f}%). No Actions.")
|
logging.info(f"Idle. In Middle Zone ({pct_position*100:.1f}%). PNL: ${current_pnl:.2f}. No Actions.")
|
||||||
|
|
||||||
time.sleep(CHECK_INTERVAL)
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|
||||||
|
|||||||
@ -392,5 +392,81 @@
|
|||||||
"zone_close_start_price": 3083.4622618522967,
|
"zone_close_start_price": 3083.4622618522967,
|
||||||
"zone_close_end_price": 3084.080897853553,
|
"zone_close_end_price": 3084.080897853553,
|
||||||
"zone_top_start_price": 3102.6399778912387
|
"zone_top_start_price": 3102.6399778912387
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "AUTOMATIC",
|
||||||
|
"token_id": 5157680,
|
||||||
|
"opened": "22:21 14/12/25",
|
||||||
|
"status": "CLOSED",
|
||||||
|
"entry_price": 3090.84,
|
||||||
|
"target_value": 1979.52,
|
||||||
|
"amount0_initial": 0.3137,
|
||||||
|
"amount1_initial": 1009.93,
|
||||||
|
"range_upper": 3121.29,
|
||||||
|
"zone_top_start_price": 3108.93,
|
||||||
|
"zone_close_top_price": 3092.24,
|
||||||
|
"zone_close_bottom_price": 3091.0,
|
||||||
|
"zone_bottom_limit_price": 3090.39,
|
||||||
|
"range_lower": 3059.48,
|
||||||
|
"static_long": 0.0,
|
||||||
|
"timestamp_open": 1765747295,
|
||||||
|
"timestamp_close": 1765755472
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "AUTOMATIC",
|
||||||
|
"token_id": 5157819,
|
||||||
|
"opened": "00:45 15/12/25",
|
||||||
|
"status": "CLOSED",
|
||||||
|
"entry_price": 3058.26,
|
||||||
|
"target_value": 1980.8,
|
||||||
|
"amount0_initial": 0.3044,
|
||||||
|
"amount1_initial": 1049.83,
|
||||||
|
"range_upper": 3087.14,
|
||||||
|
"zone_top_start_price": 3074.92,
|
||||||
|
"zone_close_top_price": 3059.02,
|
||||||
|
"zone_close_bottom_price": 3057.8,
|
||||||
|
"zone_bottom_limit_price": 3056.58,
|
||||||
|
"range_lower": 3026.02,
|
||||||
|
"static_long": 0.0,
|
||||||
|
"timestamp_open": 1765755940,
|
||||||
|
"timestamp_close": 1765762761
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "AUTOMATIC",
|
||||||
|
"token_id": 5157922,
|
||||||
|
"opened": "02:47 15/12/25",
|
||||||
|
"status": "CLOSED",
|
||||||
|
"entry_price": 3104.56,
|
||||||
|
"target_value": 1980.84,
|
||||||
|
"amount0_initial": 0.2967,
|
||||||
|
"amount1_initial": 1059.84,
|
||||||
|
"range_upper": 3133.8,
|
||||||
|
"zone_top_start_price": 3121.39,
|
||||||
|
"zone_close_top_price": 3105.26,
|
||||||
|
"zone_close_bottom_price": 3104.02,
|
||||||
|
"zone_bottom_limit_price": 3102.78,
|
||||||
|
"range_lower": 3071.75,
|
||||||
|
"static_long": 0.0,
|
||||||
|
"timestamp_open": 1765763228,
|
||||||
|
"timestamp_close": 1765765504
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "AUTOMATIC",
|
||||||
|
"token_id": 5158011,
|
||||||
|
"opened": "03:32 15/12/25",
|
||||||
|
"status": "OPEN",
|
||||||
|
"entry_price": 3135.31,
|
||||||
|
"target_value": 1983.24,
|
||||||
|
"amount0_initial": 0.3009,
|
||||||
|
"amount1_initial": 1039.86,
|
||||||
|
"range_upper": 3165.29,
|
||||||
|
"zone_top_start_price": 3152.76,
|
||||||
|
"zone_close_top_price": 3136.46,
|
||||||
|
"zone_close_bottom_price": 3135.21,
|
||||||
|
"zone_bottom_limit_price": 3133.95,
|
||||||
|
"range_lower": 3102.62,
|
||||||
|
"static_long": 0.0,
|
||||||
|
"timestamp_open": 1765765971,
|
||||||
|
"timestamp_close": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -644,6 +644,11 @@ def main():
|
|||||||
unclaimed0 = from_wei(fees_sim[0], pos_details['token0_decimals'])
|
unclaimed0 = from_wei(fees_sim[0], pos_details['token0_decimals'])
|
||||||
unclaimed1 = from_wei(fees_sim[1], pos_details['token1_decimals'])
|
unclaimed1 = from_wei(fees_sim[1], pos_details['token1_decimals'])
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
# Calculate Total Fee Value in Token1 (USDC)
|
||||||
|
# Get Current Price from Pool Data
|
||||||
|
current_price = price_from_sqrt_price_x96(pool_data['sqrtPriceX96'], pos_details['token0_decimals'], pos_details['token1_decimals'])
|
||||||
|
total_fees_usd = (unclaimed0 * current_price) + unclaimed1
|
||||||
|
|
||||||
# Check Range
|
# Check Range
|
||||||
is_out_of_range = False
|
is_out_of_range = False
|
||||||
@ -657,7 +662,7 @@ def main():
|
|||||||
|
|
||||||
print(f"\nID: {token_id} | Type: {pos_type} | Status: {status_str}")
|
print(f"\nID: {token_id} | Type: {pos_type} | Status: {status_str}")
|
||||||
print(f" Range: {position['range_lower']:.2f} - {position['range_upper']:.2f}")
|
print(f" Range: {position['range_lower']:.2f} - {position['range_upper']:.2f}")
|
||||||
print(f" Fees: {unclaimed0:.4f} {pos_details['token0_symbol']} / {unclaimed1:.4f} {pos_details['token1_symbol']}")
|
print(f" Fees: {unclaimed0:.4f} {pos_details['token0_symbol']} / {unclaimed1:.4f} {pos_details['token1_symbol']} (~${total_fees_usd:.2f})")
|
||||||
|
|
||||||
# --- AUTO CLOSE LOGIC (AUTOMATIC ONLY) ---
|
# --- AUTO CLOSE LOGIC (AUTOMATIC ONLY) ---
|
||||||
if pos_type == 'AUTOMATIC' and CLOSE_POSITION_ENABLED and is_out_of_range:
|
if pos_type == 'AUTOMATIC' and CLOSE_POSITION_ENABLED and is_out_of_range:
|
||||||
|
|||||||
Reference in New Issue
Block a user