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.
105 lines
4.1 KiB
105 lines
4.1 KiB
""" |
|
Utilities to calculate fidelity bonds values and statistics. |
|
""" |
|
from bisect import bisect_left |
|
from datetime import datetime |
|
from typing import Optional, Dict, Any, Mapping, Tuple, List, Sequence |
|
|
|
from jmclient import FidelityBondMixin, jm_single, get_interest_rate |
|
|
|
|
|
def get_percentiles(data: Sequence[Any]) -> List[Any]: |
|
""" |
|
Custom implementation of statistics.quantiles from Python standard library. |
|
Equivalent to quantiles(data, n=100, method="inclusive") |
|
Used to preserve compatibility with old Python versions. |
|
""" |
|
n = len(data) |
|
if n < 2: |
|
raise ValueError("Not enough data, at least two data points required") |
|
data = sorted(data) |
|
m = n - 1 |
|
result = [] |
|
for i in range(1, 100): |
|
j, delta = divmod(i * m, 100) |
|
interpolated = (data[j] * (100 - delta) + data[j + 1] * delta) / 100 |
|
result.append(interpolated) |
|
return result |
|
|
|
|
|
def get_next_locktime(dt: datetime) -> datetime: |
|
""" |
|
Return the next valid fidelity bond locktime. |
|
""" |
|
year = dt.year + dt.month // 12 |
|
month = dt.month % 12 + 1 |
|
return datetime(year, month, 1) |
|
|
|
|
|
def get_bond_values(amount: int, |
|
months: int, |
|
confirm_time: Optional[float] = None, |
|
interest: Optional[float] = None, |
|
exponent: Optional[float] = None, |
|
orderbook: Optional[Mapping[str, Any]] = None) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]: |
|
""" |
|
Conveniently generate values [and statistics] for multiple possible fidelity bonds. |
|
|
|
Args: |
|
amount: Fidelity bond UTXO amount in satoshi |
|
months: For how many months to calculate the results |
|
confirm_time: Fidelity bond UTXO confirmation time as timestamp, if None, current time is used. |
|
I.e., like if the fidelity bond UTXO with given amount has just confirmed on the blockchain. |
|
interest: Interest rate, if None, value is taken from config |
|
exponent: Exponent, if None, value is taken from config |
|
orderbook: Orderbook data, if given, additional statistics are included in the results. |
|
Returns: |
|
A tuple with 2 elements. |
|
First is a dictionary with all the parameters used to perform fidelity bond calculations. |
|
Second is a list of dictionaries, one for each month, with the results. |
|
""" |
|
current_time = datetime.now().timestamp() |
|
if confirm_time is None: |
|
confirm_time = current_time |
|
if interest is None: |
|
interest = get_interest_rate() |
|
if exponent is None: |
|
exponent = jm_single().config.getfloat("POLICY", "bond_value_exponent") |
|
use_config_exp = True |
|
else: |
|
old_exponent = jm_single().config.get("POLICY", "bond_value_exponent") |
|
jm_single().config.set("POLICY", "bond_value_exponent", str(exponent)) |
|
use_config_exp = False |
|
if orderbook: |
|
bond_values = [fb["bond_value"] for fb in orderbook["fidelitybonds"]] |
|
bonds_sum = sum(bond_values) |
|
percentiles = get_percentiles(bond_values) |
|
|
|
parameters = { |
|
"amount": amount, |
|
"confirm_time": confirm_time, |
|
"current_time": current_time, |
|
"interest": interest, |
|
"exponent": exponent, |
|
} |
|
locktime = get_next_locktime(datetime.fromtimestamp(current_time)) |
|
results = [] |
|
for _ in range(months): |
|
fb_value = FidelityBondMixin.calculate_timelocked_fidelity_bond_value( |
|
amount, |
|
confirm_time, |
|
locktime.timestamp(), |
|
current_time, |
|
interest, |
|
) |
|
result = {"locktime": locktime.timestamp(), |
|
"value": fb_value} |
|
if orderbook: |
|
result["weight"] = fb_value / (bonds_sum + fb_value) |
|
result["percentile"] = 100 - bisect_left(percentiles, fb_value) |
|
results.append(result) |
|
locktime = get_next_locktime(locktime) |
|
if not use_config_exp: |
|
# We don't want the modified exponent value to persist in memory, so we reset to whatever it was before |
|
jm_single().config.set("POLICY", "bond_value_exponent", old_exponent) |
|
return parameters, results
|
|
|