-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathminipools.py
More file actions
executable file
·147 lines (120 loc) · 4.76 KB
/
minipools.py
File metadata and controls
executable file
·147 lines (120 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env nix-shell
#!nix-shell -i python3 -p "python3.withPackages(ps: [ ps.matplotlib ])"
import datetime
import json
import gzip
import os
import shutil
import sys
import time
import urllib.request
# Graphing
import matplotlib
import matplotlib.pyplot as plt
params = {
8: { 'step': 1 },
16: { 'step': 2 },
}
min_collat_percent = 10
dark = "#fa9b02"
light = "#f7b852"
refresh_period_hours = 23
data_file = "./minipools.json.gz"
# Make output SVG deterministic to avoid useless diffs
matplotlib.rcParams['svg.hashsalt'] = "RPL"
def update():
refresh_data_file()
stats = load_data()
generate_page(stats)
def file_age_hours(f):
age_seconds = time.time() - os.path.getmtime(data_file)
return age_seconds / 60 / 60
def refresh_data_file():
if not os.path.exists(data_file) or file_age_hours(data_file) > refresh_period_hours:
print("Retrieving minipool data", file=sys.stderr)
headers = { 'Accept-Encoding': 'gzip' }
req = urllib.request.Request('https://rocketscan.io/api/mainnet/minipools/all', headers=headers)
with urllib.request.urlopen(req) as response, open(data_file, 'wb') as out_file:
shutil.copyfileobj(response, out_file)
def load_data():
with gzip.open(data_file, 'r') as f:
data = json.load(f)
stats = {
8: { 'count': 0, 'total': 0, 'all': [], 'total_effective': 0, 'collateral': [0] * (1 + 50 // params[8]['step']) },
16: { 'count': 0, 'total': 0, 'all': [], 'total_effective': 0, 'collateral': [0] * (1 + 150 // params[16]['step']), }
}
for minipool in data:
#print(minipool, file=sys.stderr)
leb = int(minipool['nodeDepositBalance']) // 10**18
node = minipool['node']
stake = int(node['rplStake'])
min_stake = int(node['rplMinStake'])
if min_stake == 0: continue
collat = stake/min_stake * min_collat_percent / 100
borrowed = 32 - leb
stats[leb]['total'] += collat
stats[leb]['count'] += 1
stats[leb]['all'].append(collat)
collat = min(collat, 1.5 * leb / borrowed)
stats[leb]['total_effective'] += collat
collat_perc = int(100*collat / params[leb]['step'])
stats[leb]['collateral'][collat_perc] += borrowed
return stats
def generate_page(stats):
for kind in [8, 16]:
s = stats[kind]
collateralization = s['collateral']
step = params[kind]['step']
average = 100 * s['total'] / s['count']
average_effective = 100 * s['total_effective'] / s['count']
median = 100 * sorted(s['all'])[len(s['all'])//2]
x=[]
y=[]
color=[]
# Create the figure and axes objects, specify the size and the dots per inches
fig, ax = plt.subplots(figsize=(15, 4))
start = min([coll for coll, count in enumerate(collateralization) if count > 0])
for coll, count in list(enumerate(collateralization))[start:]:
ratio = step * coll
x.append(ratio)
y.append(count)
color.append([light, dark][ratio >= min_collat_percent])
ax.bar(x, y, color=color, width=step, zorder=2)
ax.grid(which="major", axis='x', color='#DAD8D7', alpha=0.5, zorder=1)
ax.grid(which="major", axis='y', color='#DAD8D7', alpha=0.5, zorder=1)
plt.axvline(x=average, label='average', linestyle='dotted')
matplotlib.pyplot.text(average + max(x) / 100 / 2, max(y) / 2, "average", rotation=90, verticalalignment='center')
#plt.axvline(x=average_effective, label='average effective', linestyle='dotted')
#matplotlib.pyplot.text(average_effective + max(x) / 100 / 2, max(y) / 2, "average effective", rotation=90, verticalalignment='center')
plt.axvline(x=median, label='median', linestyle='dotted')
matplotlib.pyplot.text(median + max(x) / 100 / 2, max(y) / 2, "median", rotation=90, verticalalignment='center')
plt.xlabel("Staked RPL vs borrowed ETH")
# Add a '+' to the last ticker, e.g. "50%+" as the last ticker for LEB8, since it aggregates all levels above
@matplotlib.ticker.FuncFormatter
def major_formatter(tick, pos):
res = f'{tick:.0f}%'
if tick == x[-1]:
res += '+'
return res
ax.xaxis.set_major_formatter(major_formatter)
ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=5 * step))
plt.ylabel("Borrowed ETH")
ax.yaxis.set_major_formatter(matplotlib.ticker.StrMethodFormatter('Ξ{x:,.0f}'))
plt.title(f"{kind} ETH Minipools", fontsize=14, weight='bold', alpha=.8)
plt.savefig(f"../harpocryptes.github.io/minipools-{kind}eth.svg")
data_time = datetime.datetime.fromtimestamp(os.path.getmtime(data_file), datetime.timezone.utc).strftime('%Y-%m-%d %H:%M %Z')
with open("../harpocryptes.github.io/index.html", "w") as f:
f.write(f"""\
<html>
<body>
<div style="text-align: center">
<img src = "minipools-8eth.svg" alt="8 ETH Minipools"/ style="padding: 1em">
<img src = "minipools-16eth.svg" alt="16 ETH Minipools"/ style="padding: 1em">
</div>
<div style="text-align: right; color: gray">
Data retrieved from rocketscan.io/api at {data_time}
</div>
</body>
</html>
""")
update()