-
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
424 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,210 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import subprocess | ||
import argparse | ||
import json | ||
from datetime import datetime | ||
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 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('--signet', action='store_true', help='Run on signet') | ||
parser.add_argument('--regtest', action='store_true', help='Run on regtest') | ||
parser.add_argument('--network', help='Set the network explicitly') | ||
|
||
parser.add_argument('nodeid', nargs='?', help='The node ID to pass to clboss-earnings-history (optional)') | ||
|
||
args = parser.parse_args() | ||
|
||
# Reconcile network option | ||
if args.network: | ||
network_option = f'--network={args.network}' | ||
elif args.testnet: | ||
network_option = '--network=testnet' | ||
elif args.signet: | ||
network_option = '--network=signet' | ||
elif args.regtest: | ||
network_option = '--network=regtest' | ||
else: | ||
network_option = '--network=bitcoin' # lightning-cli wants "bitcoin" for mainnet | ||
|
||
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_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 | ||
|
||
# Process and format data | ||
rows = [] | ||
for entry in earnings_data['history']: | ||
in_earnings = entry['in_earnings'] | ||
in_forwarded = entry['in_forwarded'] | ||
in_expenditures = entry['in_expenditures'] | ||
in_rebalanced = entry['in_rebalanced'] | ||
|
||
out_earnings = entry['out_earnings'] | ||
out_forwarded = entry['out_forwarded'] | ||
out_expenditures = entry['out_expenditures'] | ||
out_rebalanced = entry['out_rebalanced'] | ||
|
||
# Calculate rates with checks for division by zero | ||
in_forwarded_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_forwarded_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 | ||
|
||
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 | ||
|
||
if args.nodeid: | ||
net_earnings = (in_earnings + out_earnings) - (in_expenditures + out_expenditures) | ||
else: | ||
net_earnings = in_earnings - in_expenditures | ||
total_net_earnings += net_earnings | ||
|
||
if args.nodeid: | ||
rows.append([ | ||
format_bucket_time(entry['bucket_time']), | ||
f"{in_forwarded:,}".replace(',', '_'), | ||
f"{in_forwarded_rate:,.0f}", | ||
f"{in_earnings:,}".replace(',', '_'), | ||
f"{out_forwarded:,}".replace(',', '_'), | ||
f"{out_forwarded_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"{int(net_earnings):,}".replace(',', '_') | ||
]) | ||
else: | ||
rows.append([ | ||
format_bucket_time(entry['bucket_time']), | ||
f"{in_forwarded:,}".replace(',', '_'), | ||
f"{in_forwarded_rate:,.0f}", | ||
f"{in_earnings:,}".replace(',', '_'), | ||
f"{in_rebalanced:,}".replace(',', '_'), | ||
f"{in_rebalance_rate:,.0f}", | ||
f"{in_expenditures:,}".replace(',', '_'), | ||
f"{int(net_earnings):,}".replace(',', '_') | ||
]) | ||
|
||
# Add a header (separator) row | ||
if args.nodeid: | ||
# Show both the incoming and outgoing statistics | ||
headers = [ | ||
"Date", | ||
"In Forwarded", | ||
"PPM", | ||
"In Earnings", | ||
"Out Forwarded", | ||
"PPM", | ||
"Out Earnings", | ||
"In Rebalanced", | ||
"PPM", | ||
"In Expense", | ||
"Out Rebalanced", | ||
"PPM", | ||
"Out Expense", | ||
"Net Earnings" | ||
] | ||
else: | ||
# Incoming and outgoing are always the same (balanced) | ||
headers = [ | ||
"Date", | ||
"Forwarded", | ||
"PPM", | ||
"Earnings", | ||
"Rebalanced", | ||
"PPM", | ||
"Expense", | ||
"Net Earnings" | ||
] | ||
|
||
# Make a separator row before the totals | ||
rows.append(["-" * len(header) for header in headers]) | ||
|
||
# Append the total row | ||
if args.nodeid: | ||
# Show both the incoming and outgoing statistics | ||
rows.append([ | ||
"TOTAL", | ||
f"{total_in_forwarded:,}".replace(',', '_'), | ||
# misleading because legacy numerator only: f"{total_in_forwarded_rate:,.0f}", | ||
f"", | ||
f"{total_in_earnings:,}".replace(',', '_'), | ||
f"{total_out_forwarded:,}".replace(',', '_'), | ||
# misleading because legacy numerator only: f"{total_out_forwarded_rate:,.0f}", | ||
f"", | ||
f"{total_out_earnings:,}".replace(',', '_'), | ||
f"{total_in_rebalanced:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_in_rebalance_rate:,.0f}", | ||
f"", | ||
f"{total_in_expenditures:,}".replace(',', '_'), | ||
f"{total_out_rebalanced:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_out_rebalance_rate:,.0f}", | ||
f"", | ||
f"{total_out_expenditures:,}".replace(',', '_'), | ||
f"{int(total_net_earnings):,}".replace(',', '_') | ||
]) | ||
else: | ||
# Incoming and outgoing are always the same (balanced) | ||
rows.append([ | ||
"TOTAL", | ||
f"{total_in_forwarded:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_in_forwarded_rate:,.0f}", | ||
f"", | ||
f"{total_in_earnings:,}".replace(',', '_'), | ||
f"{total_in_rebalanced:,}".replace(',', '_'), | ||
# misleading because legacy: f"{total_in_rebalance_rate:,.0f}", | ||
f"", | ||
f"{total_in_expenditures:,}".replace(',', '_'), | ||
f"{int(total_net_earnings):,}".replace(',', '_') | ||
]) | ||
|
||
if args.nodeid: | ||
alias = lookup_alias(run_lightning_cli_command, network_option, args.nodeid) | ||
print(f"Showing history for {args.nodeid} aka {alias}") | ||
|
||
|
||
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,171 @@ | ||
#!/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 | ||
|
||
headers = [ | ||
"Alias", | ||
"In Forwarded", | ||
"PPM", | ||
"In Earn", | ||
"Out Forwarded", | ||
"PPM", | ||
"Out Earn", | ||
"In Rebal", | ||
"PPM", | ||
"In Exp", | ||
"Out Rebal", | ||
"PPM", | ||
"Out Exp", | ||
"Net Earn", | ||
] | ||
|
||
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_forwarded_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_forwarded_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 | ||
|
||
rows.append([ | ||
alias, | ||
f"{in_forwarded:,}".replace(',', '_'), | ||
f"{in_forwarded_rate:,.0f}", | ||
f"{in_earnings:,}".replace(',', '_'), | ||
f"{out_forwarded:,}".replace(',', '_'), | ||
f"{out_forwarded_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(',', '_'), | ||
]) | ||
|
||
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 | ||
|
||
# Divide the net earnings total by 2 | ||
total_net_earnings /= 2 | ||
|
||
# Add a separator row | ||
rows.append(["-" * len(header) for header in headers]) | ||
|
||
# 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('--signet', action='store_true', help='Run on signet') | ||
parser.add_argument('--regtest', action='store_true', help='Run on regtest') | ||
parser.add_argument('--network', help='Set the network explicitly') | ||
|
||
parser.add_argument('days', nargs='?', help='The number of days to pass to clboss-earnings-history (optional)') | ||
|
||
args = parser.parse_args() | ||
|
||
# Reconcile network option | ||
if args.network: | ||
network_option = f'--network={args.network}' | ||
elif args.testnet: | ||
network_option = '--network=testnet' | ||
elif args.signet: | ||
network_option = '--network=signet' | ||
elif args.regtest: | ||
network_option = '--network=regtest' | ||
else: | ||
network_option = '--network=bitcoin' # lightning-cli wants "bitcoin" for mainnet | ||
|
||
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=headers, tablefmt="pretty", stralign="right", numalign="right")) | ||
|
||
if __name__ == "__main__": | ||
main() |
Empty file.
Oops, something went wrong.