Beispiel #1
0
    def propose_trades_for_total_rebalancing(
        self,
        market_state: MarketState,
    ) -> List[AbstractTrade]:
        """A total rebalancing should get us as close as possible to an equal
        distribution of value (w/r/t `self.fiat`) across all "reachable"
        markets (those in which the base currency is `self.fiat`).
        """
        ideal_fiat_value_per_coin = self._ideal_fiat_value_per_coin(
            market_state)

        est_values = market_state.estimate_values(market_state.balances,
                                                  self.fiat)

        coins_to_sell = {}
        coins_to_buy = {}
        for coin in sorted(self._possible_investments(market_state)):
            value = est_values.get(coin, 0)
            delta = value - ideal_fiat_value_per_coin
            if delta > 0:
                coins_to_sell[coin] = delta
            elif delta < 0:
                coins_to_buy[coin] = abs(delta)

        trades_to_fiat = [
            AbstractTrade(sell_coin, self.fiat, self.fiat, fiat_value)
            for sell_coin, fiat_value in coins_to_sell.items()
        ]

        trades_from_fiat = [
            AbstractTrade(self.fiat, buy_coin, self.fiat, fiat_value)
            for buy_coin, fiat_value in coins_to_buy.items()
        ]

        return trades_to_fiat + trades_from_fiat
Beispiel #2
0
    def propose_trades_for_partial_rebalancing(
        self,
        market_state: MarketState,
        coins_to_rebalance: FrozenSet[str],
    ) -> List[AbstractTrade]:
        """TODO: Trade directly from X to Y without going through fiat.
        """
        ideal_fiat_value_per_coin = self._ideal_fiat_value_per_coin(
            market_state)

        est_values = market_state.estimate_values(market_state.balances,
                                                  self.fiat)

        # 1) Fan in to fiat, selling excess value in coins we want to rebalance
        trades_to_fiat = []
        for sell_coin in sorted(coins_to_rebalance):
            if sell_coin == self.fiat:
                continue
            value = est_values.get(sell_coin, 0)
            delta = value - ideal_fiat_value_per_coin
            if delta > 0:
                trades_to_fiat.append(
                    AbstractTrade(sell_coin, self.fiat, self.fiat, delta), )

        # 2) Simulate trades and estimate portfolio state afterwards
        est_balances_after_trades = simulate_trades(
            trades_to_fiat,
            market_state,
        )
        est_values_after_trades = market_state.estimate_values(
            est_balances_after_trades,
            self.fiat,
        )

        fiat_after_trades = est_balances_after_trades[self.fiat]
        fiat_to_redistribute = fiat_after_trades - ideal_fiat_value_per_coin
        if fiat_to_redistribute <= 0:
            return trades_to_fiat

        # 3) Find coins in which we don't hold enough value
        possible_buys = set()
        for buy_coin in self._possible_investments(market_state):
            value = est_values_after_trades.get(buy_coin, 0)
            if ideal_fiat_value_per_coin > value:
                possible_buys.add(buy_coin)

        fiat_to_redistribute_per_coin = fiat_to_redistribute / len(
            possible_buys)

        # 4) Plan trades, fanning back out from fiat to others
        trades_from_fiat = []
        for buy_coin in sorted(possible_buys):
            value = est_values_after_trades.get(buy_coin, 0)
            delta = ideal_fiat_value_per_coin - value
            if delta > 0:
                available_fiat = min(fiat_to_redistribute_per_coin, delta)
                trades_from_fiat.append(
                    AbstractTrade(self.fiat, buy_coin, self.fiat,
                                  available_fiat), )

        return trades_to_fiat + trades_from_fiat