コード例 #1
0
    def _get_effective_share_info(self, share_token_items, total_share_token_amount, db_session):
        effective_share_dict = {}
        position_holder_dict = {}
        share_token_dict = {}
        total_effective_share_amount = Decimal(0)
        for item in share_token_items:
            share_token_dict[item.holder] = item.balance
           
        token_map = self._get_token_map(db_session)
        perp_addr = token_map[self._share_token_address].get('perp_addr')
        amm_proxy_addr = token_map[self._share_token_address].get('amm_proxy_addr')
        position_items = db_session.query(PositionBalance)\
            .filter(PositionBalance.perpetual_address == perp_addr)\
            .all()
        for item in position_items:
            position_holder_dict[item.holder] = item.balance
        amm_position = position_holder_dict[amm_proxy_addr]

        for holder, holder_position_in_margin_account in position_holder_dict.items():
            holder_share_token_amount = share_token_dict.get(holder)
            if holder_share_token_amount == Decimal(0) or holder_share_token_amount is None:
                continue
            holder_position_in_amm = Wad.from_number(amm_position) * Wad.from_number(holder_share_token_amount) / Wad.from_number(total_share_token_amount)
            holder_portfolio_position = holder_position_in_amm + Wad.from_number(holder_position_in_margin_account)
            imbalance_rate = abs(holder_portfolio_position / holder_position_in_amm)
            imbalance_rate = Decimal(str(imbalance_rate))
            if imbalance_rate <= Decimal(0.1):
                holder_effective_share = holder_share_token_amount
            elif imbalance_rate >= Decimal(0.9):
                holder_effective_share = holder_share_token_amount * Decimal(0.1)
            else:
                holder_effective_share = holder_share_token_amount * (Decimal(89/80) - imbalance_rate * Decimal(9/8))
            effective_share_dict[holder] = holder_effective_share
            total_effective_share_amount += holder_effective_share
        return effective_share_dict, total_effective_share_amount
コード例 #2
0
 def _get_keeper_liquidate_amount(self, keeper_account):
     markPrice = self.perp.markPrice()
     availableMargin = self.perp.getAvailableMargin(keeper_account)
     if config.INVERSE:
         markPrice = Wad.from_number(1)/markPrice
     amount = int(availableMargin * Wad.from_number(config.LEVERAGE) * markPrice)
     amount = math.floor(amount/config.LOT_SIZE)*config.LOT_SIZE
     return Wad.from_number(amount)
コード例 #3
0
    def _get_holder_reward_weight(self, block_number, pool_value_info,
                                  db_session):
        holder_amms_weight_dict = {}
        holder_amms_reward_dict = {}
        amms_pool_total_reward = Decimal(0)

        for pool_name in pool_value_info.keys():
            total_share_token_amount = pool_value_info[pool_name][
                'total_share_token_amount']
            if total_share_token_amount == 0:
                continue
            share_token_items = pool_value_info[pool_name]['share_token_items']
            pool_type = pool_value_info[pool_name]['pool_type']
            pool_reward = pool_value_info[pool_name]['pool_reward']
            amms_pool_total_reward += Decimal(str(pool_reward))

            for item in share_token_items:
                holder = item.holder
                if item.balance == Decimal(0):
                    continue
                # amm pool, use effective share after xia_rebalance_hard_fork block number
                if pool_type == 'AMM' and block_number >= self._xia_rebalance_hard_fork_block_number:
                    total_effective_share_amount = pool_value_info[pool_name][
                        'total_effective_share_amount']
                    holder_effective_share_amount = pool_value_info[pool_name][
                        'effective_share_dict'].get(holder, Decimal(0))
                    wad_reward = pool_reward * Wad.from_number(
                        holder_effective_share_amount) / Wad.from_number(
                            total_effective_share_amount)
                    reward = Decimal(str(wad_reward))
                    if holder not in holder_amms_reward_dict:
                        holder_amms_reward_dict[holder] = reward
                    else:
                        holder_amms_reward_dict[holder] += reward

        self._save_theory_mining_reward('AMM', holder_amms_reward_dict,
                                        db_session)

        holder_mcb_balance_dict = self._get_holder_mcb_balance(db_session)
        total_holder_reward_weight = Decimal(0)
        for holder, reward in holder_amms_reward_dict.items():
            holder_reward_percent = reward / amms_pool_total_reward
            holder_mcb_balance = holder_mcb_balance_dict.get(
                holder, Decimal(0))
            reward_factor = self._get_holder_reward_factor(
                holder, reward, holder_mcb_balance)
            total_holder_reward_weight += holder_reward_percent * reward_factor

        for holder, reward in holder_amms_reward_dict.items():
            holder_mcb_balance = holder_mcb_balance_dict.get(
                holder, Decimal(0))
            reward_factor = self._get_holder_reward_factor(
                holder, reward, holder_mcb_balance)
            holder_amms_weight_dict[
                holder] = reward_factor / total_holder_reward_weight

        return holder_amms_weight_dict
コード例 #4
0
 def disperse_token(self, token: Address, addresses: list, amounts: list,
                    user: Address, gasPrice: int):
     amounts_toWad = []
     for amount in amounts:
         amounts_toWad.append(Wad.from_number(amount).value)
     tx_hash = self.contract.functions.disperseToken(
         token.address, addresses, amounts_toWad).transact({
             'from':
             user.address,
             'gasPrice':
             gasPrice
         })
     return tx_hash
コード例 #5
0
 def _get_pool_usd_value(self, block_number, pool_name,
                         pool_share_token_address, inverse, db_session):
     amm_usd_value = Decimal(0)
     amm_position = Decimal(0)
     token_map = self._get_token_map(pool_share_token_address, db_session)
     perp_addr = token_map[pool_share_token_address].get('perp_addr')
     amm_proxy_addr = token_map[pool_share_token_address].get(
         'amm_proxy_addr')
     amm_proxy_item = db_session.query(PositionBalance)\
         .filter(PositionBalance.perpetual_address == perp_addr)\
         .filter(PositionBalance.holder == amm_proxy_addr)\
         .first()
     if amm_proxy_item:
         amm_position = amm_proxy_item.balance
     if inverse:
         amm_usd_value = abs(Wad.from_number(amm_position))
     else:
         chain_link_price = self._get_chain_link_price(
             block_number, pool_name, db_session)
         # vanilla contract
         amm_usd_value = abs(
             Wad.from_number(amm_position) *
             Wad.from_number(chain_link_price))
     return amm_usd_value
コード例 #6
0
    def _close_position_in_AMM(self):
        margin_account = self.perp.getMarginAccount(self.keeper_account)
        size = int(margin_account.size)
        if size < config.POSITION_LIMIT:
            return

        deadline = int(time.time()) + config.DEADLINE
        amm_available_margin = self.AMM.current_available_margin()
        self.logger.info(f"amm_available_margin:{amm_available_margin}")
        amm_position_size = self.AMM.position_size()
        self.logger.info(f"amm_position_size:{amm_position_size}")

        trade_side = PositionSide.LONG if margin_account.side == PositionSide.SHORT else PositionSide.SHORT
        try:
            trade_price = compute_AMM_price(amm_available_margin, amm_position_size, trade_side, margin_account.size)
            self.logger.info(f"compute_price:{trade_price}")
        except Exception as e:
            self.logger.fatal(f"compute amm price failed. error:{e}")
            return
        trade_price = trade_price*Wad.from_number(1 - config.PRICE_SLIPPAGE) if trade_side == PositionSide.SHORT else trade_price*Wad.from_number(1 + config.PRICE_SLIPPAGE)

        tx_hash = None
        try:
            if trade_side == PositionSide.LONG:
               tx_hash = self.AMM.buy(margin_account.size, trade_price, deadline, self.keeper_account, self.gas_price)
            else:
               tx_hash = self.AMM.sell(margin_account.size, trade_price, deadline, self.keeper_account, self.gas_price)
            self.logger.info(f"close in AMM success. price:{trade_price} size{margin_account.size}")
            # wait transaction times is 1, cause amm transaction deadline is 120s, if wait timeout, transaction will fail, no need to add gas price
            transaction_status = self._wait_transaction_receipt(tx_hash, 1)
            if transaction_status:
                self.logger.info(f"close position in AMM success. price:{trade_price} size:{margin_account.size}")
            else:
                self.logger.info(f"close position in AMM fail. price:{trade_price} amount:{margin_account.size}")
        except Exception as e:
                self.logger.fatal(f"close position in AMM failed. price:{trade_price} size:{margin_account.size} error:{e}")
コード例 #7
0
    def sync(self, watcher_id, block_number, block_hash, db_session):
        """Sync data"""
        if block_number < self._begin_block or block_number > self._end_block:
            self._logger.info(f'block_number {block_number} not in mining window!')
            return
        
        result = db_session.query(TokenBalance)\
            .filter(TokenBalance.token == self._share_token_address)\
            .with_entities(
                func.sum(TokenBalance.balance)
        ).first()
        if result[0] is None:
            self._logger.warning(f'opps, token_balance is empty!')
            return
        total_share_token_amount = result[0]

        # get all immature summary items
        immature_summary_dict = {}
        immature_summary_items = db_session.query(ImmatureMiningRewardSummary)\
            .filter(ImmatureMiningRewardSummary.mining_round == self._mining_round)\
            .all()
        for item in immature_summary_items:
            immature_summary_dict[item.holder] = item    

        share_token_items = db_session.query(TokenBalance)\
            .filter(TokenBalance.token == self._share_token_address)\
            .with_entities(
                TokenBalance.holder,
                TokenBalance.balance
        ).all()        
        self._logger.info(f'sync mining reward, block_number:{block_number}, holders:{len(share_token_items)}')
        
        # check rebalance_hard_fork block number 
        if block_number >= self._rebalance_hard_fork_block_number:
            effective_share_dict, total_effective_share_amount = self._get_effective_share_info(share_token_items, total_share_token_amount, db_session)

        for item in share_token_items:
            holder = item.holder
            if block_number >= self._rebalance_hard_fork_block_number:
                holder_effective_share_amount = effective_share_dict.get(holder, Decimal(0))
                wad_reward = Wad.from_number(self._reward_per_block) * Wad.from_number(holder_effective_share_amount) / Wad.from_number(total_effective_share_amount)
                reward = Decimal(str(wad_reward))
            else:
                holder_share_token_amount = Decimal(item.balance)
                wad_reward = Wad.from_number(self._reward_per_block) * Wad.from_number(holder_share_token_amount) / Wad.from_number(total_share_token_amount)
                reward = Decimal(str(wad_reward))

            immature_mining_reward = ImmatureMiningReward()
            immature_mining_reward.block_number = block_number
            immature_mining_reward.mining_round = self._mining_round
            immature_mining_reward.holder = holder
            immature_mining_reward.mcb_balance = reward
            db_session.add(immature_mining_reward)

            # update immature_mining_reward_summaries table, simulated materialized view
            if holder not in immature_summary_dict.keys():
                immature_summary_item = ImmatureMiningRewardSummary()
                immature_summary_item.mining_round = self._mining_round
                immature_summary_item.holder = holder
                immature_summary_item.mcb_balance = reward
            else:
                immature_summary_item = immature_summary_dict[holder]
                immature_summary_item.mcb_balance += reward
            db_session.add(immature_summary_item)
コード例 #8
0
 def _get_calculate_liquidate_amount(self, address):
     markPrice = self.perp.markPrice()
     cal_amount = int(self.perp.calculateLiquidateAmount(address, markPrice))
     cal_amount = math.ceil(cal_amount/config.LOT_SIZE)*config.LOT_SIZE
     return Wad.from_number(cal_amount)
コード例 #9
0
    def _get_effective_share_info(self, block_number, pool_share_token_address,
                                  share_token_items, total_share_token_amount,
                                  db_session):
        effective_share_dict = {}
        position_holder_dict = {}
        share_token_dict = {}
        total_effective_share_amount = Decimal(0)
        for item in share_token_items:
            share_token_dict[item.holder] = item.balance

        if block_number >= self._zhou_begin_block_number:
            # period from ZHOU, pool_effective_usd_value is pool_usd_value
            effective_share_dict = share_token_dict
            total_effective_share_amount = total_share_token_amount
            return effective_share_dict, total_effective_share_amount

        token_map = self._get_token_map(pool_share_token_address, db_session)
        perp_addr = token_map[pool_share_token_address].get('perp_addr')
        amm_proxy_addr = token_map[pool_share_token_address].get(
            'amm_proxy_addr')
        position_items = db_session.query(PositionBalance)\
            .filter(PositionBalance.perpetual_address == perp_addr)\
            .all()
        for item in position_items:
            position_holder_dict[item.holder] = item.balance
        amm_position = position_holder_dict.get(amm_proxy_addr, Decimal(0))

        for holder, holder_position_in_margin_account in position_holder_dict.items(
        ):
            holder_share_token_amount = share_token_dict.get(holder)
            if holder_share_token_amount == Decimal(
                    0) or holder_share_token_amount is None:
                continue
            holder_position_in_amm = Wad.from_number(
                amm_position) * Wad.from_number(
                    holder_share_token_amount) / Wad.from_number(
                        total_share_token_amount)
            holder_portfolio_position = holder_position_in_amm + Wad.from_number(
                holder_position_in_margin_account)
            imbalance_rate = abs(holder_portfolio_position /
                                 holder_position_in_amm)
            imbalance_rate = Decimal(str(imbalance_rate))
            if self._mining_round == 'XIA':
                if imbalance_rate <= Decimal(0.1):
                    holder_effective_share = holder_share_token_amount
                elif imbalance_rate >= Decimal(0.9):
                    holder_effective_share = holder_share_token_amount * Decimal(
                        0.1)
                else:
                    holder_effective_share = holder_share_token_amount * (
                        Decimal(89 / 80) - imbalance_rate * Decimal(9 / 8))
            elif self._mining_round == 'SHANG':
                if imbalance_rate <= Decimal(0.2):
                    holder_effective_share = holder_share_token_amount
                elif imbalance_rate >= Decimal(0.9):
                    holder_effective_share = holder_share_token_amount * Decimal(
                        0.1)
                else:
                    holder_effective_share = holder_share_token_amount * (
                        Decimal(44 / 35) - imbalance_rate * Decimal(9 / 7))
            effective_share_dict[holder] = holder_effective_share
            total_effective_share_amount += holder_effective_share
        return effective_share_dict, total_effective_share_amount
コード例 #10
0
    def _calculate_pools_reward(self, block_number, pool_info,
                                pool_reward_percent, db_session):
        pool_value_info = self._get_pool_value_info(block_number, pool_info,
                                                    pool_reward_percent,
                                                    db_session)
        self._logger.info(
            f'sync mining reward, block_number:{block_number}, pools:{",".join(pool_info.keys())}'
        )

        holder_amms_weight_dict = {}
        holder_weight_dict = {}
        if block_number >= self._qin_begin_block_number:
            holder_amms_weight_dict = self._get_holder_amms_reward_weight(
                block_number, pool_value_info, db_session)
        elif block_number >= self._zhou_begin_block_number:
            holder_weight_dict = self._get_holder_reward_weight(
                block_number, pool_value_info, db_session)

        for pool_name in pool_value_info.keys():
            if block_number >= self._qin_begin_block_number:
                holder_weight_dict = holder_amms_weight_dict.get(pool_name, {})

            # get all immature summary items of pool_name
            immature_summary_dict = {}
            immature_summary_items = db_session.query(ImmatureMiningRewardSummary)\
                .filter(ImmatureMiningRewardSummary.mining_round == self._mining_round)\
                .filter(ImmatureMiningRewardSummary.pool_name == pool_name)\
                .all()
            for item in immature_summary_items:
                immature_summary_dict[item.holder] = item

            total_share_token_amount = pool_value_info[pool_name][
                'total_share_token_amount']
            if total_share_token_amount == 0:
                self._logger.warning(
                    f'opps, pool:{pool_name}, share_token total amount is zero, skip it!'
                )
                continue

            share_token_items = pool_value_info[pool_name]['share_token_items']
            pool_type = pool_value_info[pool_name]['pool_type']
            pool_reward = pool_value_info[pool_name]['pool_reward']

            for item in share_token_items:
                holder = item.holder
                if item.balance == Decimal(0):
                    continue
                # amm pool, use effective share after xia_rebalance_hard_fork block number
                if pool_type == 'AMM' and block_number >= self._xia_rebalance_hard_fork_block_number:
                    holder_weight = holder_weight_dict.get(holder, Decimal(1))
                    total_effective_share_amount = pool_value_info[pool_name][
                        'total_effective_share_amount']
                    holder_effective_share_amount = pool_value_info[pool_name][
                        'effective_share_dict'].get(holder, Decimal(0))
                    wad_reward = Wad.from_number(
                        holder_weight) * pool_reward * Wad.from_number(
                            holder_effective_share_amount) / Wad.from_number(
                                total_effective_share_amount)
                    reward = Decimal(str(wad_reward))
                else:
                    holder_share_token_amount = Decimal(item.balance)
                    wad_reward = pool_reward * Wad.from_number(
                        holder_share_token_amount) / Wad.from_number(
                            total_share_token_amount)
                    reward = Decimal(str(wad_reward))

                immature_mining_reward = ImmatureMiningReward()
                immature_mining_reward.block_number = block_number
                immature_mining_reward.pool_name = pool_name
                immature_mining_reward.mining_round = self._mining_round
                immature_mining_reward.holder = holder
                immature_mining_reward.mcb_balance = reward
                db_session.add(immature_mining_reward)

                # update immature_mining_reward_summaries table, simulated materialized view
                if holder not in immature_summary_dict.keys():
                    immature_summary_item = ImmatureMiningRewardSummary()
                    immature_summary_item.mining_round = self._mining_round
                    immature_summary_item.pool_name = pool_name
                    immature_summary_item.holder = holder
                    immature_summary_item.mcb_balance = reward
                else:
                    immature_summary_item = immature_summary_dict[holder]
                    immature_summary_item.mcb_balance += reward
                db_session.add(immature_summary_item)
コード例 #11
0
    def _get_pool_value_info(self, block_number, pool_info,
                             pool_reward_percent, db_session):
        if self._mining_round == 'QIN' and block_number < self._qin_reduce_reward_block_number:
            self._reward_per_block = 2
        elif self._mining_round == 'QIN' and block_number >= self._qin_reduce_reward_block_number:
            self._reward_per_block = 0.2
        # support vote https://vote.mcdex.io/mainnet/proposal/14,  blocknumber >=11601000 && blocknumber <11685000 reward 0.1875
        elif block_number >= 11601000 and block_number < 11685000:
            self._reward_per_block = 0.1875
        # update uniswap_pool_proportion, every block update
        self._update_uniswap_pool_proportion(pool_info, db_session)

        pool_value_info = {}
        pools_total_effective_value = Wad(0)
        for pool_name in pool_info.keys():
            pool_share_token_address = pool_info[pool_name].get(
                'pool_share_token_address')
            if pool_name not in pool_value_info.keys():
                pool_value_info[pool_name] = {}
            pool_value_info[pool_name][
                'pool_share_token_address'] = pool_share_token_address

            # use for amm pools reward distribute
            amm_pool_proportion = pool_info[pool_name].get(
                'amm_pool_proportion', 1)
            pool_value_info[pool_name][
                'amm_pool_proportion'] = amm_pool_proportion

            # use for uniswap pools reward distribute
            uniswap_pool_proportion = pool_info[pool_name].get(
                'uniswap_pool_proportion', 1)
            pool_value_info[pool_name][
                'uniswap_pool_proportion'] = uniswap_pool_proportion

            pool_type = pool_info[pool_name].get('pool_type')
            pool_contract_inverse = pool_info[pool_name].get(
                'pool_contract_inverse', True)
            pool_value_info[pool_name]['pool_type'] = pool_type

            total_share_token_amount = self._get_total_share_token_amount(
                pool_share_token_address, db_session)
            pool_value_info[pool_name][
                'total_share_token_amount'] = total_share_token_amount

            share_token_items = self._get_share_token_items(
                pool_share_token_address, db_session)
            pool_value_info[pool_name]['share_token_items'] = share_token_items

            if pool_type == 'AMM' and block_number >= self._xia_rebalance_hard_fork_block_number:
                # pool_type is AMM, use effective share calc reward:
                # 1) period XIA and block_number >=_xia_rebalance_hard_fork_block_number;
                # 2) period SHANG;
                # 3) FROM period ZHOU, effective share eq to holder share
                effective_share_dict, total_effective_share_amount = self._get_effective_share_info(
                    block_number, pool_share_token_address, share_token_items,
                    total_share_token_amount, db_session)
                pool_value_info[pool_name][
                    'effective_share_dict'] = effective_share_dict
                pool_value_info[pool_name][
                    'total_effective_share_amount'] = total_effective_share_amount
                pool_usd_value = self._get_pool_usd_value(
                    block_number, pool_name, pool_share_token_address,
                    pool_contract_inverse, db_session)
                if total_share_token_amount != 0:
                    pool_effective_usd_value = pool_usd_value * Wad.from_number(
                        total_effective_share_amount) / Wad.from_number(
                            total_share_token_amount)
                else:
                    self._logger.warning(
                        f'opps, pool:{pool_name}, share_token total amount is zero, skip it!'
                    )
                    pool_effective_usd_value = Wad(0)
                pool_value_info[pool_name][
                    'pool_effective_usd_value'] = pool_effective_usd_value
                pools_total_effective_value += pool_effective_usd_value
            else:
                # include two case:
                # 1) pool_type is UNISWAP;
                # 2) pool_type is AMM and block number before _xia_rebalance_hard_fork_block_number;
                pool_reward = Wad.from_number(
                    pool_reward_percent) * Wad.from_number(
                        self._reward_per_block) * Wad.from_number(
                            uniswap_pool_proportion)
                pool_value_info[pool_name]['pool_reward'] = pool_reward

        # update AMM pool reward
        if pool_type == 'AMM' and block_number >= self._xia_rebalance_hard_fork_block_number:
            for pool_name in pool_value_info.keys():
                amm_pool_proportion = pool_value_info[pool_name].get(
                    'amm_pool_proportion', 1)
                pool_effective_usd_value = pool_value_info[pool_name][
                    'pool_effective_usd_value']
                if block_number >= self._qin_begin_block_number:
                    pool_reward = Wad.from_number(
                        pool_reward_percent) * Wad.from_number(
                            self._reward_per_block) * Wad.from_number(
                                amm_pool_proportion)
                else:
                    pool_reward = Wad.from_number(
                        pool_reward_percent) * Wad.from_number(
                            self._reward_per_block
                        ) * pool_effective_usd_value / Wad.from_number(
                            pools_total_effective_value)
                pool_value_info[pool_name]['pool_reward'] = pool_reward

        return pool_value_info