primecli 0.5.8__tar.gz → 0.5.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: primecli
3
- Version: 0.5.8
3
+ Version: 0.5.9
4
4
  Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
5
5
  Author: Mnemosyne-quest contributors
6
6
  License: MIT
@@ -22,6 +22,7 @@ Strategy config (JSON):
22
22
 
23
23
  import json
24
24
  import os
25
+ import re
25
26
  import subprocess
26
27
  import sys
27
28
  import time
@@ -36,18 +37,22 @@ TIER_MAX = {"basic": 5, "premium": 10}
36
37
  # Health computation
37
38
  # ════════════════════════════════════════════════════════════════════
38
39
 
39
- def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
40
- """Compute health (0-100%) using the frontend formula from DeltaPrime docs.
40
+ def compute_health(
41
+ defi_data: dict,
42
+ max_mult: int = 10,
43
+ per_asset_powers: dict[str, int] | None = None,
44
+ ) -> dict:
45
+ """Compute health (0-100%) using the cross-margin formula from DeltaPrime docs.
41
46
 
42
- Uses the cross-margin health formula (all assets assumed same borrowing power):
43
- equity = total_supplied_usd - total_debt_usd
44
- health_pct = 100 * (1 - debt / (max_mult * equity))
45
- (0% = liquidation, 100% = no debt)
47
+ Cross-margin formula (when per_asset_powers is provided):
48
+ Pr_i = power_i / (power_i + 1) # borrowing power ratio per asset
49
+ Cw_i = supplied_usd_i x Pr_i # weighted collateral per asset
50
+ Bw_i = borrowed_usd_i x Pr_i # weighted borrows per asset
51
+ H = (SigmaCw + SigmaBw - B) / SigmaCw x 100
46
52
 
47
- Background:
48
- The frontend uses Pr = tier / (tier + 1) and computes:
49
- health_pct = (Pr * supplied - debt) / (Pr * equity) * 100
50
- which simplifies to: 100 * (1 - debt / (max_mult * equity)).
53
+ Falls back to the simplified uniform formula when per_asset_powers is None
54
+ (all assets assumed at max_mult borrowing power):
55
+ health_pct = 100 * (1 - debt / (max_mult * equity))
51
56
 
52
57
  DIFFERENT from the equity-based "health_pct" in defi --json / prime-summary
53
58
  (which uses max_debt = equity * (tier - 1)).
@@ -60,34 +65,6 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
60
65
  supplied = g.get("supplied", [])
61
66
  borrowed = g.get("borrowed", [])
62
67
  health_ratio = g.get("health_ratio", 0) or 0
63
- # Use precomputed health_pct from defi --json if available (primecli >= 0.5.4)
64
- precomputed = g.get("health_pct")
65
- if precomputed is not None:
66
- # Override health_pct with frontend formula (ignores precomputed value)
67
- supplied_usd = sum(s.get("usd", 0) or 0 for s in supplied)
68
- debt_usd = sum(b.get("usd", 0) or 0 for b in borrowed)
69
- equity = max(supplied_usd - debt_usd, 0.01)
70
- raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
71
- symbols = [s.get("symbol", "") for s in supplied]
72
- has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
73
- has_lb = any(sym in ("LB_AVAX_USDC", "LB_WAVAX_USDC", "JOE") or "TRADERJOE" in sym.upper() for sym in symbols)
74
- has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
75
- # Frontend formula: health_pct = 100 * (1 - debt / (max_mult * equity))
76
- fe_health = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / (max_mult * equity)))
77
- fe_max_debt = round(max_mult * equity, 2)
78
- return {
79
- "health_pct": round(fe_health, 1),
80
- "health_ratio": round(health_ratio, 4),
81
- "supplied_usd": round(supplied_usd, 2),
82
- "debt_usd": round(debt_usd, 2),
83
- "equity": round(equity, 2),
84
- "max_debt": round(max(0, max_mult * equity), 2),
85
- "raw_usdc": round(raw_usdc, 2),
86
- "has_gmx": has_gmx,
87
- "has_lb": has_lb,
88
- "has_aero": has_aero,
89
- "action": "computed from defi --json health_pct",
90
- }
91
68
  else:
92
69
  supplied = defi_data.get("supplied", [])
93
70
  borrowed = defi_data.get("borrowed", [])
@@ -107,12 +84,48 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
107
84
  "error": "equity near zero",
108
85
  }
109
86
 
110
- max_debt = round(max_mult * equity, 2) # frontend formula: max debt before liquidation
87
+ # ── Cross-margin formula (per-asset borrowing powers) ─────────────
88
+ if per_asset_powers is not None:
89
+ powers: dict[str, int] = per_asset_powers
90
+ sum_cw = 0.0 # SigmaCw
91
+ sum_bw = 0.0 # SigmaBw
92
+ total_debt = 0.0
93
+
94
+ for s in supplied:
95
+ sym = s.get("symbol", "")
96
+ usd_val = s.get("usd", 0) or 0
97
+ p = powers.get(sym, max_mult)
98
+ pr = p / (p + 1)
99
+ sum_cw += usd_val * pr
100
+
101
+ for b in borrowed:
102
+ sym = b.get("symbol", "")
103
+ usd_val = b.get("usd", 0) or 0
104
+ p = powers.get(sym, max_mult)
105
+ pr = p / (p + 1)
106
+ sum_bw += usd_val * pr
107
+ total_debt += usd_val
108
+
109
+ if sum_cw > 0.01:
110
+ # H = (SigmaCw + SigmaBw - B) / SigmaCw * 100
111
+ health_pct = max(0.0, (sum_cw + sum_bw - total_debt) / sum_cw * 100.0)
112
+ else:
113
+ health_pct = 0.0
114
+
115
+ max_debt = round(max_mult * equity, 2)
116
+
117
+ # ── Simplified formula fallback (uniform borrowing power) ─────────
118
+ else:
119
+ max_debt = round(max_mult * equity, 2)
120
+
121
+ if max_debt > 0.01 and debt_usd >= 0:
122
+ health_pct = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / max_debt))
123
+ else:
124
+ health_pct = 100.0
111
125
 
112
- # Raw USDC in account
126
+ # Common features regardless of formula variant
113
127
  raw_usdc = sum(s.get("usd", 0) for s in supplied if s.get("symbol") == "USDC")
114
128
 
115
- # Position type detection
116
129
  symbols = [s.get("symbol", "") for s in supplied]
117
130
  has_gmx = sum(s.get("usd", 0) for s in supplied if "GM_" in s.get("symbol", "")) > 1.0
118
131
  has_lb = any(
@@ -122,11 +135,6 @@ def compute_health(defi_data: dict, max_mult: int = 10) -> dict:
122
135
  )
123
136
  has_aero = any("AERO" in sym.upper() or "CL_POSITION" in sym.upper() for sym in symbols)
124
137
 
125
- if max_debt > 0.01 and debt_usd >= 0:
126
- health_pct = max(0.0, 100.0 * (1.0 - round(debt_usd, 2) / max_debt))
127
- else:
128
- health_pct = 100.0
129
-
130
138
  # Center target (50% health): target_debt = max_debt * 0.5
131
139
  delta_debt = (max_debt * 0.5) - debt_usd
132
140
 
@@ -560,12 +568,52 @@ def run_tick(
560
568
  deployed_fail += 1
561
569
 
562
570
  elif pos_type == "lb":
563
- # LB deposits need pair + amount-x + amount-y (not a single amount),
564
- # so just leave as USDC for now — manual deployment required.
565
- result["action"] = f"lb-add needs pair + dual amounts — leaving ${split_amt:.2f} as USDC"
571
+ # Detect LB pair from defi data (look for "TraderJoe V2 LB" group)
572
+ lb_pairs = []
573
+ for g in defi_data.get("groups", []):
574
+ if g.get("type") == "TraderJoe V2 LB":
575
+ for item in g.get("items", []):
576
+ label = item.get("label", "")
577
+ m = re.match(r'\[([^\]]+)\]', label)
578
+ if m:
579
+ lb_pairs.append(m.group(1))
580
+
581
+ # Skip if tool doesn't support lb-add (degenprime)
582
+ tool_bn = os.path.basename(tool_path) if tool_path else ""
583
+ if "degenprime" in tool_bn:
584
+ result["action"] = f"lb-add not available on degenprime — leaving ${split_amt:.2f} as USDC"
585
+ deployed_fail += 1
586
+ elif not lb_pairs:
587
+ result["warning"] = f"has_lb=True but no LB pair found in defi data — leaving ${split_amt:.2f} as USDC"
588
+ deployed_fail += 1
589
+ else:
590
+ pair_key = lb_pairs[0]
591
+ try:
592
+ r = subprocess.run(
593
+ [sys.executable, tool_path, "lb-add",
594
+ "--pair", pair_key,
595
+ "--amount-x", "0",
596
+ "--amount-y", f"{split_amt:.2f}",
597
+ "--shape", "spot",
598
+ "--range", "15",
599
+ "--execute"],
600
+ capture_output=True, text=True, timeout=120,
601
+ )
602
+ if r.returncode == 0:
603
+ deployed_ok += 1
604
+ else:
605
+ result["warning"] = f"lb-add failed: {r.stderr[:200]}"
606
+ deployed_fail += 1
607
+ except Exception as e:
608
+ result["error"] = f"lb-add error: {e}"
609
+ deployed_fail += 1
566
610
 
567
611
  elif pos_type == "aero":
568
- result["action"] = f"aero deposit not yet supported by tool — leaving ${split_amt:.2f} as USDC"
612
+ # Aerodrome CL: degenprime has read-only aerodrome-positions,
613
+ # but no deposit/withdraw commands yet (write paths deferred to
614
+ # v2 — on-chain signatures vary by Aerodrome version).
615
+ # Use `degenprime aerodrome-positions` to list your NFT tokenIds.
616
+ result["action"] = f"aero deposit not yet supported (read-only via aerodrome-positions, writes deferred to v2) — leaving ${split_amt:.2f} as USDC"
569
617
 
570
618
  if deployed_ok > 0:
571
619
  cooldown_file.write_text(str(int(time.time())))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: primecli
3
- Version: 0.5.8
3
+ Version: 0.5.9
4
4
  Summary: Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required.
5
5
  Author: Mnemosyne-quest contributors
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "primecli"
7
- version = "0.5.8"
7
+ version = "0.5.9"
8
8
  description = "Agent-friendly CLI tools for the DeltaPrime (Avalanche + Arbitrum) and DegenPrime (Base) lending and leverage protocols. Preview-by-default; no Etherscan key required."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes
File without changes