Ejemplo n.º 1
0
    def apy(self, _: ApySamples) -> Apy:
        curve_3_pool = contract("0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7")
        curve_reward_distribution = contract(
            "0xA464e6DCda8AC41e03616F95f4BC98a13b8922Dc")
        curve_voting_escrow = contract(
            "0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2")
        voter = "0xF147b8125d2ef93FB6965Db97D6746952a133934"
        crv_price = magic.get_price(
            "0xD533a949740bb3306d119CC777fa900bA034cd52")
        yvecrv_price = magic.get_price(
            "0xc5bDdf9843308380375a611c18B50Fb9341f502A")

        total_vecrv = curve_voting_escrow.totalSupply()
        yearn_vecrv = curve_voting_escrow.balanceOf(voter)
        vault_supply = self.vault.totalSupply()

        week = 7 * 86400
        epoch = math.floor(time() / week) * week - week
        tokens_per_week = curve_reward_distribution.tokens_per_week(
            epoch) / 1e18
        virtual_price = curve_3_pool.get_virtual_price() / 1e18
        # although we call this APY, this is actually APR since there is no compounding
        apy = (tokens_per_week * virtual_price * 52) / (
            (total_vecrv / 1e18) * crv_price)
        vault_boost = (yearn_vecrv / vault_supply) * (crv_price / yvecrv_price)
        composite = {
            "currentBoost": vault_boost,
            "boostedApy": apy * vault_boost,
            "totalApy": apy * vault_boost,
            "poolApy": apy,
            "baseApy": apy,
        }
        return Apy("backscratcher", apy, apy, ApyFees(), composite=composite)
Ejemplo n.º 2
0
 def apy(self, _: ApySamples) -> Apy:
     data = requests.get(
         "https://api.pickle.finance/prod/protocol/pools").json()
     yvboost_eth_pool = [
         pool for pool in data if pool["identifier"] == "yvboost-eth"
     ][0]
     apy = yvboost_eth_pool["apy"] / 100.
     points = ApyPoints(apy, apy, apy)
     return Apy("yvecrv-jar", apy, apy, ApyFees(), points=points)
Ejemplo n.º 3
0
def simple(vault: VaultV1, samples: ApySamples) -> Apy:
    inception_block = contract_creation_block(vault.vault.address)

    if not inception_block:
        raise ApyError("v1:blocks", "inception_block not found")

    contract = vault.vault
    price_per_share = contract.getPricePerFullShare

    inception_price = 1e18

    try:
        now_price = price_per_share(block_identifier=samples.now)
    except ValueError:
        now_price = inception_price

    if samples.week_ago > inception_block:
        try:
            week_ago_price = price_per_share(block_identifier=samples.week_ago)
        except ValueError:
            week_ago_price = now_price
    else:
        week_ago_price = now_price

    if samples.month_ago > inception_block:
        try:
            month_ago_price = price_per_share(block_identifier=samples.month_ago)
        except ValueError:
            month_ago_price = week_ago_price
    else:
        month_ago_price = week_ago_price

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)
    month_ago_point = SharePricePoint(samples.month_ago, month_ago_price)
    inception_point = SharePricePoint(inception_block, inception_price)

    week_ago_apy = calculate_roi(now_point, week_ago_point)
    month_ago_apy = calculate_roi(now_point, month_ago_point)
    inception_apy = calculate_roi(now_point, inception_point)

    net_apy = month_ago_apy

    strategy = vault.strategy
    withdrawal = strategy.withdrawalFee() if hasattr(strategy, "withdrawalFee") else 0
    strategist_performance = strategy.performanceFee() if hasattr(strategy, "performanceFee") else 0
    strategist_reward = strategy.strategistReward() if hasattr(strategy, "strategistReward") else 0
    treasury = strategy.treasuryFee() if hasattr(strategy, "treasuryFee") else 0

    performance = strategist_reward + strategist_performance + treasury

    apy = net_apy / (1 - performance / 1e4)

    points = ApyPoints(week_ago_apy, month_ago_apy, inception_apy)
    fees = ApyFees(performance=performance, withdrawal=withdrawal)
    return Apy("v1:simple", apy, net_apy, fees, points=points)
Ejemplo n.º 4
0
def simple(vault: Vault, samples: ApySamples) -> Apy:
    harvests = sorted([
        harvest for strategy in vault.strategies
        for harvest in strategy.harvests
    ])

    if len(harvests) < 10:
        raise ApyError("v2:harvests", "harvests are < 10")

    now = harvests[-1]
    week_ago = closest(harvests, samples.week_ago)
    month_ago = closest(harvests, samples.month_ago)
    inception_block = harvests[2]

    contract = vault.vault
    price_per_share = contract.pricePerShare

    now_price = price_per_share(block_identifier=now)

    inception_price = 10**contract.decimals()

    if samples.week_ago > inception_block:
        week_ago_price = price_per_share(block_identifier=week_ago)
    else:
        week_ago_price = now_price

    if samples.month_ago > inception_block:
        month_ago_price = price_per_share(block_identifier=month_ago)
    else:
        month_ago_price = week_ago_price

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)
    month_ago_point = SharePricePoint(samples.month_ago, month_ago_price)
    inception_point = SharePricePoint(inception_block, inception_price)

    week_ago_apy = calculate_roi(now_point, week_ago_point)
    month_ago_apy = calculate_roi(now_point, month_ago_point)
    inception_apy = calculate_roi(now_point, inception_point)

    net_apy = month_ago_apy

    # performance fee is doubled since 1x strategists + 1x treasury
    performance = (contract.performanceFee() *
                   2) if hasattr(contract, "performanceFee") else 0
    management = contract.managementFee() if hasattr(contract,
                                                     "managementFee") else 0

    apy = net_apy / (1 - performance / 1e4) + (management / 1e4)

    points = ApyPoints(week_ago_apy, month_ago_apy, inception_apy)
    fees = ApyFees(performance=performance, management=management)
    return Apy("v2:simple", apy, net_apy, fees, points=points)
Ejemplo n.º 5
0
def simple(vault: Union[VaultV1, VaultV2], samples: ApySamples) -> Apy:
    lp_token = vault.token.address

    pool_address = get_pool(lp_token)
    gauge_addresses = curve_registry.get_gauges(pool_address)

    gauge_address = gauge_addresses[0][0]

    # FIXME: crvUSDP doesn't have a gauge connected in the registry
    if vault.vault.address == "0x1B5eb1173D2Bf770e50F10410C9a96F7a8eB6e75":
        gauge = "0x055be5DDB7A925BfEF3417FC157f53CA77cA7222"

    gauge = interface.CurveGauge(gauge_address)
    controller = gauge.controller()
    controller = interface.CurveGaugeController(controller)

    gauge_working_supply = gauge.working_supply()
    gauge_weight = controller.gauge_relative_weight(gauge_address)

    gauge_inflation_rate = gauge.inflation_rate()
    pool_price = curve_registry.get_virtual_price_from_lp_token(lp_token)

    underlying_coins = get_underlying_coins(lp_token)

    btc_like = any([coin in BTC_LIKE for coin in underlying_coins])
    eth_like = any([coin in ETH_LIKE for coin in underlying_coins])

    base_asset = WBTC if btc_like else WETH if eth_like else underlying_coins[0]
    base_asset_price = get_price(base_asset) or 1

    crv_price = get_price(CRV)

    y_working_balance = gauge.working_balances(YVECRV_VOTER)
    y_gauge_balance = gauge.balanceOf(YVECRV_VOTER)

    base_apr = (
        gauge_inflation_rate
        * gauge_weight
        * (SECONDS_PER_YEAR / gauge_working_supply)
        * (PER_MAX_BOOST / pool_price)
        * crv_price
    ) / base_asset_price

    if y_gauge_balance > 0:
        boost = y_working_balance / (PER_MAX_BOOST * y_gauge_balance) or 1
    else:
        boost = MAX_BOOST

    # FIXME: The HBTC v1 vault is currently still earning yield, but it is no longer boosted.
    if vault.vault.address == "0x46AFc2dfBd1ea0c0760CAD8262A5838e803A37e5":
        boost = 1

    boosted_apr = base_apr * boost

    if hasattr(gauge, "reward_contract"):
        try:
            reward_address = gauge.reward_contract()
            assert reward_address != ZERO_ADDRESS
            reward_apr = rewards(reward_address, pool_price, base_asset_price)
        except ValueError:
            reward_apr = 0
    else:
        reward_apr = 0

    price_per_share = curve_registry.get_virtual_price_from_lp_token
    now_price = price_per_share(lp_token, block_identifier=samples.now)
    try:
        week_ago_price = price_per_share(lp_token, block_identifier=samples.week_ago)
    except ValueError:
        raise ApyError("crv", "insufficient data")

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)

    pool_apr = calculate_roi(now_point, week_ago_point)
    pool_apy = (((pool_apr / 365) + 1) ** 365) - 1

    # FIXME: crvANKR's pool apy going crazy
    if vault.vault.address == "0xE625F5923303f1CE7A43ACFEFd11fd12f30DbcA4":
        pool_apy = 0

    if type(vault) is VaultV2:
        contract = vault.vault
        keep_crv = sum([strategy.keepCRV() for strategy in vault.strategies if hasattr(strategy, "keepCRV")])
        performance = (contract.performanceFee() * 2) if hasattr(contract, "performanceFee") else 0
        management = contract.managementFee() if hasattr(contract, "managementFee") else 0
    else:
        strategy = vault.strategy
        strategist_performance = strategy.performanceFee() if hasattr(strategy, "performanceFee") else 0
        strategist_reward = strategy.strategistReward() if hasattr(strategy, "strategistReward") else 0
        treasury = strategy.treasuryFee() if hasattr(strategy, "treasuryFee") else 0
        keep_crv = strategy.keepCRV() if hasattr(strategy, "keepCRV") else 0

        performance = strategist_reward + strategist_performance + treasury
        management = 0

    keep_crv /= 1e4
    performance /= 1e4
    management /= 1e4

    lose_crv = 1 - keep_crv

    gross_farmed_apy = (
        (boosted_apr * keep_crv) + (((boosted_apr * lose_crv + reward_apr) / COMPOUNDING) + 1) ** COMPOUNDING
    ) - 1

    apy = (gross_farmed_apy + 1) * (pool_apy + 1) - 1

    net_curve_apr = (boosted_apr * lose_crv + reward_apr) * (1 - performance) - management

    net_farmed_apy = ((net_curve_apr / COMPOUNDING) + 1) ** COMPOUNDING - 1
    net_apy = (net_farmed_apy + 1) * (pool_apy + 1) - 1

    fees = ApyFees(performance=performance, management=management, keep_crv=keep_crv)
    composite = {
        "boost": boost,
        "pool_apy": pool_apy,
        "boosted_apr": boosted_apr,
        "base_apr": base_apr,
        "rewards_apr": reward_apr,
    }
    return Apy("crv", apy, net_apy, fees, composite=composite)
Ejemplo n.º 6
0
def simple(vault, samples: ApySamples) -> Apy:
    lp_token = vault.token.address

    pool_address = curve.get_pool(lp_token)

    gauge_address = curve.get_gauge(pool_address)

    if gauge_address is None:
        raise ApyError("crv", "no gauge")

    gauge = contract(gauge_address)

    try:
        controller = gauge.controller()
        controller = contract(controller)
    except:
        # newer gauges do not have a 'controller' method
        controller = curve.gauge_controller

    block = samples.now
    gauge_weight = controller.gauge_relative_weight.call(
        gauge_address, block_identifier=block)
    gauge_working_supply = gauge.working_supply(block_identifier=block)
    if gauge_working_supply == 0:
        raise ApyError("crv", "gauge working supply is zero")

    gauge_inflation_rate = gauge.inflation_rate(block_identifier=block)
    pool = contract(pool_address)
    pool_price = pool.get_virtual_price(block_identifier=block)

    base_asset_price = get_price(lp_token, block=block) or 1

    crv_price = get_price(curve.crv, block=block)

    yearn_voter = addresses[chain.id]['yearn_voter_proxy']
    y_working_balance = gauge.working_balances(yearn_voter,
                                               block_identifier=block)
    y_gauge_balance = gauge.balanceOf(yearn_voter, block_identifier=block)

    base_apr = (gauge_inflation_rate * gauge_weight *
                (SECONDS_PER_YEAR / gauge_working_supply) *
                (PER_MAX_BOOST / pool_price) * crv_price) / base_asset_price

    if y_gauge_balance > 0:
        boost = y_working_balance / (PER_MAX_BOOST * y_gauge_balance) or 1
    else:
        boost = MAX_BOOST

    # FIXME: The HBTC v1 vault is currently still earning yield, but it is no longer boosted.
    if vault.vault.address == "0x46AFc2dfBd1ea0c0760CAD8262A5838e803A37e5":
        boost = 1

    # TODO: come up with cleaner way to deal with these new gauge rewards
    reward_apr = 0
    if hasattr(gauge, "reward_contract"):
        reward_address = gauge.reward_contract()
        if reward_address != ZERO_ADDRESS:
            reward_apr = rewards(reward_address,
                                 pool_price,
                                 base_asset_price,
                                 block=block)
    elif hasattr(gauge, "reward_data"
                 ):  # this is how new gauges, starting with MIM, show rewards
        # get our token
        # TODO: consider adding for loop with [gauge.reward_tokens(i) for i in range(gauge.reward_count())] for multiple rewards tokens
        gauge_reward_token = gauge.reward_tokens(0)
        if gauge_reward_token in [ZERO_ADDRESS]:
            print("no reward token")
        else:
            reward_data = gauge.reward_data(gauge_reward_token)
            rate = reward_data['rate']
            period_finish = reward_data['period_finish']
            total_supply = gauge.totalSupply()
            token_price = 0
            if gauge_reward_token == addresses[chain.id]['rkp3r_rewards']:
                rKP3R_contract = interface.rKP3R(gauge_reward_token)
                discount = rKP3R_contract.discount(block_identifier=block)
                token_price = get_price(addresses[chain.id]['kp3r'],
                                        block=block) * (100 - discount) / 100
            else:
                token_price = get_price(gauge_reward_token, block=block)
            current_time = time() if block is None else get_block_timestamp(
                block)
            if period_finish < current_time:
                reward_apr = 0
            else:
                reward_apr = (SECONDS_PER_YEAR *
                              (rate / 1e18) * token_price) / (
                                  (pool_price / 1e18) *
                                  (total_supply / 1e18) * base_asset_price)
    else:
        reward_apr = 0

    price_per_share = pool.get_virtual_price
    now_price = price_per_share(block_identifier=samples.now)
    try:
        week_ago_price = price_per_share(block_identifier=samples.week_ago)
    except ValueError:
        raise ApyError("crv", "insufficient data")

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)

    pool_apr = calculate_roi(now_point, week_ago_point)
    pool_apy = (((pool_apr / 365) + 1)**365) - 1

    # FIXME: crvANKR's pool apy going crazy
    if vault.vault.address == "0xE625F5923303f1CE7A43ACFEFd11fd12f30DbcA4":
        pool_apy = 0

    # prevent circular import for partners calculations
    from yearn.v2.vaults import Vault as VaultV2

    if isinstance(vault, VaultV2):
        vault_contract = vault.vault
        if len(vault.strategies) > 0 and hasattr(vault.strategies[0].strategy,
                                                 "keepCRV"):
            crv_keep_crv = vault.strategies[0].strategy.keepCRV(
                block_identifier=block) / 1e4
        elif len(vault.strategies) > 0 and hasattr(
                vault.strategies[0].strategy, "keepCrvPercent"):
            crv_keep_crv = vault.strategies[0].strategy.keepCrvPercent(
                block_identifier=block) / 1e4
        else:
            crv_keep_crv = 0
        performance = (vault_contract.performanceFee(block_identifier=block) *
                       2) / 1e4 if hasattr(vault_contract,
                                           "performanceFee") else 0
        management = vault_contract.managementFee(
            block_identifier=block) / 1e4 if hasattr(vault_contract,
                                                     "managementFee") else 0
    else:
        strategy = vault.strategy
        strategist_performance = strategy.performanceFee(
            block_identifier=block) if hasattr(strategy,
                                               "performanceFee") else 0
        strategist_reward = strategy.strategistReward(
            block_identifier=block) if hasattr(strategy,
                                               "strategistReward") else 0
        treasury = strategy.treasuryFee(
            block_identifier=block) if hasattr(strategy, "treasuryFee") else 0
        crv_keep_crv = strategy.keepCRV(
            block_identifier=block) / 1e4 if hasattr(strategy,
                                                     "keepCRV") else 0

        performance = (strategist_reward + strategist_performance +
                       treasury) / 1e4
        management = 0

    if isinstance(vault, VaultV2) and len(vault.strategies) == 2:
        crv_strategy = vault.strategies[0].strategy
        cvx_strategy = vault.strategies[1].strategy
        convex_voter = addresses[chain.id]['convex_voter_proxy']
        cvx_working_balance = gauge.working_balances(convex_voter,
                                                     block_identifier=block)
        cvx_gauge_balance = gauge.balanceOf(convex_voter,
                                            block_identifier=block)

        if cvx_gauge_balance > 0:
            cvx_boost = cvx_working_balance / (PER_MAX_BOOST *
                                               cvx_gauge_balance) or 1
        else:
            cvx_boost = MAX_BOOST

        cvx_booster = contract(addresses[chain.id]['convex_booster'])
        cvx_lock_incentive = cvx_booster.lockIncentive(block_identifier=block)
        cvx_staker_incentive = cvx_booster.stakerIncentive(
            block_identifier=block)
        cvx_earmark_incentive = cvx_booster.earmarkIncentive(
            block_identifier=block)
        cvx_fee = (cvx_lock_incentive + cvx_staker_incentive +
                   cvx_earmark_incentive) / 1e4
        cvx_keep_crv = cvx_strategy.keepCRV(block_identifier=block) / 1e4

        total_cliff = 1e3
        max_supply = 1e2 * 1e6 * 1e18  # ?
        reduction_per_cliff = 1e23
        cvx = contract(addresses[chain.id]['cvx'])
        supply = cvx.totalSupply(block_identifier=block)
        cliff = supply / reduction_per_cliff
        if supply <= max_supply:
            reduction = total_cliff - cliff
            cvx_minted_as_crv = reduction / total_cliff
            cvx_price = get_price(cvx, block=block)
            converted_cvx = cvx_price / crv_price
            cvx_printed_as_crv = cvx_minted_as_crv * converted_cvx
        else:
            cvx_printed_as_crv = 0

        cvx_apr = ((1 - cvx_fee) * cvx_boost *
                   base_apr) * (1 + cvx_printed_as_crv) + reward_apr
        cvx_apr_minus_keep_crv = ((1 - cvx_fee) * cvx_boost * base_apr) * (
            (1 - cvx_keep_crv) + cvx_printed_as_crv)

        crv_debt_ratio = vault.vault.strategies(crv_strategy)[2] / 1e4
        cvx_debt_ratio = vault.vault.strategies(cvx_strategy)[2] / 1e4
    else:
        cvx_apr = 0
        cvx_apr_minus_keep_crv = 0
        cvx_keep_crv = 0
        crv_debt_ratio = 1
        cvx_debt_ratio = 0

    crv_apr = base_apr * boost + reward_apr
    crv_apr_minus_keep_crv = base_apr * boost * (1 - crv_keep_crv)

    gross_apr = (1 + (crv_apr * crv_debt_ratio + cvx_apr * cvx_debt_ratio)) * (
        1 + pool_apy) - 1

    cvx_net_apr = (cvx_apr_minus_keep_crv +
                   reward_apr) * (1 - performance) - management
    cvx_net_farmed_apy = (1 + (cvx_net_apr / COMPOUNDING))**COMPOUNDING - 1
    cvx_net_apy = ((1 + cvx_net_farmed_apy) * (1 + pool_apy)) - 1

    crv_net_apr = (crv_apr_minus_keep_crv +
                   reward_apr) * (1 - performance) - management
    crv_net_farmed_apy = (1 + (crv_net_apr / COMPOUNDING))**COMPOUNDING - 1
    crv_net_apy = ((1 + crv_net_farmed_apy) * (1 + pool_apy)) - 1

    net_apy = crv_net_apy * crv_debt_ratio + cvx_net_apy * cvx_debt_ratio

    # 0.3.5+ should never be < 0% because of management
    if isinstance(vault, VaultV2) and net_apy < 0 and Version(
            vault.api_version) >= Version("0.3.5"):
        net_apy = 0

    fees = ApyFees(performance=performance,
                   management=management,
                   keep_crv=crv_keep_crv,
                   cvx_keep_crv=cvx_keep_crv)
    composite = {
        "boost": boost,
        "pool_apy": pool_apy,
        "boosted_apr": crv_apr,
        "base_apr": base_apr,
        "cvx_apr": cvx_apr,
        "rewards_apr": reward_apr,
    }

    return Apy("crv", gross_apr, net_apy, fees, composite=composite)
Ejemplo n.º 7
0
def simple(vault, samples: ApySamples) -> Apy:
    harvests = sorted([harvest for strategy in vault.strategies for harvest in strategy.harvests])

    # we don't want to display APYs when vaults are ramping up
    if len(harvests) < 2:
        raise ApyError("v2:harvests", "harvests are < 2")
    
    # set our time values for simple calcs, closest to a harvest around that time period
    now = closest(harvests, samples.now)
    week_ago = closest(harvests, samples.week_ago)
    month_ago = closest(harvests, samples.month_ago)
    
    # set our parameters
    contract = vault.vault
    price_per_share = contract.pricePerShare
    
    # calculate our current price
    now_price = price_per_share(block_identifier=now)

    # get our inception data
    inception_price = 10 ** contract.decimals()
    inception_block = harvests[:2][-1]

    if now_price == inception_price:
        raise ApyError("v2:inception", "no change from inception price")
    
    # check our historical data
    if samples.week_ago > inception_block:
        week_ago_price = price_per_share(block_identifier=week_ago)
    else:
        week_ago_price = inception_price

    if samples.month_ago > inception_block:
        month_ago_price = price_per_share(block_identifier=month_ago)
    else:
        month_ago_price = inception_price

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)
    month_ago_point = SharePricePoint(samples.month_ago, month_ago_price)
    inception_point = SharePricePoint(inception_block, inception_price)

    week_ago_apy = calculate_roi(now_point, week_ago_point)
    month_ago_apy = calculate_roi(now_point, month_ago_point)
    inception_apy = calculate_roi(now_point, inception_point)

    # use the first non-zero apy, ordered by precedence
    apys = [month_ago_apy, week_ago_apy, inception_apy]
    net_apy = next((value for value in apys if value != 0), 0)

    # for performance fee, half comes from strategy (strategist share) and half from the vault (treasury share)
    strategy_fees = []
    for strategy in vault.strategies: # look at all of our strategies
        debt_ratio = contract.strategies(strategy.strategy)['debtRatio'] / 10000
        performance_fee = contract.strategies(strategy.strategy)['performanceFee']
        proportional_fee = debt_ratio * performance_fee
        strategy_fees.append(proportional_fee)
    
    strategy_performance = sum(strategy_fees)
    vault_performance = contract.performanceFee() if hasattr(contract, "performanceFee") else 0
    management = contract.managementFee() if hasattr(contract, "managementFee") else 0
    performance = vault_performance + strategy_performance

    performance /= 1e4
    management /= 1e4

    # assume we are compounding every week
    compounding = 52

    # calculate our APR after fees
    apr_after_fees = compounding * ((net_apy + 1) ** (1 / compounding)) - compounding

    # calculate our pre-fee APR
    gross_apr = apr_after_fees / (1 - performance) + management

    # 0.3.5+ should never be < 0% because of management
    if net_apy < 0 and Version(vault.api_version) >= Version("0.3.5"):
        net_apy = 0

    points = ApyPoints(week_ago_apy, month_ago_apy, inception_apy)
    fees = ApyFees(performance=performance, management=management)
    return Apy("v2:simple", gross_apr, net_apy, fees, points=points)
Ejemplo n.º 8
0
def average(vault, samples: ApySamples) -> Apy:
    harvests = sorted([harvest for strategy in vault.strategies for harvest in strategy.harvests])

    # we don't want to display APYs when vaults are ramping up
    if len(harvests) < 2:
        raise ApyError("v2:harvests", "harvests are < 2")
    
    # set our parameters
    contract = vault.vault
    price_per_share = contract.pricePerShare
    
    # calculate our current price
    now_price = price_per_share(block_identifier=samples.now)

    # get our inception data
    inception_price = 10 ** contract.decimals()
    inception_block = harvests[:2][-1]

    if now_price == inception_price:
        raise ApyError("v2:inception", "no change from inception price")
    
    # check our historical data
    if samples.week_ago > inception_block:
        week_ago_price = price_per_share(block_identifier=samples.week_ago)
    else:
        week_ago_price = inception_price

    if samples.month_ago > inception_block:
        month_ago_price = price_per_share(block_identifier=samples.month_ago)
    else:
        month_ago_price = inception_price

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)
    month_ago_point = SharePricePoint(samples.month_ago, month_ago_price)
    inception_point = SharePricePoint(inception_block, inception_price)

    week_ago_apy = calculate_roi(now_point, week_ago_point)
    month_ago_apy = calculate_roi(now_point, month_ago_point)
    inception_apy = calculate_roi(now_point, inception_point)
    
    # we should look at a vault's harvests, age, etc to determine whether to show new APY or not

    # use the first non-zero apy, ordered by precedence
    apys = [month_ago_apy, week_ago_apy]
    two_months_ago = datetime.now() - timedelta(days=60)
    if contract.activation() > two_months_ago.timestamp():
        # if the vault was activated less than two months ago then it's ok to use
        # the inception apy, otherwise using it isn't representative of the current apy
        apys.append(inception_apy)

    net_apy = next((value for value in apys if value != 0), 0)

    # for performance fee, half comes from strategy (strategist share) and half from the vault (treasury share)
    strategy_fees = []
    for strategy in vault.strategies: # look at all of our strategies
        debt_ratio = contract.strategies(strategy.strategy)['debtRatio'] / 10000
        performance_fee = contract.strategies(strategy.strategy)['performanceFee']
        proportional_fee = debt_ratio * performance_fee
        strategy_fees.append(proportional_fee)
    
    strategy_performance = sum(strategy_fees)
    vault_performance = contract.performanceFee() if hasattr(contract, "performanceFee") else 0
    management = contract.managementFee() if hasattr(contract, "managementFee") else 0
    performance = vault_performance + strategy_performance

    performance /= 1e4
    management /= 1e4

    # assume we are compounding every week
    compounding = 52

    # calculate our APR after fees
    # if net_apy is negative no fees are charged
    apr_after_fees = compounding * ((net_apy + 1) ** (1 / compounding)) - compounding if net_apy > 0 else net_apy

    # calculate our pre-fee APR
    gross_apr = apr_after_fees / (1 - performance) + management

    # 0.3.5+ should never be < 0% because of management
    if net_apy < 0 and Version(vault.api_version) >= Version("0.3.5"):
        net_apy = 0

    points = ApyPoints(week_ago_apy, month_ago_apy, inception_apy)
    fees = ApyFees(performance=performance, management=management)
    return Apy("v2:averaged", gross_apr, net_apy, fees, points=points)
Ejemplo n.º 9
0
def simple(vault, samples: ApySamples) -> Apy:
    inception_block = contract_creation_block(vault.vault.address)

    if not inception_block:
        raise ApyError("v1:blocks", "inception_block not found")

    contract = vault.vault
    price_per_share = contract.getPricePerFullShare

    inception_price = 1e18

    try:
        now_price = price_per_share(block_identifier=samples.now)
    except ValueError:
        now_price = inception_price

    if samples.week_ago > inception_block:
        try:
            week_ago_price = price_per_share(block_identifier=samples.week_ago)
        except ValueError:
            week_ago_price = now_price
    else:
        week_ago_price = now_price

    if samples.month_ago > inception_block:
        try:
            month_ago_price = price_per_share(
                block_identifier=samples.month_ago)
        except ValueError:
            month_ago_price = week_ago_price
    else:
        month_ago_price = week_ago_price

    now_point = SharePricePoint(samples.now, now_price)
    week_ago_point = SharePricePoint(samples.week_ago, week_ago_price)
    month_ago_point = SharePricePoint(samples.month_ago, month_ago_price)
    inception_point = SharePricePoint(inception_block, inception_price)

    week_ago_apy = calculate_roi(now_point, week_ago_point)
    month_ago_apy = calculate_roi(now_point, month_ago_point)
    inception_apy = calculate_roi(now_point, inception_point)

    # use the first non-zero apy, ordered by precedence
    apys = [week_ago_apy, month_ago_apy, inception_apy]
    net_apy = next((value for value in apys if value != 0), 0)

    strategy = vault.strategy
    withdrawal = strategy.withdrawalFee() if hasattr(strategy,
                                                     "withdrawalFee") else 0
    strategist_performance = strategy.performanceFee() if hasattr(
        strategy, "performanceFee") else 0
    strategist_reward = strategy.strategistReward() if hasattr(
        strategy, "strategistReward") else 0
    treasury = strategy.treasuryFee() if hasattr(strategy,
                                                 "treasuryFee") else 0

    performance = (strategist_reward + strategist_performance + treasury) / 1e4

    # assume we are compounding every week
    compounding = 52

    # calculate our APR after fees
    # if net_apy is negative no fees are charged
    apr_after_fees = compounding * (
        (net_apy + 1)**
        (1 / compounding)) - compounding if net_apy > 0 else net_apy

    # calculate our pre-fee APR
    gross_apr = apr_after_fees / (1 - performance)

    points = ApyPoints(week_ago_apy, month_ago_apy, inception_apy)
    fees = ApyFees(performance=performance, withdrawal=withdrawal)
    return Apy("v1:simple", gross_apr, net_apy, fees, points=points)