From 52aefa8c34f20f30e4f9e9c3ec677f0f3eab0e04 Mon Sep 17 00:00:00 2001 From: misagh Date: Wed, 14 Nov 2018 10:32:13 +0100 Subject: [PATCH] drafting first order refactoring phase --- freqtrade/execution/default_execution.py | 67 ++++++++++++++++++ freqtrade/execution/interface.py | 89 ++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 freqtrade/execution/default_execution.py create mode 100644 freqtrade/execution/interface.py diff --git a/freqtrade/execution/default_execution.py b/freqtrade/execution/default_execution.py new file mode 100644 index 000000000..5c21e03f7 --- /dev/null +++ b/freqtrade/execution/default_execution.py @@ -0,0 +1,67 @@ +# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement + +import logging +from freqtrade.execution.interface import IExecution +from typing import Dict + +logger = logging.getLogger(__name__) + + +class DefaultExecution(IExecution): + + def _get_target_bid(self, pair: str, ticker: Dict[str, float]) -> float: + """ + Calculates bid target between current ask price and last price + :param ticker: Ticker to use for getting Ask and Last Price + :return: float: Price + """ + if ticker['ask'] < ticker['last']: + ticker_rate = ticker['ask'] + else: + balance = self.config['bid_strategy']['ask_last_balance'] + ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask']) + + used_rate = ticker_rate + config_bid_strategy = self.config.get('bid_strategy', {}) + if 'use_order_book' in config_bid_strategy and\ + config_bid_strategy.get('use_order_book', False): + logger.info('Getting price from order book') + order_book_top = config_bid_strategy.get('order_book_top', 1) + order_book = self.exchange.get_order_book(pair, order_book_top) + logger.debug('order_book %s', order_book) + # top 1 = index 0 + order_book_rate = order_book['bids'][order_book_top - 1][0] + # if ticker has lower rate, then use ticker ( usefull if down trending ) + logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate) + if ticker_rate < order_book_rate: + logger.info('...using ticker rate instead %0.8f', ticker_rate) + used_rate = ticker_rate + else: + used_rate = order_book_rate + else: + logger.info('Using Last Ask / Last Price') + used_rate = ticker_rate + + return used_rate + + def execute_buy(self, pair: str, stake_amount: float, price: float) -> str: + pair_s = pair.replace('_', '/') + + if price: + buy_limit = price + else: + # Calculate amount + buy_limit = self._get_target_bid(pair, self.exchange.get_ticker(pair)) + + min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit) + if min_stake_amount is not None and min_stake_amount > stake_amount: + logger.warning( + f'Can\'t open a new trade for {pair_s}: stake amount' + f' is too small ({stake_amount} < {min_stake_amount})' + ) + return False + + amount = stake_amount / buy_limit + + order_id = self.exchange.buy(pair, buy_limit, amount)['id'] + return order_id or None diff --git a/freqtrade/execution/interface.py b/freqtrade/execution/interface.py new file mode 100644 index 000000000..37aef9eca --- /dev/null +++ b/freqtrade/execution/interface.py @@ -0,0 +1,89 @@ +""" +IExecute interface +This module defines the execution strategy +""" + +from abc import ABC, abstractmethod +from freqtrade.exchange import Exchange + + +class MarketOrder(): + """ + Class encapsulating an order to be placed at the current market price. + """ + + def __init__(self, exchange=None): + self._exchange = exchange + + def get_limit_price(self, _is_buy): + return None + + def get_stop_price(self, _is_buy): + return None + + +class LimitOrder(): + """ + Execution style representing an order to be executed at a price equal to or + better than a specified limit price. + """ + + def __init__(self, limit_price, asset=None, exchange=None): + """ + Store the given price. + """ + + self.limit_price = limit_price + self._exchange = exchange + self.asset = asset + + +class StopOrder(): + """ + Execution style representing an order to be placed once the market price + reaches a specified stop price. + """ + + def __init__(self, stop_price, asset=None, exchange=None): + """ + Store the given price. + """ + + self.stop_price = stop_price + self._exchange = exchange + self.asset = asset + + +class StopLimitOrder(): + """ + Execution style representing a limit order to be placed with a specified + limit price once the market reaches a specified stop price. + """ + + def __init__(self, limit_price, stop_price, asset=None, exchange=None): + """ + Store the given prices + """ + + self.limit_price = limit_price + self.stop_price = stop_price + self._exchange = exchange + self.asset = asset + + +class IExecution(ABC): + + def __init__(self, config: dict, exchange: Exchange) -> None: + self.config = config + self.exchange = exchange + + @abstractmethod + def execute_buy(self, pair: str, stake_amount: float = None, price: float = None) -> str: + """ + Populate indicators that will be used in the Buy and Sell strategy + :param pair: pair to buy + :param stake_amount: Amount to buy (Optional) + :param price: Price at which the asset should be bought (Optional) + :return: Order id if execution was successful, otherwise None + """ + raise NotImplementedError