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)
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)
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)
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)
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)
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)
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)
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)
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)