Exemple #1
0
    def get_fund_with(self,
                      currency,
                      gain_greater_than: float = 0.0,
                      force_include_target=True) -> Optional[Fund]:
        if currency == self.target_currency and force_include_target:
            return Fund(self._currency_positions[currency].total_amount,
                        currency)

        profitable_amount = self._currency_positions[currency].get_amount_with(
            self._market, gain_greater_than)
        if profitable_amount > 0:
            return Fund(profitable_amount, currency)

        return None
Exemple #2
0
    def funds(self) -> List[Fund]:
        result = []
        for position in self._currency_positions.values():
            if position.total_amount > 0:
                result.append(Fund(position.total_amount, position.currency))

        return result
Exemple #3
0
    def _web_request_transfer(self, source_amount, source_currency,
                              target_currency):
        portfolio: PortfolioController = self._last_portfolio
        state = portfolio._current_portfolio_state
        try:
            if source_amount.strip().lower() == "all":
                source_amount = portfolio.get_fund_with(
                    source_currency, gain_greater_than=0).amount
            else:
                source_amount = float(source_amount)

            commands = portfolio.create_transfer_commands(
                Fund(source_amount, source_currency), target_currency)
            if not commands:
                return f"Transfer path is not available between {source_currency} to {target_currency}"

            state = deepcopy(state)
            for command in commands:
                command.apply(state, portfolio._market)

        except Exception as e:
            return str(e)

        is_success = True
        for command in commands:
            log_command(command)
            is_success = is_success and self.portfolio_connector.execute(
                command)

        if is_success:
            return f"Successful transfer: {commands}"
        else:
            return f"Transfer declined: {commands}"
Exemple #4
0
    def total_value(self):
        present = self.present

        accumulator = Fund(0, self._market.target_currency)
        for fund in self.funds:
            accumulator += present.get_value(fund)

        return accumulator
Exemple #5
0
    def get_funds_with(self,
                       gain_greater_than: float,
                       force_include_target: bool = True) -> List[Fund]:
        result = []
        for position in self._currency_positions.values():
            if position.currency == self.target_currency and force_include_target:
                if position.total_amount > 0:
                    result.append(
                        Fund(position.total_amount, position.currency))
                continue

            for bucket_amount in position.get_bucket_amounts_with(
                    self._market, gain_greater_than):
                if bucket_amount > 0:
                    result.append(Fund(bucket_amount, position.currency))

        return result
Exemple #6
0
    def to_convert(self, fund: Fund):
        is_reversed = self.target_currency == fund.currency
        is_valid = is_reversed or self.source_currency == fund.currency

        if not is_valid:
            raise ValueError(
                f"Can't process sell of {fund} on pair {self._reader.pair}")

        # todo consider levels
        # todo consider fees
        bid, ask = self.bid_ask
        if is_reversed:
            price_per_source_unit = bid
            return Fund(fund.amount / price_per_source_unit,
                        self.source_currency)
        else:
            price_per_source_unit = ask
            return Fund(fund.amount * price_per_source_unit,
                        self.target_currency)
Exemple #7
0
    def update_portfolio(self, portfolio: PortfolioController):
        if not self._is_web_started:
            self._is_web_started = True
            self._start_server(portfolio.target_currency, portfolio.currencies,
                               portfolio.pairs)

        self._last_portfolio = portfolio

        present = portfolio.present

        state = {}
        state["timestamp"] = present.timestamp
        state["total_value"] = portfolio.total_value.amount

        state["funds"] = funds = {}
        for currency in portfolio.currencies:
            funds[currency] = {
                "amount": 0.0,
                "initial_value": 0.0,
                "current_value": 0.0
            }

        for currency, position in portfolio._current_portfolio_state[
                "positions"].items():
            amount = 0.0
            initial_value = 0.0
            for bucket in position:
                amount += bucket["amount"]
                initial_value += bucket["initial_value"]

            current_value = present.get_value(Fund(amount, currency)).amount
            funds[currency].update({
                "amount": amount,
                "initial_value": initial_value,
                "current_value": current_value
            })

        state["prices"] = prices = {}
        for pair in portfolio.pairs:
            orderbook = present.get_pricebook(*parse_pair(pair))
            prices[pair] = {
                "b": orderbook.buy_levels[0][0],
                "s": orderbook.sell_levels[-1][0]
            }

        self.interface_state = state
Exemple #8
0
    def update_portfolio(self, portfolio: PortfolioController):
        single_buy_value = 10  # how much will be sent for every buy
        required_gain_threshold = 1.001  # how much the bought currency must gain in value to be sold
        stair_height = (required_gain_threshold - 1.0) * 2

        present = portfolio.present
        for currency in portfolio.non_target_currencies:
            # run scalping algorithm on every currency that can be bought by target currency

            current_level = present.get_unit_cost(currency)
            last_level = self._last_stair_levels[currency]

            # detect whether price stepped down/up the stair
            diff = current_level / last_level
            if diff < 1 - stair_height:
                # we stepped down the whole stair
                # we expect the price to go high after some time - lets buy
                if not portfolio.can_sell(single_buy_value,
                                          portfolio.target_currency):
                    continue  # we don't have enough to buy

                self._last_stair_levels[
                    currency] = current_level  # update last level
                portfolio.request_transfer(
                    Fund(single_buy_value, portfolio.target_currency),
                    currency)

            elif not portfolio.get_funds(currency):
                # no pending funds - move upstairs freely
                self._last_stair_levels[currency] = max(
                    last_level, current_level)

        # scalp all the profitable funds we collected
        for fund in portfolio.get_funds_with(
                gain_greater_than=required_gain_threshold):
            if fund.currency == portfolio.target_currency:
                continue  # target fund can't be converted to target again :)

            portfolio.request_transfer(fund, portfolio.target_currency)
            self._last_stair_levels[fund.currency] = present.get_unit_cost(
                fund.currency)
Exemple #9
0
 def get_value(self, amount, currency):
     return self.get_history(0).get_value(Fund(amount, currency))
Exemple #10
0
 def get_unit_cost(self, currency: str) -> float:
     return self.get_cost(Fund(1.0, currency)).amount
Exemple #11
0
    def get_funds(self, currency):
        result = []
        for amount in self._currency_positions[currency].get_bucket_amounts():
            result.append(Fund(amount, currency))

        return result
Exemple #12
0
    def apply(self, portfolio_state, market: Market):
        if self._source_amount <= DUST_LEVEL:
            raise PortfolioUpdateException(
                f"Requested source amount {self._source_amount} is too low.")

        present = market.present

        target_amount = present.after_conversion(
            Fund(self._source_amount, self._source), self._target).amount
        if target_amount < self._min_target_amount:
            # the real target amount is not meeting the expectations
            raise PortfolioUpdateException(
                f"Requested {self._min_target_amount} but got only {target_amount} of {self._target}"
            )

        target_value = present.get_value(Fund(target_amount,
                                              self._target)).amount
        source_initial_value = present.get_value(
            Fund(self._source_amount, self._source)).amount

        if source_initial_value < target_value:
            raise PortfolioUpdateException(
                f"Value can't increase during portfolio update. Source value: {source_initial_value} < Target value: {target_value}"
            )

        positions = portfolio_state["positions"]

        # subtract amount from source positions
        pending_amount = self._source_amount
        closed_initial_value = 0.0

        # get required amount from source buckets
        for source_bucket in positions[self._source]:
            amount = source_bucket["amount"]
            diff = min(amount, pending_amount)
            if diff < DUST_LEVEL:
                continue

            # calculate proportional value according to the amount subtracted
            partial_initial_value = max(
                0.0, diff / amount) * source_bucket["initial_value"]
            source_bucket["amount"] -= diff
            source_bucket["initial_value"] -= partial_initial_value
            closed_initial_value += partial_initial_value
            pending_amount -= diff
            if pending_amount <= 0:
                break

        if pending_amount > DUST_LEVEL:
            raise PortfolioUpdateException(
                f"Missing {pending_amount} {self._source}.")

        if self._target not in positions:
            # ensure the target position exists
            positions[self._target] = []

        # add amount to target bucket
        target_buckets = positions[self._target]
        target_buckets.append({
            "amount": target_amount,
            "initial_value": source_initial_value
        })

        self._postprocess_buckets(positions, market)

        return {
            "source_currency": self._source,
            "source_amount": self._source_amount,
            "min_target_amount": self._min_target_amount,
            "target_currency": self._target,
            "target_amount": target_amount,
            "source_initial_value": source_initial_value,
            "closed_initial_value": closed_initial_value
        }
Exemple #13
0
    def get_value(self, fund):
        if fund.currency == self.target_currency:
            return fund  # value of target currency can't change because value is relative to target currency

        predicted_value = self._actual_unit_values[fund.currency] * fund.amount
        return Fund(predicted_value, self.target_currency)