def calculate(self, reward_logs=None, total_reward_amount=None): """ :param reward_logs: Nothing is expected. This value is not used. reward_logs are generated from provider object. :param total_reward_amount: Nothing is expected. This value is not used. total amount is calculated in calling function. :return: tuple (reward_logs, total reward amount) reward_logs is a list of RewardLog objects. Last item is owners_parent record. total_reward equals to sum( delegator rewards + owners_parent rewards ) """ ratio_sum = 0.0 total_delegator_balance = 0 reward_logs = [] delegate_staking_balance = self.reward_provider_model.delegate_staking_balance delegators_balance_dict = self.reward_provider_model.delegator_balance_dict # calculate how rewards will be distributed # ratio is stake/total staking balance # total of ratios must be 1 for address, delegator_info in delegators_balance_dict.items(): staking_balance = delegator_info["staking_balance"] current_balance = delegator_info["current_balance"] originaladdress = (delegator_info["originaladdress"] if "originaladdress" in delegator_info else None) total_delegator_balance += staking_balance ratio = staking_balance / delegate_staking_balance reward_item = RewardLog( address=address, type=reward_log.TYPE_DELEGATOR, staking_balance=staking_balance, current_balance=current_balance, originaladdress=originaladdress, ) reward_item.ratio = ratio reward_item.ratio0 = reward_item.ratio ratio_sum += ratio reward_logs.append(reward_item) owners_rl = RewardLog( address=reward_log.TYPE_OWNERS_PARENT, type=reward_log.TYPE_OWNERS_PARENT, staking_balance=delegate_staking_balance - total_delegator_balance, current_balance=0, ) owners_rl.ratio = 1 - ratio_sum owners_rl.ratio0 = owners_rl.ratio reward_logs.append(owners_rl) return reward_logs
def test_calculate(self): rewards = [] ratios = [0.25, 0.05, 0.3, 0.15, 0.25] total_reward = 1000 for i, ratio in enumerate(ratios, start=1): rl0 = RewardLog( address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0, ) rl0.ratio = ratio rl0.ratio3 = ratio rewards.append(rl0) rewards[0].type = TYPE_OWNERS_PARENT rewards[1].type = TYPE_FOUNDERS_PARENT rewards.append( RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 3)) founders_map = {"addr1": 0.4, "addr2": 0.6} owners_map = {"addr1": 0.6, "addr2": 0.4} phase4 = CalculatePhase4(founders_map, owners_map) new_rewards, new_total_reward = phase4.calculate(rewards, total_reward) # new_total_reward = total_reward self.assertEqual(total_reward, new_total_reward) # check new ratios sum up to 1 # old and new reward amount is the same ratio_sum = 0.0 # filter out skipped records new_rewards = list(rl for rl in new_rewards if not rl.skipped) # 2 owner, 2 founders and 3 delegators self.assertEqual(7, len(new_rewards)) founder_ratio = 0.0 owner_ratio = 0.0 for rl4 in new_rewards: if rl4.skipped: continue if rl4.type == TYPE_FOUNDER: founder_ratio += rl4.ratio4 if rl4.type == TYPE_OWNER: owner_ratio += rl4.ratio4 ratio_sum += rl4.ratio4 self.assertAlmostEqual(1.0, ratio_sum, delta=1e-6) self.assertAlmostEqual(0.25, owner_ratio, delta=1e-6) self.assertAlmostEqual(0.05, founder_ratio, delta=1e-6)
def calculate(self, reward_data3, total_amount): new_rewards = [] # move skipped records to next phase for rl3 in self.iterateskipped(reward_data3): new_rewards.append(rl3) for rl3 in self.filterskipped(reward_data3): if rl3.type == TYPE_FOUNDERS_PARENT: for addr, ratio in self.founders_map.items(): rl4 = RewardLog(addr, TYPE_FOUNDER, 0, 0) # new ratio is parent ratio * ratio of the founder rl4.ratio = ratio * rl3.ratio rl4.ratio4 = rl4.ratio rl4.service_fee_ratio = 0 rl4.service_fee_rate = 0 rl4.parent = rl3 new_rewards.append(rl4) # if no founders, add parent object to rewards list if not self.founders_map.items(): new_rewards.append(rl3) elif rl3.type == TYPE_OWNERS_PARENT: for addr, ratio in self.owners_map.items(): rl4 = RewardLog(addr, TYPE_OWNER, ratio * rl3.staking_balance, 0) # new ratio is parent ratio * ratio of the owner rl4.ratio = ratio * rl3.ratio rl4.ratio4 = rl4.ratio rl4.service_fee_ratio = 0 rl4.service_fee_rate = 0 rl4.parent = rl3 new_rewards.append(rl4) # if no owners, add parent object to rewards list if not self.owners_map.items(): new_rewards.append(rl3) else: rl3.ratio4 = rl3.ratio new_rewards.append(rl3) return new_rewards, total_amount
def calculate(self, reward_data2, total_amount): new_rewards = [] total_excluded_ratio = 0.0 for rl2 in self.iterateskipped(reward_data2): # move skipped records to next phase new_rewards.append(rl2) # exclude requested items for rl2 in self.filterskipped(reward_data2): if rl2.address in self.excluded_set: rl2.skip(desc=BY_CONFIGURATION, phase=self.phase) new_rewards.append(rl2) total_excluded_ratio += rl2.ratio elif ( MIN_DELEGATION_KEY in self.excluded_set and rl2.staking_balance < self.min_delegation_amount ): rl2.skip(desc=BY_MIN_DELEGATION, phase=self.phase) new_rewards.append(rl2) total_excluded_ratio += rl2.ratio else: new_rewards.append(rl2) total_service_fee_ratio = total_excluded_ratio # set fee rates and ratios for rl in self.filterskipped(new_rewards): rl.service_fee_rate = self.fee_calc.calculate(rl.originaladdress) rl.service_fee_ratio = rl.service_fee_rate * rl.ratio rl.ratio = rl.ratio - rl.service_fee_ratio rl.ratio3 = rl.ratio total_service_fee_ratio += rl.service_fee_ratio # create founders parent record if total_service_fee_ratio > ALMOST_ZERO: rl = RewardLog( address=TYPE_FOUNDERS_PARENT, type=TYPE_FOUNDERS_PARENT, staking_balance=0, current_balance=0, ) rl.service_fee_rate = 0 rl.service_fee_ratio = 0 rl.ratio = total_service_fee_ratio rl.ratio3 = rl.ratio new_rewards.append(rl) return new_rewards, int(total_amount)
def test_calculate(self): rewards = [] ratios = [0.25, 0.05, 0.3, 0.15, 0.25] total_reward = 1000 for i, ratio in enumerate(ratios, start=1): rl0 = RewardLog( address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0, ) rl0.ratio = ratio rl0.ratio2 = ratio rewards.append(rl0) rewards.append( RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 2)) excluded_set = {"addr1"} fee_calculator = ServiceFeeCalculator(set(), dict(), 20) # 20% fee phase3 = CalculatePhase3(fee_calculator, excluded_set) new_rewards, new_total_reward = phase3.calculate(rewards, total_reward) # filter out skipped records new_rewards = list(rl for rl in new_rewards if not rl.skipped) # new_total_reward = total_reward self.assertEqual(total_reward, new_total_reward) # check new ratios sum up to 1 # old and new reward amount is the same ratio_sum = 0.0 service_fee_ratio_sum = 0.0 founder_pl = None for rl3 in new_rewards: if rl3.skipped: continue if rl3.type == TYPE_FOUNDERS_PARENT: founder_pl = rl3 ratio_sum += rl3.ratio3 service_fee_ratio_sum += rl3.service_fee_ratio self.assertAlmostEqual(1.0, ratio_sum, delta=ALMOST_ZERO) self.assertAlmostEqual(0.15, service_fee_ratio_sum, delta=ALMOST_ZERO) self.assertAlmostEqual(0.4, founder_pl.ratio3, delta=ALMOST_ZERO)
def calculate(self, reward_logs): # if address is in address destination dictionary; # then set payment address to mapped address value for rl in self.filterskipped(reward_logs): rl.ratio6 = rl.ratio address_set = set(rl.paymentaddress for rl in self.filterskipped(reward_logs)) payment_address_list_dict = {addr: [] for addr in address_set} # group payments by paymentaddress for rl in self.filterskipped(reward_logs): payment_address_list_dict[rl.paymentaddress].append(rl) reward_data6 = [] for rl in self.iterateskipped(reward_logs): reward_data6.append(rl) for addr, rl_list in payment_address_list_dict.items(): if len(rl_list) > 1: total_staking_balance = sum( [rl.staking_balance for rl in rl_list]) total_current_balance = sum( [rl.current_balance for rl in rl_list]) total_ratio = sum([rl.ratio for rl in rl_list]) total_payment_amount = sum([rl.amount for rl in rl_list]) total_adjusted_payment_amount = sum( [rl.adjusted_amount for rl in rl_list]) total_adjustment = sum([rl.adjustment for rl in rl_list]) total_service_fee_amount = sum( [rl.service_fee_amount for rl in rl_list]) total_service_fee_ratio = sum( [rl.service_fee_ratio for rl in rl_list]) merged = RewardLog(addr, TYPE_MERGED, total_staking_balance, total_current_balance) merged.ratio = total_ratio merged.amount = total_payment_amount merged.adjusted_amount = total_adjusted_payment_amount merged.adjustment = total_adjustment merged.service_fee_amount = total_service_fee_amount merged.service_fee_ratio = total_service_fee_ratio merged.service_fee_rate = 0 merged.parents = rl_list reward_data6.append(merged) else: reward_data6.append(rl_list[0]) return reward_data6
def test_calculate(self): rewards = [] ratios = [0.25, 0.05, 0.3, 0.15, 0.25] total_reward = 1000 for i, ratio in enumerate(ratios, start=1): rl0 = RewardLog( address="addr" + str(i), type="D", staking_balance=total_reward * ratio, current_balance=0, ) rl0.ratio = ratio rl0.ratio4 = ratio rewards.append(rl0) rewards.append(RewardLog("addrdummy", "D", 0, 0).skip("skipped for testing", 4)) phaseMapping = CalculatePhaseMapping() new_rewards = phaseMapping.calculate(rewards, {"addr2": "addr1"}) # filter out skipped records new_rewards = list(rl for rl in new_rewards if not rl.skipped) # check new ratios sum up to 1 # old and new reward amount is the same ratio_sum = sum(rl.ratio5 for rl in new_rewards) # payment address for address2 is address1 payment_address_set = set(rl.paymentaddress for rl in new_rewards) self.assertEqual(4, len(payment_address_set)) self.assertAlmostEqual(1.0, ratio_sum, delta=ALMOST_ZERO) # ratio of records having payment address addr1 must be 0.30 (0.25+0.05) self.assertAlmostEqual( 0.30, sum( rl.ratio for rl in list( filter( lambda rl: rl.paymentaddress == "addr1", filter(lambda rl: not rl.skipped, new_rewards), ) ) ), delta=ALMOST_ZERO, )
def test_calculate_sepecials(self): rewards = [] ratios = [0.25, 0.05, 0.3, 0.15, 0.25] total_reward = 1000 for i, ratio in enumerate(ratios, start=1): rl0 = RewardLog(address="addr" + str(i), type="D", balance=total_reward * ratio) rl0.ratio = ratio rl0.ratio2 = ratio rewards.append(rl0) rewards.append( RewardLog("addrdummy", "D", 0).skip("skipped for testing", 2)) excluded_set = {"addr1"} supporters_set = {"addr2"} specials_map = {"addr3": 30} fee_calculator = ServiceFeeCalculator(supporters_set, specials_map, 20) # 20% fee phase3 = CalculatePhase3(fee_calculator, excluded_set) new_rewards, new_total_reward = phase3.calculate(rewards, total_reward) # filter out skipped records new_rewards = list(rl for rl in new_rewards if not rl.skipped) # new_total_reward = total_reward self.assertEqual(total_reward, new_total_reward) # check new ratios sum up to 1 # old and new reward amount is the same ratio_sum = 0.0 service_fee_ratio_sum = 0.0 founder_pl = None for rl3 in new_rewards: if rl3.skipped: continue if rl3.type == TYPE_FOUNDERS_PARENT: founder_pl = rl3 ratio_sum += rl3.ratio3 service_fee_ratio_sum += rl3.service_fee_ratio self.assertAlmostEqual(1.0, ratio_sum, delta=1e-6) self.assertAlmostEqual(0.17, service_fee_ratio_sum, delta=1e-6) self.assertAlmostEqual(0.42, founder_pl.ratio3, delta=1e-6) for rl3 in new_rewards: if rl3.skipped: continue if rl3.address == "addr2": self.assertEqual(0, rl3.service_fee_rate) self.assertEqual(0, rl3.service_fee_ratio) if rl3.address == "addr3": self.assertEqual(0.3, rl3.service_fee_rate) self.assertEqual(specials_map["addr3"] / 100 * rl3.ratio2, rl3.service_fee_ratio)