def get_order_executor(self,
                           identifier=None,
                           throw_exception: bool = True) -> OrderExecutor:

        if identifier is None:

            if len(self._order_executors.keys()) == 0:
                raise OperationalException(
                    "Algorithm has no order executors registered")

            return self._order_executors[list(self._order_executors.keys())[0]]

        if identifier not in self._order_executors:

            if identifier in RESERVED_IDENTIFIERS:
                order_executor = DefaultOrderExecutorFactory\
                    .of_market(identifier)
                order_executor.initialize(self)
                self._order_executors[order_executor.identifier] = \
                    order_executor
                return order_executor

            if throw_exception:
                raise OperationalException(
                    f"No corresponding order executor found for "
                    f"identifier {identifier}")

            return None

        return self._order_executors[identifier]
    def validate_sell_order(self, order, portfolio):

        position = portfolio.positions\
            .filter_by(portfolio=portfolio)\
            .filter_by(symbol=order.target_symbol)\
            .first()

        if position is None:
            raise OperationalException(
                "Can't add sell order to non existing position")

        if OrderType.LIMIT.equals(order.order_type):
            if position.amount < order.amount_target_symbol:
                raise OperationalException(
                    "Order amount is larger then amount of open position")
        else:
            if position.amount < order.amount_target_symbol:
                raise OperationalException(
                    "Order amount is larger then amount of open position")

        if not order.trading_symbol == portfolio.trading_symbol:
            raise OperationalException(
                f"Can't add sell order with target "
                f"symbol {order.target_symbol} to "
                f"portfolio with trading currency {portfolio.trading_symbol}")
    def validate_market_order(order, portfolio):

        if OrderSide.BUY.equals(order.order_side):

            if order.amount_trading_symbol is None:
                raise OperationalException(
                    f"Market order needs an amount specified in the trading "
                    f"symbol {order.trading_symbol}")

            if order.amount_trading_symbol > portfolio.unallocated:
                raise OperationalException(
                    f"Market order amount {order.amount_trading_symbol} "
                    f"{portfolio.trading_symbol.upper()} is larger then "
                    f"unallocated {portfolio.unallocated} "
                    f"{portfolio.trading_symbol.upper()}")
        else:
            position = portfolio.positions\
                .filter_by(symbol=order.target_symbol)\
                .first()

            if position is None:
                raise OperationalException(
                    "Can't add market sell order to non existing position")

            if order.amount_target_symbol > position.amount:
                raise OperationalException(
                    "Sell order amount larger then position size")
    def _initialize_config(self, config=None):

        if not self._configured:

            if config is not None:
                self._config = config

            if self._config is None:
                raise OperationalException("No config object set")

            if inspect.isclass(self._config) \
                    and issubclass(self._config, Config):
                self._config = self._config()
            elif type(self._config) is dict:
                self._config = Config.from_dict(self._config)
            else:
                raise OperationalException("Config object not supported")

            if self._resource_directory is not None:
                self._config[RESOURCES_DIRECTORY] = self._resource_directory

            if RESOURCES_DIRECTORY not in self._config \
                    or self._config[RESOURCES_DIRECTORY] is None:
                raise OperationalException("Resource directory not specified")

            self._configured = True
            setup_logging(self.config.get(LOG_LEVEL, "INFO"))
    def get_data_provider(self,
                          identifier=None,
                          throw_exception: bool = True) -> DataProvider:

        if identifier is None:

            if len(self._data_providers.keys()) == 0:
                raise OperationalException(
                    "Algorithm has no data providers registered")

            return self._data_providers[list(self._data_providers.keys())[0]]

        if identifier not in self._data_providers:

            if identifier in RESERVED_IDENTIFIERS:
                data_provider = DefaultDataProviderFactory\
                    .of_identifier(identifier)

                if data_provider is not None:
                    self._data_providers[identifier.upper()] = data_provider
                    return data_provider

            if throw_exception:
                raise OperationalException(
                    f"No corresponding data provider found for "
                    f"identifier {identifier}")

            return None

        return self._data_providers[identifier]
    def get_portfolio_manager(self,
                              identifier: str = None,
                              throw_exception: bool = True):

        if identifier is None:

            if len(self._portfolio_managers.keys()) == 0:
                raise OperationalException(
                    "Algorithm has no portfolio managers registered")

            return self._portfolio_managers[list(
                self._portfolio_managers.keys())[0]]

        if identifier not in self._portfolio_managers:

            if identifier in RESERVED_IDENTIFIERS:
                portfolio_manager = DefaultPortfolioManagerFactory\
                    .of_market(identifier)

                portfolio_manager.initialize(self)
                self._portfolio_managers[portfolio_manager.identifier] \
                    = portfolio_manager

                return portfolio_manager

            if throw_exception:
                raise OperationalException(
                    f"No corresponding portfolio manager found for "
                    f"identifier {identifier}")

            return None

        return self._portfolio_managers[identifier]
    def __getitem__(self, item) -> Any:

        if isinstance(item, str):

            if not hasattr(self, item):
                raise OperationalException(
                    "ContextConfig object doesn't have the specific "
                    "attribute {}".format(item))

            return self.__getattribute__(item)
        else:
            raise OperationalException(
                "ContextConfig attributes can only be referenced by string")
    def set_executed(self,
                     snapshot=True,
                     executed_at=None,
                     amount=None,
                     price=None):

        # Can't execute limit orders that have 0 size
        if OrderType.LIMIT.equals(self.order_type) \
                and self.amount_target_symbol == 0:
            return

        if not OrderStatus.PENDING.equals(self.status) and \
                self.status is not None:
            raise OperationalException("Order is not in pending state")

        if self.amount_target_symbol != 0 and self.position is None:
            raise OperationalException("Order is not linked to a position")

        if OrderType.MARKET.equals(self.order_type):

            if amount is None or price is None:
                raise OperationalException(
                    "Amount and price needs to be provided for execution "
                    "of a market order")
            self.initial_price = price

            if OrderSide.SELL.equals(self.order_side):
                self.set_amount_trading_symbol(amount)

        if executed_at is not None:
            self.executed_at = executed_at
        else:
            self.executed_at = datetime.utcnow()

        if OrderSide.BUY.equals(self.order_side):
            self.status = OrderStatus.SUCCESS.value
            self.position.amount += self.amount_target_symbol
            self.position.portfolio.total_cost += \
                self.amount_target_symbol * self.initial_price
        else:
            self.status = OrderStatus.SUCCESS.value
            self.close_buy_orders()
            self.position.portfolio.total_revenue += \
                self.amount_target_symbol * self.initial_price

        db.session.commit()

        if snapshot:
            self.snapshot(self.executed_at)
    def copy(self, amount=None):

        if amount is None:
            amount = self.amount

        if amount > self.amount_target_symbol:
            raise OperationalException("Amount is larger then original order")

        order = SQLLiteOrder(
            amount_trading_symbol=self.get_amount_trading_symbol(),
            price=self.initial_price,
            order_side=self.order_side,
            order_type=self.order_type,
            target_symbol=self.target_symbol,
            trading_symbol=self.trading_symbol,
            amount_target_symbol=self.amount_target_symbol -
            (self.amount_target_symbol - amount))

        order.amount_trading_symbol = order.initial_price \
                                      * order.amount_target_symbol
        order.order_reference = self.order_reference
        order.status = self.status
        order.executed_at = self.executed_at
        order.updated_at = self.updated_at
        order.created_at = self.created_at
        order.closing_price = self.closing_price

        self.amount_trading_symbol -= order.amount_trading_symbol
        self.amount_target_symbol -= order.amount_target_symbol

        return order
    def split(self, amount):

        if not OrderSide.BUY.equals(self.order_side):
            raise OperationalException("Sell order can't be split")

        if not OrderStatus.SUCCESS.equals(self.status):
            raise OperationalException("Order can't be split")

        if amount <= 0 or amount >= self.amount_target_symbol:
            raise OperationalException("Split amount has a wrong value")

        algorithm_order = self.copy(amount=amount)
        self.position.orders.append(algorithm_order)
        db.session.commit()

        return self, algorithm_order
    def save(self, db, commit=True):

        if self.position is None:
            raise OperationalException(
                "Can't save order that is not linked to an position")

        super(Order, self).save(db, commit)
    def create_order(
        self,
        context,
        order_type,
        symbol,
        price=None,
        amount_trading_symbol=None,
        amount_target_symbol=None,
        order_side=OrderSide.BUY.value,
    ):
        if OrderType.MARKET.equals(order_type):

            if OrderSide.SELL.equals(order_side):
                order = SQLLiteOrder(target_symbol=symbol,
                                     trading_symbol=self.trading_symbol,
                                     amount_target_symbol=amount_target_symbol,
                                     order_type=OrderType.MARKET.value,
                                     order_side=OrderSide.SELL.value,
                                     price=price)
            else:
                raise OperationalException("Buy market order is not supported")
        else:
            order = SQLLiteOrder(target_symbol=symbol,
                                 trading_symbol=self.trading_symbol,
                                 amount_target_symbol=amount_target_symbol,
                                 amount_trading_symbol=amount_trading_symbol,
                                 order_type=order_type,
                                 order_side=order_side,
                                 price=price)

        return order
    def create_order(self,
                     symbol,
                     price=None,
                     amount_trading_symbol=None,
                     amount_target_symbol=None,
                     order_type=OrderType.LIMIT.value,
                     order_side=OrderSide.BUY.value,
                     context=None,
                     validate_pair=True):

        if context is None:
            from investing_algorithm_framework import current_app
            context = current_app.algorithm

        if validate_pair:
            market_service = context.get_market_service(self.market)
            if not market_service.pair_exists(symbol, self.trading_symbol):
                raise OperationalException(
                    f"Pair {symbol} {self.trading_symbol} does not exist "
                    f"on market {self.market}")

        return self.get_portfolio().create_order(
            context=context,
            order_type=order_type,
            symbol=symbol,
            price=price,
            amount_trading_symbol=amount_trading_symbol,
            amount_target_symbol=amount_target_symbol,
            order_side=order_side,
        )
    def set_pending(self):

        if not OrderStatus.TO_BE_SENT.equals(self.status):
            raise OperationalException("Order status is not TO_BE_SENT")

        self.status = OrderStatus.PENDING.value
        db.session.commit()
    def validate_buy_order(order, portfolio):

        if not order.trading_symbol == portfolio.trading_symbol:
            raise OperationalException(
                f"Can't add buy order with trading "
                f"symbol {order.trading_symbol} to "
                f"portfolio with trading currency {portfolio.trading_symbol}")
    def __check_initialization(self):
        """
        Utils function to check the necessary attributes of the
        AlgorithmContext instance
        """

        if self.algorithm_id is None:
            raise OperationalException("AlgorithmContext Id is not set")
    def set(self, key: str, value: Any) -> None:

        if hasattr(self, key):
            raise OperationalException(
                "ContextConfig object already have the specific "
                "attribute {} specified".format(key))

        setattr(self, key, value)
    def withdraw(self, amount, creation_datetime=datetime.utcnow()):

        if amount > self.unallocated:
            raise OperationalException(
                "Withdrawal is larger then unallocated size")

        self.unallocated -= amount
        db.session.commit()
        self.snapshot(creation_datetime=creation_datetime, withdrawel=amount)
Beispiel #19
0
    def get_order(self, order_id, target_symbol: str, trading_symbol: str):
        self.initialize_exchange(credentials=True)

        try:
            symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
            return self.exchange.fetchOrder(order_id, symbol)
        except Exception as e:
            logger.exception(e)
            raise OperationalException("Could not retrieve order")
    def add_order_executor(self, order_executor):
        from investing_algorithm_framework.core.order_executors \
            import OrderExecutor

        if inspect.isclass(order_executor):
            order_executor = order_executor()

        if order_executor in RESERVED_IDENTIFIERS:
            raise OperationalException(
                "Identifier of order executor is reserved")

        assert isinstance(order_executor, OrderExecutor), (
            'Provided object must be an instance of the OrderExecutor class')

        if order_executor.identifier in self._order_executors:
            raise OperationalException("Order executor id already exists")

        self._order_executors[order_executor.identifier] = order_executor
    def add_data_provider(self, data_provider):
        from investing_algorithm_framework.core.data_providers \
            import DataProvider

        if inspect.isclass(data_provider):
            data_provider = data_provider()

        if data_provider.identifier in RESERVED_IDENTIFIERS:
            raise OperationalException(
                "Identifier of data provider is reserved")

        assert isinstance(data_provider, DataProvider), (
            'Provided object must be an instance of the DataProvider class')

        if data_provider.identifier in self._data_providers:
            raise OperationalException("DataProvider id already exists")

        self._data_providers[data_provider.identifier] = data_provider
    def get_portfolio(self, throw_exception=True) -> Portfolio:
        portfolio = SQLLitePortfolio.query\
            .filter_by(identifier=self.identifier)\
            .first()

        if portfolio is None and throw_exception:
            raise OperationalException("No portfolio model implemented")

        return portfolio
    def validate_limit_order(order, portfolio):

        total_price = order.amount_target_symbol * order.initial_price

        if float(portfolio.unallocated) < total_price:
            raise OperationalException(
                f"Order total: {total_price} {portfolio.trading_symbol}, is "
                f"larger then unallocated size: {portfolio.unallocated} "
                f"{portfolio.trading_symbol} of the portfolio")
    def start_algorithm(self):

        if not scheduler.running:
            raise OperationalException(
                "Could not start algorithm because the scheduler "
                "is not running"
            )

        # Start the algorithm
        self._algorithm.start()
    def get_api_key(self, throw_exception=True):
        api_key = getattr(self, BINANCE_API_KEY, None)

        if api_key is None and throw_exception:
            raise OperationalException(
                "Binance api key is not set. Either override 'get_api_key' "
                "method or set the 'binance_api_key' attribute in the "
                "algorithm config.")

        return api_key
Beispiel #26
0
    def get_order_book(self, target_symbol: str, trading_symbol: str):
        self.initialize_exchange()

        try:
            symbol = f"{target_symbol.upper()}/{trading_symbol.upper()}"
            return self.exchange.fetchOrderBook(symbol)
        except Exception as e:
            logger.exception(e)
            raise OperationalException(
                "Could not retrieve order book"
            )
    def from_string(value: str):

        if isinstance(value, str):

            if value.lower() in ('sqlite', 'sqlite3'):
                return DatabaseType.SQLITE3

            elif value.lower() in ('postgresql', 'postgres'):
                return DatabaseType.POSTGRESQL
            else:
                raise OperationalException(
                    'Could not convert value {} to a data base type'.format(
                        value
                    )
                )

        else:
            raise OperationalException(
                "Could not convert non string value to a data base type"
            )
    def get_secret_key(self, throw_exception=True):
        secret_key = getattr(self, BINANCE_SECRET_KEY, None)

        if secret_key is None and throw_exception:
            raise OperationalException(
                f"Binance secret key is not set on class "
                f"{self.__class__.__name__}. "
                f"Either override 'get_secret_key' method or set "
                f"the 'secret_key' attribute in the algorithm config.")

        return secret_key
    def __init__(self,
                 order_side,
                 order_type,
                 target_symbol,
                 trading_symbol,
                 price=None,
                 amount_target_symbol=None,
                 amount_trading_symbol=None,
                 **kwargs):
        self.order_side = OrderSide.from_value(order_side).value
        self.order_type = OrderType.from_value(order_type).value
        self.target_symbol = target_symbol
        self.trading_symbol = trading_symbol
        self.status = None
        self.initial_price = price

        if OrderType.MARKET.equals(self.order_type):

            if OrderSide.SELL.equals(self.order_side):
                if amount_target_symbol is None:
                    raise OperationalException(
                        "Amount target symbol needs to be set "
                        "for a market sell order")

                self.set_amount_target_symbol(amount_target_symbol)
            else:
                raise OperationalException(
                    "Market buy order is not yet supported")
        else:
            if amount_target_symbol is None \
                    and amount_trading_symbol is None:
                raise OperationalException(
                    "Amount target symbol or amount trading "
                    "symbol needs to be set for limit order")

            if amount_target_symbol is not None:
                self.set_amount_target_symbol(amount_target_symbol)
            elif amount_trading_symbol is not None:
                self.set_amount_trading_symbol(amount_trading_symbol)

        super(Order, self).__init__(**kwargs)
    def _initialize_database(self):

        if self._configured and not self._database_configured:
            setup_database(self.config)

            if self.config[DATABASE_CONFIG][DATABASE_DIRECTORY_PATH] is None:
                raise OperationalException(
                    f"{DATABASE_DIRECTORY_PATH} is not set in config"
                )

            if self.config[DATABASE_CONFIG][DATABASE_NAME] is None:
                raise OperationalException(
                    f"{DATABASE_NAME} is not set in config"
                )

            if self.config[SQLALCHEMY_DATABASE_URI] is None:
                raise OperationalException(
                    f"{SQLALCHEMY_DATABASE_URI} is not set in config"
                )

            self._database_configured = True