lbry-sdk/lbrynet/core/Strategy.py

88 lines
3.1 KiB
Python
Raw Normal View History

import logging
from lbrynet.core.Offer import Offer
from lbrynet.core.PriceModel import MeanAvailabilityWeightedPrice
log = logging.getLogger(__name__)
def get_default_strategy(blob_tracker, **kwargs):
return BasicAvailabilityWeightedStrategy(blob_tracker, **kwargs)
class BasicAvailabilityWeightedStrategy(object):
"""
Basic strategy to target blob prices based on supply relative to mean supply
Discount price target with each incoming request, and raise it with each outgoing from the modeled price
until the rate is accepted or a threshold is reached
"""
def __init__(self, blob_tracker, acceleration=1.25, deceleration=0.9, max_rate=0.005, min_rate=0.0):
self._acceleration = acceleration # rate of how quickly to ramp offer
self._deceleration = deceleration
self._min_rate = min_rate
self._max_rate = max_rate
self._count_up = {}
self._count_down = {}
self._requested = {}
self._offers_to_peers = {}
self.model = MeanAvailabilityWeightedPrice(blob_tracker)
def respond_to_offer(self, offer, peer, blobs):
request_count = self._count_up.get(peer, 0)
rates = [self._calculate_price(blob) for blob in blobs]
rate = sum(rates) / max(len(rates), 1)
discounted = self._discount(rate, request_count)
price = self._bounded_price(discounted)
log.info("Price target: %f, final: %f", discounted, price)
self._inc_up_count(peer)
if offer.rate == 0.0 and request_count == 0:
# give blobs away for free by default on the first request
offer.accept()
return offer
elif offer.rate >= price:
log.info("Accept: %f", offer.rate)
offer.accept()
return offer
else:
log.info("Reject: %f", offer.rate)
offer.reject()
return offer
def make_offer(self, peer, blobs):
# use mean turn-discounted price for all the blobs requested
request_count = self._count_down.get(peer, 0)
self._inc_down_count(peer)
if request_count == 0:
# Try asking for it for free
offer = Offer(0.0)
else:
rates = [self._calculate_price(blob) for blob in blobs]
mean_rate = sum(rates) / max(len(blobs), 1)
with_premium = self._premium(mean_rate, request_count)
price = self._bounded_price(with_premium)
offer = Offer(price)
return offer
def _bounded_price(self, price):
price_for_return = min(self._max_rate, max(price, self._min_rate))
return price_for_return
def _inc_up_count(self, peer):
turn = self._count_up.get(peer, 0) + 1
self._count_up.update({peer: turn})
def _inc_down_count(self, peer):
turn = self._count_down.get(peer, 0) + 1
self._count_down.update({peer: turn})
def _calculate_price(self, blob):
return self.model.calculate_price(blob)
def _premium(self, rate, turn):
return rate * (self._acceleration ** turn)
def _discount(self, rate, turn):
return rate * (self._deceleration ** turn)