Add sell-reason to sell-tree

This commit is contained in:
Matthias 2018-07-11 19:57:01 +02:00
parent 6bb7167b56
commit f991109b0a
4 changed files with 31 additions and 20 deletions

View File

@ -20,6 +20,7 @@ from freqtrade.fiat_convert import CryptoToFiatConverter
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.state import State
from freqtrade.strategy.interface import SellType
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
logger = logging.getLogger(__name__)
@ -505,8 +506,9 @@ class FreqtradeBot(object):
(buy, sell) = self.strategy.get_signal(self.exchange,
trade.pair, self.strategy.ticker_interval)
if self.strategy.should_sell(trade, current_rate, datetime.utcnow(), buy, sell):
self.execute_sell(trade, current_rate)
should_sell = self.strategy.should_sell(trade, current_rate, datetime.utcnow(), buy, sell)
if should_sell[0]:
self.execute_sell(trade, current_rate, should_sell[1])
return True
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
return False
@ -607,17 +609,19 @@ class FreqtradeBot(object):
# TODO: figure out how to handle partially complete sell orders
return False
def execute_sell(self, trade: Trade, limit: float) -> None:
def execute_sell(self, trade: Trade, limit: float, sellreason: SellType) -> None:
"""
Executes a limit sell for the given trade and limit
:param trade: Trade instance
:param limit: limit rate for the sell order
:param sellrason: Reaseon the sell was triggered
:return: None
"""
# Execute sell and update trade record
order_id = self.exchange.sell(str(trade.pair), limit, trade.amount)['id']
trade.open_order_id = order_id
trade.close_rate_requested = limit
trade.sell_reason = sellreason.value
profit_trade = trade.calc_profit(rate=limit)
current_rate = self.exchange.get_ticker(trade.pair)['bid']

View File

@ -88,6 +88,7 @@ def check_migrate(engine) -> None:
stop_loss = get_column_def(cols, 'stop_loss', '0.0')
initial_stop_loss = get_column_def(cols, 'initial_stop_loss', '0.0')
max_rate = get_column_def(cols, 'max_rate', '0.0')
sell_reason = get_column_def(cols, 'sell_reason', 'null')
# Schema migration necessary
engine.execute(f"alter table trades rename to {table_back_name}")
@ -99,7 +100,7 @@ def check_migrate(engine) -> None:
(id, exchange, pair, is_open, fee_open, fee_close, open_rate,
open_rate_requested, close_rate, close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
stop_loss, initial_stop_loss, max_rate
stop_loss, initial_stop_loss, max_rate, sell_reason
)
select id, lower(exchange),
case
@ -114,7 +115,7 @@ def check_migrate(engine) -> None:
{close_rate_requested} close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
{stop_loss} stop_loss, {initial_stop_loss} initial_stop_loss,
{max_rate} max_rate
{max_rate} max_rate, {sell_reason} sell_reason
from {table_back_name}
""")
@ -170,6 +171,7 @@ class Trade(_DECL_BASE):
initial_stop_loss = Column(Float, nullable=True, default=0.0)
# absolute value of the highest reached price
max_rate = Column(Float, nullable=True, default=0.0)
sell_reason = Column(String, nullable=True)
def __repr__(self):
open_since = arrow.get(self.open_date).humanize() if self.is_open else 'closed'

View File

@ -13,6 +13,7 @@ import sqlalchemy as sql
from numpy import mean, nan_to_num
from pandas import DataFrame
from freqtrade.analyze import SellType
from freqtrade.misc import shorten_date
from freqtrade.persistence import Trade
from freqtrade.state import State
@ -344,7 +345,7 @@ class RPC(object):
# Get current rate and execute sell
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
self._freqtrade.execute_sell(trade, current_rate)
self._freqtrade.execute_sell(trade, current_rate, SellType.FORCE_SELL)
# ---- EOF def _exec_forcesell ----
if self._freqtrade.state != State.RUNNING:

View File

@ -6,7 +6,7 @@ import logging
from abc import ABC, abstractmethod
from datetime import datetime
from enum import Enum
from typing import Dict, List, Tuple
from typing import Dict, List, Tuple, Optional
import arrow
from pandas import DataFrame
@ -35,6 +35,7 @@ class SellType(Enum):
STOP_LOSS = "stop_loss"
TRAILING_STOP_LOSS = "trailing_stop_loss"
SELL_SIGNAL = "sell_signal"
FORCE_SELL = "force_sell"
class IStrategy(ABC):
@ -147,40 +148,42 @@ class IStrategy(ABC):
)
return buy, sell
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, sell: bool) -> bool:
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool,
sell: bool) -> Tuple[bool, Optional[SellType]]:
"""
This function evaluate if on the condition required to trigger a sell has been reached
if the threshold is reached and updates the trade record.
:return: True if trade should be sold, False otherwise
"""
current_profit = trade.calc_profit_percent(rate)
if self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date,
current_profit=current_profit):
return True
stoplossflag = self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date,
current_profit=current_profit)
if stoplossflag[0]:
return (True, stoplossflag[1])
experimental = self.config.get('experimental', {})
if buy and experimental.get('ignore_roi_if_buy_signal', False):
logger.debug('Buy signal still active - not selling.')
return False
return (False, None)
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
logger.debug('Required profit reached. Selling..')
return True
return (True, SellType.ROI)
if experimental.get('sell_profit_only', False):
logger.debug('Checking if trade is profitable..')
if trade.calc_profit(rate=rate) <= 0:
return False
return (False, None)
if sell and not buy and experimental.get('use_sell_signal', False):
logger.debug('Sell signal received. Selling..')
return True
return (True, SellType.SELL_SIGNAL)
return False
return (False, None)
def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime,
current_profit: float) -> bool:
current_profit: float) -> Tuple[bool, Optional[SellType]]:
"""
Based on current profit of the trade and configured (trailing) stoploss,
decides to sell or not
@ -192,8 +195,9 @@ class IStrategy(ABC):
# evaluate if the stoploss was hit
if self.stoploss is not None and trade.stop_loss >= current_rate:
selltype = SellType.STOP_LOSS
if trailing_stop:
selltype = SellType.TRAILING_STOP_LOSS
logger.debug(
f"HIT STOP: current price at {current_rate:.6f}, "
f"stop loss is {trade.stop_loss:.6f}, "
@ -202,7 +206,7 @@ class IStrategy(ABC):
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
logger.debug('Stop loss hit.')
return True
return (True, selltype)
# update the stop loss afterwards, after all by definition it's supposed to be hanging
if trailing_stop:
@ -219,7 +223,7 @@ class IStrategy(ABC):
trade.adjust_stop_loss(current_rate, stop_loss_value)
return False
return (False, None)
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
"""