optimalized parameters

This commit is contained in:
2025-12-15 09:33:32 +01:00
parent b85fcb8246
commit 109ef7cd24
3 changed files with 196 additions and 26 deletions

View File

@ -30,7 +30,7 @@ setup_logging("normal", "SCALPER_HEDGER")
# --- CONFIGURATION ---
COIN_SYMBOL = "ETH"
CHECK_INTERVAL = 1 # Faster check for scalper
CHECK_INTERVAL = 5 # Optimized for cost/noise reduction (was 1)
LEVERAGE = 5 # 3x Leverage
STATUS_FILE = "hedge_status.json"
@ -39,8 +39,8 @@ STATUS_FILE = "hedge_status.json"
ZONE_BOTTOM_HEDGE_LIMIT = 0.5
# Close Zone: 15% to 20% -> Close All Hedges (Flatten)
ZONE_CLOSE_START = 0.51
ZONE_CLOSE_END = 0.52
ZONE_CLOSE_START = 0.52
ZONE_CLOSE_END = 0.54
# Middle Zone: 20% to 85% -> Idle (No new orders, keep existing)
# Implied by gaps between other zones.
@ -49,8 +49,8 @@ ZONE_CLOSE_END = 0.52
ZONE_TOP_HEDGE_START = 0.8
# --- ORDER SETTINGS ---
PRICE_BUFFER_PCT = 0.0005 # 0.05% price move triggers order update
MIN_THRESHOLD_ETH = 0.01 # Minimum trade size in ETH
PRICE_BUFFER_PCT = 0.002 # 0.2% price move triggers order update (Relaxed for cost)
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
def get_active_automatic_position():
@ -286,6 +286,24 @@ class ScalperHedger:
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):
try:
mids = self.info.all_mids()
@ -321,9 +339,12 @@ class ScalperHedger:
user_state = self.info.user_state(self.vault_address or self.account.address)
for pos in user_state["assetPositions"]:
if pos["position"]["coin"] == coin:
return float(pos["position"]["szi"])
return 0.0
except: return 0.0
return {
'size': float(pos["position"]["szi"]),
'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):
try:
@ -341,10 +362,12 @@ class ScalperHedger:
logging.info(f"🕒 PLACING LIMIT: {coin} {'BUY' if is_buy else 'SELL'} {size} @ {price:.2f}")
reduce_only = is_buy
try:
# Gtc order (Maker)
# Gtc order (Maker) -> Changed to Alo to force Maker
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"]
if status == "ok":
response_data = order_result["response"]["data"]
@ -421,18 +444,69 @@ class ScalperHedger:
self.cancel_order(COIN_SYMBOL, o['oid'])
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
is_buy = current_pos < 0
final_size = round_to_sz_decimals(abs(current_pos), self.sz_decimals)
if final_size == 0: return
# Market order for closing
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)
self.active_position_id = None
price = self.get_market_price(COIN_SYMBOL) # Get mid price for safety fallback
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"Error closing: {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
logging.info("✅ MARKET CLOSE Order Placed (Ioc).")
except Exception as e:
logging.error(f"Error closing positions: {e}", exc_info=True)
def run(self):
logging.info(f"Starting Scalper Monitor Loop. Interval: {CHECK_INTERVAL}s")
@ -467,13 +541,17 @@ class ScalperHedger:
continue
# 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:
time.sleep(5)
continue
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
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_top_start_price = clp_low_range + (range_width * ZONE_TOP_HEDGE_START)
# Update JSON with zone prices if missing
if 'zone_bottom_limit_price' not in active_pos:
# Update JSON with zone prices if they are None (initially set by uniswap_manager.py)
if active_pos.get('zone_bottom_limit_price') is None:
update_position_zones_in_json(active_pos['token_id'], {
'zone_top_start_price': round(zone_top_start_price, 2),
'zone_close_top_price': round(zone_close_top_price, 2),
@ -513,7 +591,7 @@ class ScalperHedger:
# --- Execute Logic ---
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()
time.sleep(CHECK_INTERVAL)
continue
@ -532,20 +610,31 @@ class ScalperHedger:
min_trade_size = MIN_ORDER_VALUE_USD / price
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:
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")
self.place_limit_order(COIN_SYMBOL, is_buy, trade_size, price)
if is_buy:
exec_price = book_levels['bid'] - TICK_SIZE
else:
logging.info("Trade size rounds to 0. Skipping.")
exec_price = book_levels['ask'] + TICK_SIZE
self.place_limit_order(COIN_SYMBOL, is_buy, trade_size, exec_price)
else:
logging.info(f"Idle. Diff {diff_abs:.4f} < Threshold {rebalance_threshold:.4f}. In Hedge Zone.")
logging.info(f"Trade size rounds to 0. Skipping. PNL: ${current_pnl:.2f}")
else:
logging.info(f"Idle. Diff {diff_abs:.4f} < Threshold {rebalance_threshold:.4f}. In Hedge Zone. PNL: ${current_pnl:.2f}")
else:
# MIDDLE ZONE (IDLE)
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)

View File

@ -392,5 +392,81 @@
"zone_close_start_price": 3083.4622618522967,
"zone_close_end_price": 3084.080897853553,
"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
}
]

View File

@ -645,6 +645,11 @@ def main():
unclaimed1 = from_wei(fees_sim[1], pos_details['token1_decimals'])
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
is_out_of_range = False
status_str = "IN RANGE"
@ -657,7 +662,7 @@ def main():
print(f"\nID: {token_id} | Type: {pos_type} | Status: {status_str}")
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) ---
if pos_type == 'AUTOMATIC' and CLOSE_POSITION_ENABLED and is_out_of_range: