URI: 
       tgetfeerate command: add optional parameters to specify custom fee level (#4264) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit a98e833897dc11ec7ff8c854fe512a3fc549f6c7
   DIR parent 2cc15fca578b0ee6e87c1429d8afeedc4a7e156a
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Fri, 15 Jun 2018 17:02:44 +0200
       
       getfeerate command: add optional parameters to specify custom fee level (#4264)
       
       
       Diffstat:
         M lib/commands.py                     |      25 +++++++++++++++++++++----
         M lib/daemon.py                       |       2 ++
         M lib/simple_config.py                |      46 ++++++++++++++++++++++++-------
       
       3 files changed, 59 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -657,10 +657,23 @@ class Commands:
                return self.wallet.is_up_to_date()
        
            @command('n')
       -    def getfeerate(self):
       -        """Return current optimal fee rate per kilobyte, according
       -        to config settings (static/dynamic)"""
       -        return self.config.fee_per_kb()
       +    def getfeerate(self, fee_method=None, fee_level=None):
       +        """Return current suggested fee rate (in sat/kvByte), according to config
       +        settings or supplied parameters.
       +        """
       +        if fee_method is None:
       +            dyn, mempool = None, None
       +        elif fee_method.lower() == 'static':
       +            dyn, mempool = False, False
       +        elif fee_method.lower() == 'eta':
       +            dyn, mempool = True, False
       +        elif fee_method.lower() == 'mempool':
       +            dyn, mempool = True, True
       +        else:
       +            raise Exception('Invalid fee estimation method: {}'.format(fee_method))
       +        if fee_level is not None:
       +            fee_level = Decimal(fee_level)
       +        return self.config.fee_per_kb(dyn=dyn, mempool=mempool, fee_level=fee_level)
        
            @command('')
            def help(self):
       t@@ -719,6 +732,8 @@ command_options = {
            'show_addresses': (None, "Show input and output addresses"),
            'show_fiat':   (None, "Show fiat value of transactions"),
            'year':        (None, "Show history for a given year"),
       +    'fee_method':  (None, "Fee estimation method to use"),
       +    'fee_level':   (None, "Float between 0.0 and 1.0, representing fee slider position")
        }
        
        
       t@@ -738,6 +753,8 @@ arg_types = {
            'fee': lambda x: str(Decimal(x)) if x is not None else None,
            'amount': lambda x: str(Decimal(x)) if x != '!' else '!',
            'locktime': int,
       +    'fee_method': str,
       +    'fee_level': json_loads,
        }
        
        config_variables = {
   DIR diff --git a/lib/daemon.py b/lib/daemon.py
       t@@ -260,7 +260,9 @@ class Daemon(DaemonThread):
                password = config_options.get('password')
                new_password = config_options.get('new_password')
                config = SimpleConfig(config_options)
       +        # FIXME this is ugly...
                config.fee_estimates = self.network.config.fee_estimates.copy()
       +        config.mempool_fees  = self.network.config.mempool_fees.copy()
                cmdname = config.get('cmd')
                cmd = known_commands[cmdname]
                if cmd.requires_wallet:
   DIR diff --git a/lib/simple_config.py b/lib/simple_config.py
       t@@ -4,6 +4,7 @@ import time
        import os
        import stat
        from decimal import Decimal
       +from typing import Union
        
        from copy import deepcopy
        
       t@@ -278,16 +279,18 @@ class SimpleConfig(PrintError):
                return get_fee_within_limits
        
            @impose_hard_limits_on_fee
       -    def eta_to_fee(self, i):
       +    def eta_to_fee(self, slider_pos) -> Union[int, None]:
                """Returns fee in sat/kbyte."""
       -        if i < 4:
       -            j = FEE_ETA_TARGETS[i]
       -            fee = self.fee_estimates.get(j)
       +        slider_pos = max(slider_pos, 0)
       +        slider_pos = min(slider_pos, len(FEE_ETA_TARGETS))
       +        if slider_pos < len(FEE_ETA_TARGETS):
       +            target_blocks = FEE_ETA_TARGETS[slider_pos]
       +            fee = self.fee_estimates.get(target_blocks)
                else:
       -            assert i == 4
                    fee = self.fee_estimates.get(2)
                    if fee is not None:
                        fee += fee/2
       +                fee = int(fee)
                return fee
        
            def fee_to_depth(self, target_fee):
       t@@ -301,9 +304,9 @@ class SimpleConfig(PrintError):
                return depth
        
            @impose_hard_limits_on_fee
       -    def depth_to_fee(self, i):
       +    def depth_to_fee(self, slider_pos) -> int:
                """Returns fee in sat/kbyte."""
       -        target = self.depth_target(i)
       +        target = self.depth_target(slider_pos)
                depth = 0
                for fee, s in self.mempool_fees:
                    depth += s
       t@@ -313,8 +316,10 @@ class SimpleConfig(PrintError):
                    return 0
                return fee * 1000
        
       -    def depth_target(self, i):
       -        return FEE_DEPTH_TARGETS[i]
       +    def depth_target(self, slider_pos):
       +        slider_pos = max(slider_pos, 0)
       +        slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1)
       +        return FEE_DEPTH_TARGETS[slider_pos]
        
            def eta_target(self, i):
                if i == len(FEE_ETA_TARGETS):
       t@@ -430,14 +435,35 @@ class SimpleConfig(PrintError):
            def use_mempool_fees(self):
                return bool(self.get('mempool_fees', False))
        
       -    def fee_per_kb(self, dyn=None, mempool=None):
       +    def _feerate_from_fractional_slider_position(self, fee_level: float, dyn: bool,
       +                                                 mempool: bool) -> Union[int, None]:
       +        fee_level = max(fee_level, 0)
       +        fee_level = min(fee_level, 1)
       +        if dyn:
       +            max_pos = (len(FEE_DEPTH_TARGETS) - 1) if mempool else len(FEE_ETA_TARGETS)
       +            slider_pos = round(fee_level * max_pos)
       +            fee_rate = self.depth_to_fee(slider_pos) if mempool else self.eta_to_fee(slider_pos)
       +        else:
       +            max_pos = len(FEERATE_STATIC_VALUES) - 1
       +            slider_pos = round(fee_level * max_pos)
       +            fee_rate = FEERATE_STATIC_VALUES[slider_pos]
       +        return fee_rate
       +
       +    def fee_per_kb(self, dyn: bool=None, mempool: bool=None, fee_level: float=None) -> Union[int, None]:
                """Returns sat/kvB fee to pay for a txn.
                Note: might return None.
       +
       +        fee_level: float between 0.0 and 1.0, representing fee slider position
                """
                if dyn is None:
                    dyn = self.is_dynfee()
                if mempool is None:
                    mempool = self.use_mempool_fees()
       +        if fee_level is not None:
       +            return self._feerate_from_fractional_slider_position(fee_level, dyn, mempool)
       +        # there is no fee_level specified; will use config.
       +        # note: 'depth_level' and 'fee_level' in config are integer slider positions,
       +        # unlike fee_level here, which (when given) is a float in [0.0, 1.0]
                if dyn:
                    if mempool:
                        fee_rate = self.depth_to_fee(self.get_depth_level())