darkie.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. from core.utils import *
  2. from core.strategy import *
  3. class Darkie():
  4. def __init__(self, airdrop, initial_stake=None, vesting=[], hp=False, commit=True, epoch_len=EPOCH_LENGTH, strategy=random_strategy(EPOCH_LENGTH)):
  5. self.vesting = vesting
  6. self.stake = (Num(airdrop) if hp else airdrop)
  7. self.initial_stake = [self.stake]
  8. self.Sigma = None
  9. self.feedback = None
  10. self.f = None
  11. self.epoch_len=epoch_len # epoch length during which the stake is static
  12. self.strategy = strategy
  13. self.slot = 0
  14. self.won_hist = [] # winning history boolean
  15. def clone(self):
  16. return Darkie(self.stake)
  17. """
  18. calculate APY (with compound interest every epoch) every epoch scaled to runningtime
  19. @param rewards: rewards at each epoch
  20. @returns: apy
  21. """
  22. def apy_scaled_to_runningtime(self, rewards):
  23. avg_apy = 0
  24. for idx, reward in enumerate(rewards):
  25. #init_stake = Num(self.initial_stake[idx-1]) if len(self.initial_stake)>=idx else Num(self.initial_stake[-1])
  26. current_epoch_staked_tokens = Num(self.strategy.staked_tokens_ratio[idx-1]) * Num(self.initial_stake[idx-1])
  27. avg_apy += (Num(reward) / current_epoch_staked_tokens) if current_epoch_staked_tokens!=0 else 0
  28. return avg_apy * Num(ONE_YEAR/(self.slot/EPOCH_LENGTH)) if self.slot and self.initial_stake[0]>0 >0 else 0
  29. """
  30. calculate APR every epoch scaled to running time
  31. @returns: apr
  32. """
  33. def apr_scaled_to_runningtime(self):
  34. initial_stake = self.vesting_wrapped_initial_stake()
  35. assert self.stake >= initial_stake, 'stake: {}, initial_stake: {}, slot: {}, current: {}, previous: {} vesting'.format(self.stake, initial_stake, self.slot, self.current_vesting(), self.prev_vesting())
  36. apr_scaled = Num(self.stake - initial_stake) / Num(initial_stake) if initial_stake>0 else 0
  37. if self.slot <= HEADSTART_AIRDROP:
  38. # during this phase, it's only called at end of epoch
  39. apr_period = EPOCH_LENGTH
  40. else:
  41. apr_period = self.slot-HEADSTART_AIRDROP
  42. apr = apr_scaled * Num(ONE_YEAR/apr_period) if initial_stake > 0 and self.slot>0 else 0
  43. #if apr>0 and self.stake-initial_stake>0:
  44. #print("apr: {}, stake: {}, initial_stake: {}".format(apr, self.stake, initial_stake))
  45. return apr
  46. """
  47. add vesting to initial stake
  48. @returns: vesting plus initial stake
  49. """
  50. def vesting_wrapped_initial_stake(self):
  51. #returns vesting stake plus initial stake gained from zero coin headstart during aridrop period
  52. vesting = self.current_vesting()
  53. if self.slot <= HEADSTART_AIRDROP:
  54. initial_stake = self.initial_stake[-1]
  55. else:
  56. initial_stake = self.initial_stake[int(HEADSTART_AIRDROP/EPOCH_LENGTH)]
  57. return vesting + initial_stake
  58. """
  59. update stake with vesting return every scheduled vesting period
  60. """
  61. def update_vesting(self):
  62. self.stake += self.vesting_differential()
  63. """
  64. @returns: current epoch vesting
  65. """
  66. def current_vesting(self):
  67. '''
  68. current corresponding slot vesting
  69. '''
  70. vesting_idx = int(self.slot/VESTING_PERIOD)
  71. return self.vesting[vesting_idx] if vesting_idx < len(self.vesting) else 0
  72. """
  73. @returns: previous epoch vesting
  74. """
  75. def prev_vesting(self):
  76. '''
  77. previous corresponding slot vesting
  78. '''
  79. prev_vesting_idx = int((self.slot-1)/VESTING_PERIOD)
  80. return (self.vesting[prev_vesting_idx] if self.slot>0 else self.current_vesting()) if prev_vesting_idx < len(self.vesting) else 0
  81. def vesting_differential(self):
  82. vesting_value = self.current_vesting() - self.prev_vesting()
  83. return vesting_value
  84. def staked_tokens(self):
  85. '''
  86. the ratio of the staked tokens during the epochs
  87. of the total running time
  88. '''
  89. return Num(self.initial_stake[0])*self.staked_tokens_ratio()
  90. """
  91. @returns: average stakeholder's staked ratio from genesis until current slot
  92. """
  93. def staked_tokens_ratio(self):
  94. staked_ratio = Num(sum(self.strategy.staked_tokens_ratio)/len(self.strategy.staked_tokens_ratio))
  95. assert staked_ratio <= 1 and staked_ratio >=0, 'staked_ratio: {}'.format(staked_ratio)
  96. return staked_ratio
  97. def set_sigma_feedback(self, sigma, feedback, f, count, hp=True):
  98. self.Sigma = (Num(sigma) if hp else sigma)
  99. self.feedback = (Num(feedback) if hp else feedback)
  100. self.f = (Num(f) if hp else f)
  101. self.slot = count
  102. """
  103. @param hp: high precision decimal option
  104. play lottery if stakeholder won, update state
  105. """
  106. def run(self, hp=True):
  107. k=N_TERM
  108. def target(tune_parameter, stake):
  109. x = (Num(1) if hp else 1) - (Num(tune_parameter) if hp else tune_parameter)
  110. c = (x.ln() if type(x)==Num else math.log(x))
  111. sigmas = [ c/((self.Sigma+EPSILON)**i) * ( ((L_HP if hp else L)/fact(i)) ) for i in range(1, k+1) ]
  112. headstart = (BASE_L_HP if hp else BASE_L) if self.slot < HEADSTART_AIRDROP else 0
  113. scaled_target = approx_target_in_zk(sigmas, Num(stake)) + headstart
  114. return scaled_target
  115. if self.slot % EPOCH_LENGTH ==0 and self.slot > 0:
  116. apr = self.apr_scaled_to_runningtime()
  117. # staked ratio is added in strategy
  118. self.strategy.set_ratio(self.slot, apr)
  119. # epoch stake is added
  120. self.initial_stake += [self.stake]
  121. T = target(self.f, self.strategy.staked_value(self.stake))
  122. won = lottery(T, hp)
  123. self.won_hist += [won]
  124. """
  125. update stake upon winning lottery with single lead
  126. """
  127. def update_stake(self, reward):
  128. if self.won_hist[-1]:
  129. self.stake += reward
  130. """
  131. update stake after fork finalization
  132. """
  133. def resync_stake(self, reward):
  134. self.stake += reward
  135. def write(self, idx):
  136. with open('log/darkie'+str(idx)+'.log', 'w+') as f:
  137. buf = 'initial stake:'+','.join([str(i) for i in self.initial_stake])
  138. buf += '\r\n'
  139. buf += '(apr,staked ratio,{}):'.format(self.strategy.type)+','.join(['('+str(apr)+','+str(sr)+')' for sr, apr in zip(self.strategy.staked_tokens_ratio, self.strategy.annual_return)])
  140. buf+='\r\n'
  141. buf += 'apr: {}'.format(self.apr_scaled_to_runningtime())
  142. f.write(buf)