def get_simulation_results(c): df = run_simulation(c) df_final = df[df.substep.eq(2)] random_func = new_random_number_func(None) last_network = df_final.iloc[-1, 0] candidates = len( get_proposals(last_network, status=ProposalStatus.CANDIDATE)) actives = len(get_proposals(last_network, status=ProposalStatus.ACTIVE)) completed = len( get_proposals(last_network, status=ProposalStatus.COMPLETED)) failed = len(get_proposals(last_network, status=ProposalStatus.FAILED)) participants = len(get_participants(last_network)) score = CommonsScore(params=c, df_final=df_final) result = { "timestep": list(df_final["timestep"]), "funding_pool": list(df_final["funding_pool"]), "token_price": list(df_final["token_price"]), "sentiment": list(df_final["sentiment"]), "score": score.eval(), "participants": participants, "proposals": { "candidates": candidates, "actives": actives, "completed": completed, "failed": failed, "total": candidates + actives + completed + failed } } return result, df_final
def su_update_participants_token_batch_age(params, step, sL, s, _input, **kwargs): network = s["network"] participants = get_participants(network) for i, participant in participants: participant.update_token_batch_age() return "network", network
def test_p_participant_decides_if_he_wants_to_exit(self): participants = get_participants(self.network) for i, participant in participants: participant.sentiment = 0.1 self.params["probability_func"] = always ans = ParticipantExits.p_participant_decides_if_he_wants_to_exit( self.params, 0, 0, self.default_state) self.assertEqual(len(ans["defectors"]), 2)
def p_decide_to_buy_tokens_bulk(params, step, sL, s, **kwargs): network = s["network"] commons = s["commons"] participants = get_participants(network) ans = {} total_dai = 0 for i, participant in participants: # If a participant decides to buy, it will be specified in units of DAI. # If a participant decides to sell, it will be specified in units of tokens. x = participant.buy() if x > 0: total_dai += x ans[i] = x # Now that we have the sum of DAI, ask the Commons object how many # tokens this would be minted as a result. This will be inaccurate due # to slippage, and we need the result of this policy to be final to # avoid chaining 2 state update functions, so we run the deposit on a # throwaway copy of Commons if total_dai == 0: if params.get("debug"): print( "ParticipantBuysTokens: No Participants bought tokens in timestep {}" .format(step)) return { "participant_decisions": ans, "total_dai": 0, "tokens": 0, "token_price": 0, "final_token_distribution": {} } else: commons2 = copy.copy(commons) tokens, token_price = commons2.deposit(total_dai) final_token_distribution = {} for i in ans: final_token_distribution[i] = ans[i] / total_dai if params.get("debug"): print( "ParticipantBuysTokens: These Participants have decided to buy tokens with this amount of DAI: {}" .format(ans)) print( "ParticipantBuysTokens: A total of {} DAI will be deposited. {} tokens should be minted as a result at a price of {} DAI/token" .format(total_dai, tokens, token_price)) return { "participant_decisions": ans, "total_dai": total_dai, "tokens": tokens, "token_price": token_price, "final_token_distribution": final_token_distribution }
def test_p_participant_decides_if_he_wants_to_exit(self): participants = get_participants(self.network) for i, participant in participants: participant.sentiment = 0.1 with patch("entities.probability") as p: p.return_value = True ans = ParticipantExits.p_participant_decides_if_he_wants_to_exit( self.params, 0, 0, self.default_state) self.assertEqual(len(ans["defectors"]), 4)
def test_su_update_participants_token_batch_age(self): """ Test that after running the state update function the participants' token batch age in days are incremented by one """ _input = {} _, network = GenerateNewParticipant.su_update_participants_token_batch_age( self.params, 0, 0, {"network": self.network.copy()}, _input) participants = get_participants(network) for i, participant in participants: self.assertEqual(participant.holdings.age_days, 1)
def su_update_sentiment_decay(params, step, sL, s, _input, **kwargs): network = s["network"] participants = get_participants(network) for participant_idx, participant in participants: sentiment_old = network.nodes[participant_idx]["item"].sentiment sentiment_new = sentiment_old - config.sentiment_decay sentiment_new = 0 if sentiment_new < 0 else sentiment_new network.nodes[participant_idx]["item"].sentiment = sentiment_new return "network", network
def p_decide_to_sell_tokens_bulk(params, step, sL, s, **kwargs): network = s["network"] commons = s["commons"] participants = get_participants(network) ans = {} total_tokens = 0 for i, participant in participants: # If a participant decides to buy, it will be specified in units of DAI. # If a participant decides to sell, it will be specified in units of tokens. x = participant.sell() if x > 0: total_tokens += x ans[i] = x # Now that we have the sum of tokens, ask the Commons object how many # DAI would be redeemed as a result. This will be inaccurate due # to slippage, and we need the result of this policy to be final to # avoid chaining 2 state update functions, so we run the operation on a # throwaway copy of Commons if total_tokens == 0: if params.get("debug"): print( "ParticipantSellsTokens: No Participants sold tokens in timestep {}" .format(step)) return { "participant_decisions": ans, "total_tokens": 0, "dai_returned": 0, "realized_price": 0 } else: commons2 = copy.copy(commons) dai_returned, realized_price = commons2.burn(total_tokens) final_dai_distribution = {} for i, participant in participants: final_dai_distribution[i] = ans[i] / total_tokens if params.get("debug"): print( "ParticipantSellsTokens: These Participants have decided to sell this many tokens: {}" .format(ans)) print( "ParticipantSellsTokens: A total of {} tokens will be burned. {} DAI should be returned as a result, at a price of {} DAI/token" .format(total_tokens, dai_returned, realized_price)) return { "participant_decisions": ans, "total_tokens": total_tokens, "dai_returned": dai_returned, "realized_price": realized_price }
def test_bootstrap_network(self): """ Tests that the network was created and that the subcomponents work too. """ token_batches = [TokenBatch(1000, VestingOptions(10, 30)) for _ in range(4)] network = bootstrap_network(token_batches, 1, 3000, 4e6, 0.2) edges = list(network.edges(data="type")) _, _, edge_types = list(zip(*edges)) self.assertEqual(edge_types.count('support'), 4) self.assertEqual(len(get_participants(network)), 4) self.assertEqual(len(get_proposals(network)), 1)
def p_randomly(params, step, sL, s): """ Randomly picks a Participant from the network and asks him if he wants to create a Proposal. """ funding_pool = s["funding_pool"] network = s["network"] participants = get_participants(network) i, participant = random.sample(participants, 1)[0] wants_to_create_proposal = participant.create_proposal(calc_total_funds_requested( network), calc_median_affinity(network), funding_pool) return {"new_proposal": wants_to_create_proposal, "proposed_by_participant": i}
def p_participant_decides_if_he_wants_to_exit(params, step, sL, s, **kwargs): network = s["network"] participants = get_participants(network) defectors = {} for i, participant in participants: e = participant.wants_to_exit() if e: defectors[i] = { "sentiment": participant.sentiment, "holdings": participant.holdings.total, } if params.get("debug"): print("ParticipantExits: Participants {} (2nd number is their sentiment) want to exit".format( defectors)) return {"defectors": defectors}
def p_participant_votes_on_proposal_according_to_affinity( params, step, sL, s, **kwargs): """ This policy collects data from the DiGraph to tell the Participant class which candidate proposals it supports, and Participant.vote_on_candidate_proposals() will decide which proposals it will take action on. Then, Participant.stake_across_all_supported_proposals() will tell us how much it will stake on each of them. """ network = s["network"] participants = get_participants(network) participants_stakes = {} for participant_idx, participant in participants: proposal_idx_affinity = {} # {4: 0.9, 5: 0.9} candidate_proposals = get_proposals_by_participant_and_status( network, participant_idx=participant_idx, status_filter=[ProposalStatus.CANDIDATE]) for proposal_idx, proposal in candidate_proposals.items(): proposal_idx_affinity[proposal_idx] = proposal[ "support"].affinity proposals_that_participant_cares_enough_to_vote_on = participant.vote_on_candidate_proposals( proposal_idx_affinity) stake_across_all_supported_proposals_input = [] for proposal_idx, affinity in proposals_that_participant_cares_enough_to_vote_on.items( ): stake_across_all_supported_proposals_input.append( (affinity, proposal_idx)) stakes = participant.stake_across_all_supported_proposals( stake_across_all_supported_proposals_input) participants_stakes[participant_idx] = stakes if params.get("debug"): if proposals_that_participant_cares_enough_to_vote_on: print( "ParticipantVoting: Participant {} was given Proposals with corresponding affinities {} and he decided to vote on {}, distributing his tokens thusly {}" .format( participant_idx, proposal_idx_affinity, proposals_that_participant_cares_enough_to_vote_on, stakes)) return {"participants_stake_on_proposals": participants_stakes}
def test_calc_avg_sentiment(self): """ Ensure that the average sentiment was calculated correctly """ participants = get_participants(self.network) for _, p in participants: p.sentiment = 0.5 self.assertEqual(0.5, calc_avg_sentiment(self.network)) for _, p in participants: p.sentiment = 1 self.assertEqual(1, calc_avg_sentiment(self.network)) for _, p in participants: p.sentiment = 1 self.network.nodes[8]["item"].sentiment = 0.5 self.assertEqual(0.9, calc_avg_sentiment(self.network))
def p_randomly(params, step, sL, s, **kwargs): """ Randomly picks a Participant from the network and asks him if he wants to create a Proposal. """ funding_pool = s["funding_pool"] network = s["network"] choice_func = params["choice_func"] participants = get_participants(network) participants_dict = dict(participants) i = choice_func(list(participants_dict)) participant = participants_dict[i] wants_to_create_proposal = participant.create_proposal(calc_total_funds_requested( network), calc_median_affinity(network), funding_pool) return {"new_proposal": wants_to_create_proposal, "proposed_by_participant": i}
def test_su_update_sentiment_decay(self): """ Test that the participants' sentiment is decreased by the value defined on config.sentiment_decay after executing the state update function su_update_sentiment_decay. """ participants_old_sentiment = [] participants = get_participants(self.network) for _, participant in participants: participants_old_sentiment.append(participant.sentiment) network = ParticipantSentiment.su_update_sentiment_decay( self.params, 0, 0, {"network": self.network.copy()}, {}) for i, participant in participants: old_sentiment = participants_old_sentiment[i] sentiment_decay = old_sentiment - participant.sentiment self.assertAlmostEqual(sentiment_decay, config.sentiment_decay)
def test_participant_token_batch_age_is_updated_every_timestep(self): """ Test that the age of the Participants' token batch is updated every timestep. The test checks if the older token batch age has the same age of the simulation (timestep). It considers that at least one participant stays in the commons from the beginning to the end of the simulation. """ for index, row in self.df_final.iterrows(): timestep = row["timestep"] network = row["network"] participants = get_participants(network) participants_token_batch_ages = [] for i, participant in participants: participants_token_batch_ages.append( participant.holdings.age_days) # Check if the older token batch has the same age of the simulation self.assertEqual(max(participants_token_batch_ages), timestep)
def test_p_participant_votes_on_proposal_according_to_affinity_vesting_nonvesting( self): """ Ensure that when a Participant with vesting and nonvesting tokens votes on 2 Proposals of equal affinity, all of his tokens will be split 50-50 between them. """ participants = get_participants(self.network) for _, participant in participants: participant.holdings = TokenBatch(1000, 1000) with patch("entities.probability") as p: p.return_value = True ans = ParticipantVoting.p_participant_votes_on_proposal_according_to_affinity( self.params, 0, 0, { "network": copy.copy(self.network), "funding_pool": 1000, "token_supply": 1000 }) reference = { 'participants_stake_on_proposals': { 0: { 4: 1000.0, 5: 1000.0 }, 1: { 4: 1000.0, 5: 1000.0 }, 2: { 4: 1000.0, 5: 1000.0 }, 3: { 4: 1000.0, 5: 1000.0 } } } self.assertEqual(ans, reference)
def eval(self) -> float: ''' Calculates the final score using all the defined metrics methods in this class ''' last_network = self.df_final.iloc[-1, 0] p_candidates = get_proposals(last_network, status=ProposalStatus.CANDIDATE) candidates = len(p_candidates) p_actives = get_proposals(last_network, status=ProposalStatus.ACTIVE) actives = len(p_actives) p_completed = get_proposals(last_network, status=ProposalStatus.COMPLETED) completed = len(p_completed) p_failed = get_proposals(last_network, status=ProposalStatus.FAILED) failed = len(p_failed) participants = len(get_participants(last_network)) funds_candidates = sum([p.funds_requested for _, p in p_candidates]) funds_actives = sum([p.funds_requested for _, p in p_actives]) funds_completed = sum([p.funds_requested for _, p in p_completed]) funds_failed = sum([p.funds_requested for _, p in p_failed]) self.metrics = Metrics(participants=participants, candidates=candidates, funds_candidates=funds_candidates, actives=actives, funds_actives=funds_actives, completed=completed, funds_completed=funds_completed, failed=failed, funds_failed=funds_failed) methods = [ attr for attr in dir(self) if callable(getattr(self, attr)) and attr.startswith('calc_') ] return min( round( sum([getattr(self, method)() for method in methods]) * self.sigma), 1000)
def test_get_participants(self): res = get_participants(self.network) self.assertEqual(len(res), 5)