You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
132 lines
5.2 KiB
132 lines
5.2 KiB
#!/usr/bin/env python3 |
|
import sys |
|
from datetime import datetime |
|
from decimal import Decimal |
|
from json import loads |
|
from optparse import OptionParser |
|
|
|
from jmbase import EXIT_ARGERROR, jmprint, get_log, utxostr_to_utxo, EXIT_FAILURE |
|
from jmbitcoin import amount_to_sat, sat_to_btc |
|
from jmclient import add_base_options, load_program_config, jm_single, get_bond_values |
|
|
|
DESCRIPTION = """Given either a Bitcoin UTXO in the form TXID:n |
|
(e.g., 0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098:0) |
|
or an amount in either satoshi or bitcoin (e.g., 150000, 0.1, 10.123, 10btc), |
|
calculate fidelity bond values for all possible locktimes in a one-year period |
|
(12 months, you can change that with the `-m --months` option). |
|
By default it uses the values from your joinmarket.cfg, |
|
you can override these with the `-i --interest` and `-e --exponent` options. |
|
Additionally, you can export the orderbook from ob-watcher.py and use the data here |
|
with the `-o --orderbook` option, this will compare the results from this script |
|
with the fidelity bonds in the orderbook. |
|
""" |
|
|
|
log = get_log() |
|
|
|
|
|
def main() -> None: |
|
parser = OptionParser( |
|
usage="usage: %prog [options] UTXO or amount", |
|
description=DESCRIPTION, |
|
) |
|
add_base_options(parser) |
|
parser.add_option( |
|
"-i", |
|
"--interest", |
|
action="store", |
|
type="float", |
|
dest="interest", |
|
help="Interest rate to use for fidelity bond calculation (instead of interest_rate config)", |
|
) |
|
parser.add_option( |
|
"-e", |
|
"--exponent", |
|
action="store", |
|
type="float", |
|
dest="exponent", |
|
help="Exponent to use for fidelity bond calculation (instead of bond_value_exponent config)", |
|
) |
|
parser.add_option( |
|
"-m", |
|
"--months", |
|
action="store", |
|
type="int", |
|
dest="months", |
|
help="For how many months to calculate the fidelity bond values, each month has its own stats (default 12)", |
|
default=12, |
|
) |
|
parser.add_option( |
|
"-o", |
|
"--orderbook", |
|
action="store", |
|
type="str", |
|
dest="path_to_json", |
|
help="Path to the exported orderbook in JSON format", |
|
) |
|
|
|
options, args = parser.parse_args() |
|
load_program_config(config_path=options.datadir) |
|
if len(args) != 1: |
|
log.error("Invalid arguments, see --help") |
|
sys.exit(EXIT_ARGERROR) |
|
if options.path_to_json: |
|
try: |
|
with open(options.path_to_json, "r", encoding="UTF-8") as orderbook: |
|
orderbook = loads(orderbook.read()) |
|
except FileNotFoundError as exc: |
|
log.error(exc) |
|
sys.exit(EXIT_ARGERROR) |
|
else: |
|
orderbook = None |
|
try: |
|
amount = amount_to_sat(args[0]) |
|
confirm_time = None |
|
except ValueError: |
|
# If it's not a valid amount then it has to be a UTXO |
|
if jm_single().bc_interface is None: |
|
log.error("For calculation based on UTXO access to Bitcoin Core is required") |
|
sys.exit(EXIT_FAILURE) |
|
success, utxo = utxostr_to_utxo(args[0]) |
|
if not success: |
|
# utxo contains the error message |
|
log.error(utxo) |
|
sys.exit(EXIT_ARGERROR) |
|
utxo_data = jm_single().bc_interface.query_utxo_set(utxo, includeconfs=True)[0] |
|
amount = utxo_data["value"] |
|
if utxo_data["confirms"] == 0: |
|
log.warning("Given UTXO is unconfirmed, current time will be used as confirmation time") |
|
confirm_time = None |
|
elif utxo_data["confirms"] < 0: |
|
log.error("Given UTXO is invalid, reason: conflicted") |
|
sys.exit(EXIT_ARGERROR) |
|
else: |
|
current_height = jm_single().bc_interface.get_current_block_height() |
|
block_hash = jm_single().bc_interface.get_block_hash(current_height - utxo_data["confirms"] + 1) |
|
confirm_time = jm_single().bc_interface.get_block_time(block_hash) |
|
|
|
parameters, results = get_bond_values(amount, |
|
options.months, |
|
confirm_time, |
|
options.interest, |
|
options.exponent, |
|
orderbook) |
|
jmprint(f"Amount locked: {amount} ({sat_to_btc(amount)} btc)") |
|
jmprint(f"Confirmation time: {datetime.fromtimestamp(parameters['confirm_time'])}") |
|
jmprint(f"Interest rate: {parameters['interest']} ({parameters['interest'] * 100}%)") |
|
jmprint(f"Exponent: {parameters['exponent']}") |
|
jmprint(f"\nFIDELITY BOND VALUES (BTC^{parameters['exponent']})") |
|
jmprint("\nSee /docs/fidelity-bonds.md for complete formula and more") |
|
|
|
for result in results: |
|
locktime = datetime.fromtimestamp(result["locktime"]) |
|
# Mimic the locktime value the user would have to insert to create such fidelity bond |
|
jmprint(f"\nLocktime: {locktime.year}-{locktime.month}") |
|
# Mimic orderbook value |
|
jmprint(f"Bond value: {float(Decimal(result['value']) / Decimal(1e16)):.16f}") |
|
if options.path_to_json: |
|
jmprint(f"Weight: {result['weight']:.5f} ({result['weight'] * 100:.2f}% of all bonds)") |
|
jmprint(f"Top {result['percentile']}% of the orderbook by value") |
|
|
|
|
|
if __name__ == "__main__": |
|
main()
|
|
|