-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
contrib: add clboss-earnings-history and clboss-recent-earnings scripts
- Loading branch information
Showing
5 changed files
with
340 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
clboss | ||
/clboss | ||
create-tarball | ||
dev-boltz-api | ||
dev-boltz | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import subprocess | ||
import argparse | ||
import json | ||
from datetime import datetime | ||
from tabulate import tabulate | ||
|
||
def run_lightning_cli_command(network_option, command, *args): | ||
try: | ||
result = subprocess.run(['lightning-cli', network_option, command, *args], capture_output=True, text=True, check=True) | ||
return json.loads(result.stdout) | ||
except subprocess.CalledProcessError as e: | ||
print(f"Command '{command}' failed with error: {e}") | ||
except json.JSONDecodeError as e: | ||
print(f"Failed to parse JSON from command '{command}': {e}") | ||
return None | ||
|
||
def format_bucket_time(bucket_time): | ||
if bucket_time == 0: | ||
return "Legacy" | ||
else: | ||
return datetime.utcfromtimestamp(bucket_time).strftime('%Y-%m-%d') | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description="Run lightning-cli with specified network") | ||
parser.add_argument('--mainnet', action='store_true', help='Run on mainnet') | ||
parser.add_argument('--testnet', action='store_true', help='Run on testnet') | ||
parser.add_argument('nodeid', nargs='?', help='The node ID to pass to clboss-earnings-history (optional)') | ||
|
||
args = parser.parse_args() | ||
|
||
if args.testnet: | ||
network_option = '--testnet' | ||
else: | ||
network_option = '--mainnet' # Default to mainnet if no option is specified | ||
|
||
if args.nodeid: | ||
earnings_data = run_lightning_cli_command(network_option, 'clboss-earnings-history', args.nodeid) | ||
else: | ||
earnings_data = run_lightning_cli_command(network_option, 'clboss-earnings-history') | ||
|
||
# Initialize totals | ||
total_forwarded = 0 | ||
total_earnings = 0 | ||
total_rebalanced = 0 | ||
total_expense = 0 | ||
total_net_earnings = 0 | ||
|
||
# Process and format data | ||
rows = [] | ||
for entry in earnings_data['history']: | ||
if args.nodeid: | ||
# Sum "in" and "out" values | ||
earnings = entry['in_earnings'] + entry['out_earnings'] | ||
forwarded = entry['in_forwarded'] + entry['out_forwarded'] | ||
expense = entry['in_expenditures'] + entry['out_expenditures'] | ||
rebalanced = entry['in_rebalanced'] + entry['out_rebalanced'] | ||
else: | ||
# Just use the in values, they are symetrical | ||
earnings = entry['in_earnings'] | ||
forwarded = entry['in_forwarded'] | ||
expense = entry['in_expenditures'] | ||
rebalanced = entry['in_rebalanced'] | ||
|
||
# Calculate rates with checks for division by zero | ||
forwarded_rate = (earnings / forwarded) * 1_000_000 if forwarded != 0 else 0 | ||
rebalance_rate = (expense / rebalanced) * 1_000_000 if rebalanced != 0 else 0 | ||
net_earnings = earnings - expense | ||
|
||
# Update totals | ||
total_forwarded += forwarded | ||
total_earnings += earnings | ||
total_rebalanced += rebalanced | ||
total_expense += expense | ||
total_net_earnings += net_earnings | ||
|
||
rows.append([ | ||
format_bucket_time(entry['bucket_time']), | ||
f"{forwarded:,}".replace(',', '_'), | ||
f"{forwarded_rate:,.0f}", | ||
f"{earnings:,}".replace(',', '_'), | ||
f"{rebalanced:,}".replace(',', '_'), | ||
f"{rebalance_rate:,.0f}", | ||
f"{expense:,}".replace(',', '_'), | ||
f"{int(net_earnings):,}".replace(',', '_') | ||
]) | ||
|
||
# Calculate total rates | ||
total_forwarded_rate = (total_earnings / total_forwarded) * 1_000_000 if total_forwarded != 0 else 0 | ||
total_rebalance_rate = (total_expense / total_rebalanced) * 1_000_000 if total_rebalanced != 0 else 0 | ||
|
||
# Add a separator row | ||
separator_row = ["-" * len(header) for header in ["Date", "Forwarded", "Rate", "Earnings", "Rebalanced", "Rate", "Expense", "Net Earnings"]] | ||
rows.append(separator_row) | ||
|
||
# Append the total row | ||
rows.append([ | ||
"TOTAL", | ||
f"{total_forwarded:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_forwarded_rate:,.0f}", | ||
f"", | ||
f"{total_earnings:,}".replace(',', '_'), | ||
f"{total_rebalanced:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_rebalance_rate:,.0f}", | ||
f"", | ||
f"{total_expense:,}".replace(',', '_'), | ||
f"{int(total_net_earnings):,}".replace(',', '_') | ||
]) | ||
|
||
headers = [ | ||
"Date", | ||
"Forwarded", | ||
"Rate", | ||
"Earnings", | ||
"Rebalanced", | ||
"Rate", | ||
"Expense", | ||
"Net Earnings" | ||
] | ||
|
||
print(tabulate(rows, headers=headers, tablefmt="pretty", stralign="right", numalign="right")) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import os | ||
import subprocess | ||
import argparse | ||
import json | ||
from tabulate import tabulate | ||
from clboss.alias_cache import lookup_alias | ||
|
||
def run_lightning_cli_command(network_option, command, *args): | ||
try: | ||
result = subprocess.run(['lightning-cli', network_option, command, *args], capture_output=True, text=True, check=True) | ||
return json.loads(result.stdout) | ||
except subprocess.CalledProcessError as e: | ||
print(f"Command '{command}' failed with error: {e}") | ||
except json.JSONDecodeError as e: | ||
print(f"Failed to parse JSON from command '{command}': {e}") | ||
return None | ||
|
||
def calculate_net_earnings(data, network_option): | ||
rows = [] | ||
|
||
# Initialize totals | ||
total_net_earnings = 0 | ||
total_in_earnings = 0 | ||
total_in_forwarded = 0 | ||
total_in_expenditures = 0 | ||
total_in_rebalanced = 0 | ||
total_out_earnings = 0 | ||
total_out_forwarded = 0 | ||
total_out_expenditures = 0 | ||
total_out_rebalanced = 0 | ||
|
||
for node, stats in data['recent'].items(): | ||
in_earnings = stats['in_earnings'] | ||
in_forwarded = stats['in_forwarded'] | ||
in_expenditures = stats['in_expenditures'] | ||
in_rebalanced = stats['in_rebalanced'] | ||
|
||
out_earnings = stats['out_earnings'] | ||
out_forwarded = stats['out_forwarded'] | ||
out_expenditures = stats['out_expenditures'] | ||
out_rebalanced = stats['out_rebalanced'] | ||
|
||
# Skip rows where all values are zero | ||
if ( | ||
in_earnings == 0 and in_forwarded == 0 and in_expenditures == 0 and in_rebalanced == 0 and | ||
out_earnings == 0 and out_forwarded == 0 and out_expenditures == 0 and out_rebalanced == 0 | ||
): | ||
continue | ||
alias = lookup_alias(run_lightning_cli_command, network_option, node) | ||
in_rate = (in_earnings / in_forwarded) * 1_000_000 if in_forwarded != 0 else 0 | ||
in_rebalance_rate = (in_expenditures / in_rebalanced) * 1_000_000 if in_rebalanced != 0 else 0 | ||
out_rate = (out_earnings / out_forwarded) * 1_000_000 if out_forwarded != 0 else 0 | ||
out_rebalance_rate = (out_expenditures / out_rebalanced) * 1_000_000 if out_rebalanced != 0 else 0 | ||
|
||
net_earnings = in_earnings - in_expenditures + out_earnings - out_expenditures | ||
|
||
# Update totals | ||
total_net_earnings += net_earnings | ||
total_in_earnings += in_earnings | ||
total_in_forwarded += in_forwarded | ||
total_in_expenditures += in_expenditures | ||
total_in_rebalanced += in_rebalanced | ||
total_out_earnings += out_earnings | ||
total_out_forwarded += out_forwarded | ||
total_out_expenditures += out_expenditures | ||
total_out_rebalanced += out_rebalanced | ||
|
||
avg_in_earnings_rate = (total_in_earnings / total_in_forwarded) * 1_000_000 if total_in_forwarded != 0 else 0 | ||
avg_out_earnings_rate = (total_out_earnings / total_out_forwarded) * 1_000_000 if total_out_forwarded != 0 else 0 | ||
avg_in_expenditures_rate = (total_in_expenditures / total_in_rebalanced) * 1_000_000 if total_in_rebalanced != 0 else 0 | ||
avg_out_expenditures_rate = (total_out_expenditures / total_out_rebalanced) * 1_000_000 if total_out_rebalanced != 0 else 0 | ||
|
||
rows.append([ | ||
alias, | ||
f"{in_forwarded:,}".replace(',', '_'), | ||
f"{in_rate:,.0f}", | ||
f"{in_earnings:,}".replace(',', '_'), | ||
f"{out_forwarded:,}".replace(',', '_'), | ||
f"{out_rate:,.0f}", | ||
f"{out_earnings:,}".replace(',', '_'), | ||
f"{in_rebalanced:,}".replace(',', '_'), | ||
f"{in_rebalance_rate:,.0f}", | ||
f"{in_expenditures:,}".replace(',', '_'), | ||
f"{out_rebalanced:,}".replace(',', '_'), | ||
f"{out_rebalance_rate:,.0f}", | ||
f"{out_expenditures:,}".replace(',', '_'), | ||
f"{net_earnings:,}".replace(',', '_'), | ||
]) | ||
|
||
# Divide the net earnings total by 2 | ||
total_net_earnings /= 2 | ||
|
||
# Add a separator row | ||
separator_row = ["-" * len(header) for header in [ | ||
"Alias", | ||
"In Forwarded", | ||
"Rate", | ||
"In Earn", | ||
"Out Forwarded", | ||
"Rate", | ||
"Out Earn", | ||
"In Rebal", | ||
"Rate", | ||
"In Exp", | ||
"Out Rebal", | ||
"Rate", | ||
"Out Exp", | ||
"Net Earn", | ||
]] | ||
rows.append(separator_row) | ||
|
||
# Append the total row | ||
rows.append([ | ||
"TOTAL", | ||
f"{total_in_forwarded:,}".replace(',', '_'), | ||
f"{avg_in_earnings_rate:,.0f}", | ||
f"{total_in_earnings:,}".replace(',', '_'), | ||
f"{total_out_forwarded:,}".replace(',', '_'), | ||
f"{avg_out_earnings_rate:,.0f}", | ||
f"{total_out_earnings:,}".replace(',', '_'), | ||
f"{total_in_rebalanced:,}".replace(',', '_'), | ||
f"{avg_in_expenditures_rate:,.0f}", | ||
f"{total_in_expenditures:,}".replace(',', '_'), | ||
f"{total_out_rebalanced:,}".replace(',', '_'), | ||
f"{avg_out_expenditures_rate:,.0f}", | ||
f"{total_out_expenditures:,}".replace(',', '_'), | ||
f"{int(total_net_earnings):,}".replace(',', '_'), | ||
]) | ||
|
||
return rows | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description="Run lightning-cli with specified network") | ||
parser.add_argument('--mainnet', action='store_true', help='Run on mainnet') | ||
parser.add_argument('--testnet', action='store_true', help='Run on testnet') | ||
parser.add_argument('days', nargs='?', help='The number of days to pass to clboss-earnings-history (optional)') | ||
|
||
args = parser.parse_args() | ||
|
||
if args.testnet: | ||
network_option = '--testnet' | ||
else: | ||
network_option = '--mainnet' # Default to mainnet if no option is specified | ||
|
||
if args.days: | ||
earnings_data = run_lightning_cli_command(network_option, 'clboss-recent-earnings', str(args.days)) | ||
else: | ||
earnings_data = run_lightning_cli_command(network_option, 'clboss-recent-earnings') | ||
|
||
if earnings_data: | ||
rows = calculate_net_earnings(earnings_data, network_option) | ||
print(tabulate(rows, headers=[ | ||
"Alias", | ||
"In Forwarded", | ||
"Rate", | ||
"In Earn", | ||
"Out Forwarded", | ||
"Rate", | ||
"Out Earn", | ||
"In Rebal", | ||
"Rate", | ||
"In Exp", | ||
"Out Rebal", | ||
"Rate", | ||
"Out Exp", | ||
"Net Earn", | ||
],tablefmt="pretty", stralign="right", numalign="right")) | ||
|
||
if __name__ == "__main__": | ||
main() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import os | ||
import json | ||
|
||
# Define the cache directory and file path | ||
CACHE_DIR = os.path.join(os.path.expanduser("~"), ".clboss") | ||
CACHE_FILE = os.path.join(CACHE_DIR, "alias_cache.json") | ||
|
||
def load_cache(): | ||
if os.path.exists(CACHE_FILE): | ||
with open(CACHE_FILE, 'r') as f: | ||
return json.load(f) | ||
return {} | ||
|
||
def save_cache(cache): | ||
# Ensure the cache directory exists | ||
if not os.path.exists(CACHE_DIR): | ||
os.makedirs(CACHE_DIR) | ||
|
||
with open(CACHE_FILE, 'w') as f: | ||
json.dump(cache, f) | ||
|
||
def lookup_alias(run_lightning_cli_command, network_option, peer_id): | ||
# Load the cache | ||
cache = load_cache() | ||
|
||
# Check if the alias is already cached | ||
if peer_id in cache: | ||
return cache[peer_id] | ||
|
||
# Perform the lookup | ||
alias = peer_id # Default to peer_id if alias not found | ||
listnodes_data = run_lightning_cli_command(network_option, 'listnodes', peer_id) | ||
if listnodes_data: | ||
nodes = listnodes_data.get("nodes", []) | ||
for node in nodes: | ||
alias = node.get("alias", peer_id) # Fallback to peer_id if alias not found | ||
|
||
# Cache the result | ||
cache[peer_id] = alias | ||
save_cache(cache) | ||
|
||
return alias |