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.
- {primecli-0.5.8 → primecli-0.5.9}/PKG-INFO +1 -1
- {primecli-0.5.8 → primecli-0.5.9}/primecli/health_monitor.py +98 -50
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/PKG-INFO +1 -1
- {primecli-0.5.8 → primecli-0.5.9}/pyproject.toml +1 -1
- {primecli-0.5.8 → primecli-0.5.9}/LICENSE +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/README.md +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli/__init__.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli/arbprime.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli/degenprime.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli/deltaprime.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/SOURCES.txt +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/dependency_links.txt +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/entry_points.txt +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/requires.txt +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/primecli.egg-info/top_level.txt +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/setup.cfg +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_cross_file_identity.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_gas_pricing.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_health_monitor.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_paraswap_validator.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_redstone_encoding.py +0 -0
- {primecli-0.5.8 → primecli-0.5.9}/tests/test_to_wei_units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: primecli
|
|
3
|
-
Version: 0.5.
|
|
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(
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
564
|
-
|
|
565
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|