samsonleegh commited on
Commit
370010e
·
verified ·
1 Parent(s): 33a69c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -0
app.py CHANGED
@@ -149,6 +149,17 @@ You can ask in natural language, e.g.:
149
  RE_TA = re.compile(r"\bTA\s+([A-Za-z0-9_\-]+)(?:\s+(\d+[mhHdD]))?(?:\s+lookback\s+(\d+))?", re.IGNORECASE)
150
  RE_LONG = re.compile(r"\bLONG\s+([A-Za-z0-9_\-]+)\s+([\d.]+)(?:\s+at\s+(market|mkt|[\d.]+))?(?:.*?\bSL\s+([\d.%]+))?(?:.*?\bTP\s+([\d.%]+))?", re.IGNORECASE)
151
  RE_SHORT = re.compile(r"\bSHORT\s+([A-Za-z0-9_\-]+)\s+([\d.]+)(?:\s+at\s+(market|mkt|[\d.]+))?(?:.*?\bSL\s+([\d.%]+))?(?:.*?\bTP\s+([\d.%]+))?", re.IGNORECASE)
 
 
 
 
 
 
 
 
 
 
 
152
 
153
  def pct_or_price(s):
154
  if not s:
@@ -352,6 +363,61 @@ async def handle_message(message: str, history: list[tuple[str, str]]):
352
  + COMMAND_HELP
353
  )
354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  # ---------- Pretty printing for account/positions ----------
356
 
357
  from math import isnan
 
149
  RE_TA = re.compile(r"\bTA\s+([A-Za-z0-9_\-]+)(?:\s+(\d+[mhHdD]))?(?:\s+lookback\s+(\d+))?", re.IGNORECASE)
150
  RE_LONG = re.compile(r"\bLONG\s+([A-Za-z0-9_\-]+)\s+([\d.]+)(?:\s+at\s+(market|mkt|[\d.]+))?(?:.*?\bSL\s+([\d.%]+))?(?:.*?\bTP\s+([\d.%]+))?", re.IGNORECASE)
151
  RE_SHORT = re.compile(r"\bSHORT\s+([A-Za-z0-9_\-]+)\s+([\d.]+)(?:\s+at\s+(market|mkt|[\d.]+))?(?:.*?\bSL\s+([\d.%]+))?(?:.*?\bTP\s+([\d.%]+))?", re.IGNORECASE)
152
+ RE_CLOSE = re.compile(r"\b(close|exit|flatten)\s+(all|[A-Za-z0-9_\-]+)(?:\s+(\d+)%|\s+([\d.]+))?", re.IGNORECASE)
153
+
154
+ def _close_desc(coin_or_all: str, pct: str | None, qty: str | None) -> str:
155
+ coin_or_all = coin_or_all.upper()
156
+ if coin_or_all == "ALL":
157
+ return "Close ALL open positions at market"
158
+ if pct:
159
+ return f"Close {pct}% of {coin_or_all} position at market"
160
+ if qty:
161
+ return f"Reduce {coin_or_all} position by {qty} units at market"
162
+ return f"Close {coin_or_all} position at market"
163
 
164
  def pct_or_price(s):
165
  if not s:
 
363
  + COMMAND_HELP
364
  )
365
 
366
+ mclose = RE_CLOSE.search(text)
367
+ if mclose:
368
+ # groups: verb, coin_or_all, pct (digits%), qty (number)
369
+ coin_or_all = mclose.group(2).strip()
370
+ pct = mclose.group(3) # e.g., "50" meaning 50%
371
+ qty = mclose.group(4) # absolute size to reduce
372
+ desc = _close_desc(coin_or_all, pct, qty)
373
+
374
+ hyper_servers = []
375
+ news_servers = []
376
+ ta_servers = []
377
+ try:
378
+ # Tools for trader context (optional but helpful)
379
+ news_servers = make_crypto_news_mcp_servers()
380
+ ta_servers = make_technical_analyst_mcp_servers()
381
+ hyper_servers = make_hyperliquid_trader_mcp_servers()
382
+
383
+ await asyncio.gather(
384
+ connect_all(news_servers),
385
+ connect_all(ta_servers),
386
+ connect_all(hyper_servers),
387
+ )
388
+
389
+ news_tool = await build_news_tool(news_servers)
390
+ ta_tool = await build_ta_tool(ta_servers)
391
+ trader = await build_trader(hyper_servers, [news_tool, ta_tool])
392
+
393
+ # Natural-language prompt to place the close orders via Hyperliquid MCP
394
+ # (The trader agent already has the rules + account context)
395
+ trade_prompt = f"""
396
+ User request: {desc}.
397
+ Instructions:
398
+ - If 'ALL', close every open position at market.
399
+ - If a coin is specified:
400
+ - If a percent is provided, close that % of the CURRENT open position size.
401
+ - If a qty is provided, reduce by that absolute base-asset amount.
402
+ - If neither provided, fully close that coin position.
403
+ - Include SL/TP cleanup if needed (cancel/replace any attached orders).
404
+ - If the coin has no open position, report that clearly.
405
+ - Return a concise execution summary listing each order (coin, side, size, order type, price if applicable) and rationale.
406
+ """
407
+ with trace("close_positions"):
408
+ result = await Runner.run(trader, trade_prompt, max_turns=20)
409
+
410
+ save_memory(f"[{now_sgt():%Y-%m-%d %H:%M}] Close: {desc}")
411
+ return f"### 🧹 Close — {desc}\n\n{result.final_output}"
412
+ except Exception as e:
413
+ return f"❌ Close error: `{e}`"
414
+ finally:
415
+ await asyncio.gather(
416
+ close_all(news_servers),
417
+ close_all(ta_servers),
418
+ close_all(hyper_servers),
419
+ )
420
+
421
  # ---------- Pretty printing for account/positions ----------
422
 
423
  from math import isnan