Merge pull request #5299 from kevinjulian/feat/kevinjulian/add-buy-signal-name
Add buy signal name
This commit is contained in:
commit
138b126d03
@ -114,6 +114,36 @@ class AwesomeStrategy(IStrategy):
|
|||||||
|
|
||||||
See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks.
|
See [Dataframe access](#dataframe-access) for more information about dataframe use in strategy callbacks.
|
||||||
|
|
||||||
|
## Buy Tag
|
||||||
|
|
||||||
|
When your strategy has multiple buy signals, you can name the signal that triggered.
|
||||||
|
Then you can access you buy signal on `custom_sell`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
dataframe.loc[
|
||||||
|
(
|
||||||
|
(dataframe['rsi'] < 35) &
|
||||||
|
(dataframe['volume'] > 0)
|
||||||
|
),
|
||||||
|
['buy', 'buy_tag']] = (1, 'buy_signal_rsi')
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def custom_sell(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||||
|
current_profit: float, **kwargs):
|
||||||
|
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||||
|
last_candle = dataframe.iloc[-1].squeeze()
|
||||||
|
if trade.buy_tag == 'buy_signal_rsi' and last_candle['rsi'] > 80:
|
||||||
|
return 'sell_signal_rsi'
|
||||||
|
return None
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
`buy_tag` is limited to 100 characters, remaining data will be truncated.
|
||||||
|
|
||||||
|
|
||||||
## Custom stoploss
|
## Custom stoploss
|
||||||
|
|
||||||
The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss.
|
The stoploss price can only ever move upwards - if the stoploss value returned from `custom_stoploss` would result in a lower stoploss price than was previously set, it will be ignored. The traditional `stoploss` value serves as an absolute lower level and will be instated as the initial stoploss.
|
||||||
|
@ -83,6 +83,7 @@ Possible parameters are:
|
|||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
* `order_type`
|
* `order_type`
|
||||||
* `current_rate`
|
* `current_rate`
|
||||||
|
* `buy_tag`
|
||||||
|
|
||||||
### Webhookbuycancel
|
### Webhookbuycancel
|
||||||
|
|
||||||
@ -100,6 +101,7 @@ Possible parameters are:
|
|||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
* `order_type`
|
* `order_type`
|
||||||
* `current_rate`
|
* `current_rate`
|
||||||
|
* `buy_tag`
|
||||||
|
|
||||||
### Webhookbuyfill
|
### Webhookbuyfill
|
||||||
|
|
||||||
@ -115,6 +117,7 @@ Possible parameters are:
|
|||||||
* `stake_amount`
|
* `stake_amount`
|
||||||
* `stake_currency`
|
* `stake_currency`
|
||||||
* `fiat_currency`
|
* `fiat_currency`
|
||||||
|
* `buy_tag`
|
||||||
|
|
||||||
### Webhooksell
|
### Webhooksell
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ BT_DATA_COLUMNS = ['pair', 'stake_amount', 'amount', 'open_date', 'close_date',
|
|||||||
'fee_open', 'fee_close', 'trade_duration',
|
'fee_open', 'fee_close', 'trade_duration',
|
||||||
'profit_ratio', 'profit_abs', 'sell_reason',
|
'profit_ratio', 'profit_abs', 'sell_reason',
|
||||||
'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs',
|
'initial_stop_loss_abs', 'initial_stop_loss_ratio', 'stop_loss_abs',
|
||||||
'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', ]
|
'stop_loss_ratio', 'min_rate', 'max_rate', 'is_open', 'buy_tag']
|
||||||
|
|
||||||
|
|
||||||
def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str:
|
def get_latest_optimize_filename(directory: Union[Path, str], variant: str) -> str:
|
||||||
|
@ -3,5 +3,5 @@ from freqtrade.enums.backteststate import BacktestState
|
|||||||
from freqtrade.enums.rpcmessagetype import RPCMessageType
|
from freqtrade.enums.rpcmessagetype import RPCMessageType
|
||||||
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
|
from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
|
||||||
from freqtrade.enums.selltype import SellType
|
from freqtrade.enums.selltype import SellType
|
||||||
from freqtrade.enums.signaltype import SignalType
|
from freqtrade.enums.signaltype import SignalTagType, SignalType
|
||||||
from freqtrade.enums.state import State
|
from freqtrade.enums.state import State
|
||||||
|
@ -7,3 +7,10 @@ class SignalType(Enum):
|
|||||||
"""
|
"""
|
||||||
BUY = "buy"
|
BUY = "buy"
|
||||||
SELL = "sell"
|
SELL = "sell"
|
||||||
|
|
||||||
|
|
||||||
|
class SignalTagType(Enum):
|
||||||
|
"""
|
||||||
|
Enum for signal columns
|
||||||
|
"""
|
||||||
|
BUY_TAG = "buy_tag"
|
||||||
|
@ -420,20 +420,24 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# running get_signal on historical data fetched
|
# running get_signal on historical data fetched
|
||||||
(buy, sell) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df)
|
(buy, sell, buy_tag) = self.strategy.get_signal(
|
||||||
|
pair,
|
||||||
|
self.strategy.timeframe,
|
||||||
|
analyzed_df
|
||||||
|
)
|
||||||
|
|
||||||
if buy and not sell:
|
if buy and not sell:
|
||||||
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
|
stake_amount = self.wallets.get_trade_stake_amount(pair, self.edge)
|
||||||
|
|
||||||
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {})
|
||||||
if ((bid_check_dom.get('enabled', False)) and
|
if ((bid_check_dom.get('enabled', False)) and
|
||||||
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
(bid_check_dom.get('bids_to_ask_delta', 0) > 0)):
|
||||||
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
if self._check_depth_of_market_buy(pair, bid_check_dom):
|
||||||
return self.execute_buy(pair, stake_amount)
|
return self.execute_buy(pair, stake_amount, buy_tag=buy_tag)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return self.execute_buy(pair, stake_amount)
|
return self.execute_buy(pair, stake_amount, buy_tag=buy_tag)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -462,7 +466,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None,
|
def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None,
|
||||||
forcebuy: bool = False) -> bool:
|
forcebuy: bool = False, buy_tag: Optional[str] = None) -> bool:
|
||||||
"""
|
"""
|
||||||
Executes a limit buy for the given pair
|
Executes a limit buy for the given pair
|
||||||
:param pair: pair for which we want to create a LIMIT_BUY
|
:param pair: pair for which we want to create a LIMIT_BUY
|
||||||
@ -565,6 +569,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
exchange=self.exchange.id,
|
exchange=self.exchange.id,
|
||||||
open_order_id=order_id,
|
open_order_id=order_id,
|
||||||
strategy=self.strategy.get_strategy_name(),
|
strategy=self.strategy.get_strategy_name(),
|
||||||
|
buy_tag=buy_tag,
|
||||||
timeframe=timeframe_to_minutes(self.config['timeframe'])
|
timeframe=timeframe_to_minutes(self.config['timeframe'])
|
||||||
)
|
)
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
@ -590,6 +595,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
msg = {
|
msg = {
|
||||||
'trade_id': trade.id,
|
'trade_id': trade.id,
|
||||||
'type': RPCMessageType.BUY,
|
'type': RPCMessageType.BUY,
|
||||||
|
'buy_tag': trade.buy_tag,
|
||||||
'exchange': self.exchange.name.capitalize(),
|
'exchange': self.exchange.name.capitalize(),
|
||||||
'pair': trade.pair,
|
'pair': trade.pair,
|
||||||
'limit': trade.open_rate,
|
'limit': trade.open_rate,
|
||||||
@ -614,6 +620,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
msg = {
|
msg = {
|
||||||
'trade_id': trade.id,
|
'trade_id': trade.id,
|
||||||
'type': RPCMessageType.BUY_CANCEL,
|
'type': RPCMessageType.BUY_CANCEL,
|
||||||
|
'buy_tag': trade.buy_tag,
|
||||||
'exchange': self.exchange.name.capitalize(),
|
'exchange': self.exchange.name.capitalize(),
|
||||||
'pair': trade.pair,
|
'pair': trade.pair,
|
||||||
'limit': trade.open_rate,
|
'limit': trade.open_rate,
|
||||||
@ -634,6 +641,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
msg = {
|
msg = {
|
||||||
'trade_id': trade.id,
|
'trade_id': trade.id,
|
||||||
'type': RPCMessageType.BUY_FILL,
|
'type': RPCMessageType.BUY_FILL,
|
||||||
|
'buy_tag': trade.buy_tag,
|
||||||
'exchange': self.exchange.name.capitalize(),
|
'exchange': self.exchange.name.capitalize(),
|
||||||
'pair': trade.pair,
|
'pair': trade.pair,
|
||||||
'open_rate': trade.open_rate,
|
'open_rate': trade.open_rate,
|
||||||
@ -692,7 +700,11 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair,
|
||||||
self.strategy.timeframe)
|
self.strategy.timeframe)
|
||||||
|
|
||||||
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df)
|
(buy, sell, _) = self.strategy.get_signal(
|
||||||
|
trade.pair,
|
||||||
|
self.strategy.timeframe,
|
||||||
|
analyzed_df
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug('checking sell')
|
logger.debug('checking sell')
|
||||||
sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
|
sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
|
||||||
|
@ -43,6 +43,7 @@ CLOSE_IDX = 3
|
|||||||
SELL_IDX = 4
|
SELL_IDX = 4
|
||||||
LOW_IDX = 5
|
LOW_IDX = 5
|
||||||
HIGH_IDX = 6
|
HIGH_IDX = 6
|
||||||
|
BUY_TAG_IDX = 7
|
||||||
|
|
||||||
|
|
||||||
class Backtesting:
|
class Backtesting:
|
||||||
@ -217,9 +218,13 @@ class Backtesting:
|
|||||||
for pair, pair_data in processed.items():
|
for pair, pair_data in processed.items():
|
||||||
self.check_abort()
|
self.check_abort()
|
||||||
self.progress.increment()
|
self.progress.increment()
|
||||||
|
has_buy_tag = 'buy_tag' in pair_data
|
||||||
|
headers = headers + ['buy_tag'] if has_buy_tag else headers
|
||||||
if not pair_data.empty:
|
if not pair_data.empty:
|
||||||
pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist
|
pair_data.loc[:, 'buy'] = 0 # cleanup if buy_signal is exist
|
||||||
pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist
|
pair_data.loc[:, 'sell'] = 0 # cleanup if sell_signal is exist
|
||||||
|
if has_buy_tag:
|
||||||
|
pair_data.loc[:, 'buy_tag'] = None # cleanup if buy_tag is exist
|
||||||
|
|
||||||
df_analyzed = self.strategy.advise_sell(
|
df_analyzed = self.strategy.advise_sell(
|
||||||
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
|
self.strategy.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
|
||||||
@ -230,6 +235,8 @@ class Backtesting:
|
|||||||
# from the previous candle
|
# from the previous candle
|
||||||
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
|
df_analyzed.loc[:, 'buy'] = df_analyzed.loc[:, 'buy'].shift(1)
|
||||||
df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1)
|
df_analyzed.loc[:, 'sell'] = df_analyzed.loc[:, 'sell'].shift(1)
|
||||||
|
if has_buy_tag:
|
||||||
|
df_analyzed.loc[:, 'buy_tag'] = df_analyzed.loc[:, 'buy_tag'].shift(1)
|
||||||
|
|
||||||
df_analyzed.drop(df_analyzed.head(1).index, inplace=True)
|
df_analyzed.drop(df_analyzed.head(1).index, inplace=True)
|
||||||
|
|
||||||
@ -264,7 +271,7 @@ class Backtesting:
|
|||||||
# Worst case: price reaches stop_positive_offset and dives down.
|
# Worst case: price reaches stop_positive_offset and dives down.
|
||||||
stop_rate = (sell_row[OPEN_IDX] *
|
stop_rate = (sell_row[OPEN_IDX] *
|
||||||
(1 + abs(self.strategy.trailing_stop_positive_offset) -
|
(1 + abs(self.strategy.trailing_stop_positive_offset) -
|
||||||
abs(self.strategy.trailing_stop_positive)))
|
abs(self.strategy.trailing_stop_positive)))
|
||||||
else:
|
else:
|
||||||
# Worst case: price ticks tiny bit above open and dives down.
|
# Worst case: price ticks tiny bit above open and dives down.
|
||||||
stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct))
|
stop_rate = sell_row[OPEN_IDX] * (1 - abs(trade.stop_loss_pct))
|
||||||
@ -360,6 +367,7 @@ class Backtesting:
|
|||||||
|
|
||||||
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
|
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
|
||||||
# Enter trade
|
# Enter trade
|
||||||
|
has_buy_tag = len(row) >= BUY_TAG_IDX + 1
|
||||||
trade = LocalTrade(
|
trade = LocalTrade(
|
||||||
pair=pair,
|
pair=pair,
|
||||||
open_rate=row[OPEN_IDX],
|
open_rate=row[OPEN_IDX],
|
||||||
@ -369,6 +377,7 @@ class Backtesting:
|
|||||||
fee_open=self.fee,
|
fee_open=self.fee,
|
||||||
fee_close=self.fee,
|
fee_close=self.fee,
|
||||||
is_open=True,
|
is_open=True,
|
||||||
|
buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None,
|
||||||
exchange='backtesting',
|
exchange='backtesting',
|
||||||
)
|
)
|
||||||
return trade
|
return trade
|
||||||
|
@ -47,6 +47,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
min_rate = get_column_def(cols, 'min_rate', 'null')
|
min_rate = get_column_def(cols, 'min_rate', 'null')
|
||||||
sell_reason = get_column_def(cols, 'sell_reason', 'null')
|
sell_reason = get_column_def(cols, 'sell_reason', 'null')
|
||||||
strategy = get_column_def(cols, 'strategy', 'null')
|
strategy = get_column_def(cols, 'strategy', 'null')
|
||||||
|
buy_tag = get_column_def(cols, 'buy_tag', 'null')
|
||||||
# If ticker-interval existed use that, else null.
|
# If ticker-interval existed use that, else null.
|
||||||
if has_column(cols, 'ticker_interval'):
|
if has_column(cols, 'ticker_interval'):
|
||||||
timeframe = get_column_def(cols, 'timeframe', 'ticker_interval')
|
timeframe = get_column_def(cols, 'timeframe', 'ticker_interval')
|
||||||
@ -80,7 +81,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
|
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
|
||||||
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
||||||
stoploss_order_id, stoploss_last_update,
|
stoploss_order_id, stoploss_last_update,
|
||||||
max_rate, min_rate, sell_reason, sell_order_status, strategy,
|
max_rate, min_rate, sell_reason, sell_order_status, strategy, buy_tag,
|
||||||
timeframe, open_trade_value, close_profit_abs
|
timeframe, open_trade_value, close_profit_abs
|
||||||
)
|
)
|
||||||
select id, lower(exchange),
|
select id, lower(exchange),
|
||||||
@ -103,7 +104,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
|
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
|
||||||
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
||||||
{sell_order_status} sell_order_status,
|
{sell_order_status} sell_order_status,
|
||||||
{strategy} strategy, {timeframe} timeframe,
|
{strategy} strategy, {buy_tag} buy_tag, {timeframe} timeframe,
|
||||||
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs
|
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs
|
||||||
from {table_back_name}
|
from {table_back_name}
|
||||||
"""))
|
"""))
|
||||||
@ -160,7 +161,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
|
|||||||
table_back_name = get_backup_name(tabs, 'trades_bak')
|
table_back_name = get_backup_name(tabs, 'trades_bak')
|
||||||
|
|
||||||
# Check for latest column
|
# Check for latest column
|
||||||
if not has_column(cols, 'open_trade_value'):
|
if not has_column(cols, 'buy_tag'):
|
||||||
logger.info(f'Running database migration for trades - backup: {table_back_name}')
|
logger.info(f'Running database migration for trades - backup: {table_back_name}')
|
||||||
migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)
|
migrate_trades_table(decl_base, inspector, engine, table_back_name, cols)
|
||||||
# Reread columns - the above recreated the table!
|
# Reread columns - the above recreated the table!
|
||||||
|
@ -257,6 +257,7 @@ class LocalTrade():
|
|||||||
sell_reason: str = ''
|
sell_reason: str = ''
|
||||||
sell_order_status: str = ''
|
sell_order_status: str = ''
|
||||||
strategy: str = ''
|
strategy: str = ''
|
||||||
|
buy_tag: Optional[str] = None
|
||||||
timeframe: Optional[int] = None
|
timeframe: Optional[int] = None
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
@ -288,6 +289,7 @@ class LocalTrade():
|
|||||||
'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None,
|
'amount_requested': round(self.amount_requested, 8) if self.amount_requested else None,
|
||||||
'stake_amount': round(self.stake_amount, 8),
|
'stake_amount': round(self.stake_amount, 8),
|
||||||
'strategy': self.strategy,
|
'strategy': self.strategy,
|
||||||
|
'buy_tag': self.buy_tag,
|
||||||
'timeframe': self.timeframe,
|
'timeframe': self.timeframe,
|
||||||
|
|
||||||
'fee_open': self.fee_open,
|
'fee_open': self.fee_open,
|
||||||
@ -636,7 +638,7 @@ class LocalTrade():
|
|||||||
|
|
||||||
# skip case if trailing-stop changed the stoploss already.
|
# skip case if trailing-stop changed the stoploss already.
|
||||||
if (trade.stop_loss == trade.initial_stop_loss
|
if (trade.stop_loss == trade.initial_stop_loss
|
||||||
and trade.initial_stop_loss_pct != desired_stoploss):
|
and trade.initial_stop_loss_pct != desired_stoploss):
|
||||||
# Stoploss value got changed
|
# Stoploss value got changed
|
||||||
|
|
||||||
logger.info(f"Stoploss for {trade} needs adjustment...")
|
logger.info(f"Stoploss for {trade} needs adjustment...")
|
||||||
@ -703,6 +705,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
sell_reason = Column(String(100), nullable=True)
|
sell_reason = Column(String(100), nullable=True)
|
||||||
sell_order_status = Column(String(100), nullable=True)
|
sell_order_status = Column(String(100), nullable=True)
|
||||||
strategy = Column(String(100), nullable=True)
|
strategy = Column(String(100), nullable=True)
|
||||||
|
buy_tag = Column(String(100), nullable=True)
|
||||||
timeframe = Column(Integer, nullable=True)
|
timeframe = Column(Integer, nullable=True)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -151,6 +151,7 @@ class TradeSchema(BaseModel):
|
|||||||
amount_requested: float
|
amount_requested: float
|
||||||
stake_amount: float
|
stake_amount: float
|
||||||
strategy: str
|
strategy: str
|
||||||
|
buy_tag: Optional[str]
|
||||||
timeframe: int
|
timeframe: int
|
||||||
fee_open: Optional[float]
|
fee_open: Optional[float]
|
||||||
fee_open_cost: Optional[float]
|
fee_open_cost: Optional[float]
|
||||||
|
@ -208,15 +208,25 @@ class Telegram(RPCHandler):
|
|||||||
else:
|
else:
|
||||||
msg['stake_amount_fiat'] = 0
|
msg['stake_amount_fiat'] = 0
|
||||||
|
|
||||||
message = (f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}"
|
content = []
|
||||||
f" (#{msg['trade_id']})\n"
|
content.append(
|
||||||
f"*Amount:* `{msg['amount']:.8f}`\n"
|
f"\N{LARGE BLUE CIRCLE} *{msg['exchange']}:* Buying {msg['pair']}"
|
||||||
f"*Open Rate:* `{msg['limit']:.8f}`\n"
|
f" (#{msg['trade_id']})\n"
|
||||||
f"*Current Rate:* `{msg['current_rate']:.8f}`\n"
|
)
|
||||||
f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}")
|
if msg.get('buy_tag', None):
|
||||||
|
content.append(f"*Buy Tag:* `{msg['buy_tag']}`\n")
|
||||||
|
content.append(f"*Amount:* `{msg['amount']:.8f}`\n")
|
||||||
|
content.append(f"*Open Rate:* `{msg['limit']:.8f}`\n")
|
||||||
|
content.append(f"*Current Rate:* `{msg['current_rate']:.8f}`\n")
|
||||||
|
content.append(
|
||||||
|
f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}"
|
||||||
|
)
|
||||||
if msg.get('fiat_currency', None):
|
if msg.get('fiat_currency', None):
|
||||||
message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}"
|
content.append(
|
||||||
|
f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}"
|
||||||
|
)
|
||||||
|
|
||||||
|
message = ''.join(content)
|
||||||
message += ")`"
|
message += ")`"
|
||||||
return message
|
return message
|
||||||
|
|
||||||
@ -354,6 +364,7 @@ class Telegram(RPCHandler):
|
|||||||
"*Trade ID:* `{trade_id}` `(since {open_date_hum})`",
|
"*Trade ID:* `{trade_id}` `(since {open_date_hum})`",
|
||||||
"*Current Pair:* {pair}",
|
"*Current Pair:* {pair}",
|
||||||
"*Amount:* `{amount} ({stake_amount} {base_currency})`",
|
"*Amount:* `{amount} ({stake_amount} {base_currency})`",
|
||||||
|
"*Buy Tag:* `{buy_tag}`" if r['buy_tag'] else "",
|
||||||
"*Open Rate:* `{open_rate:.8f}`",
|
"*Open Rate:* `{open_rate:.8f}`",
|
||||||
"*Close Rate:* `{close_rate}`" if r['close_rate'] else "",
|
"*Close Rate:* `{close_rate}`" if r['close_rate'] else "",
|
||||||
"*Current Rate:* `{current_rate:.8f}`",
|
"*Current Rate:* `{current_rate:.8f}`",
|
||||||
|
@ -13,7 +13,7 @@ from pandas import DataFrame
|
|||||||
|
|
||||||
from freqtrade.constants import ListPairsWithTimeframes
|
from freqtrade.constants import ListPairsWithTimeframes
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.enums import SellType, SignalType
|
from freqtrade.enums import SellType, SignalTagType, SignalType
|
||||||
from freqtrade.exceptions import OperationalException, StrategyError
|
from freqtrade.exceptions import OperationalException, StrategyError
|
||||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds
|
||||||
from freqtrade.exchange.exchange import timeframe_to_next_date
|
from freqtrade.exchange.exchange import timeframe_to_next_date
|
||||||
@ -422,6 +422,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
logger.debug("Skipping TA Analysis for already analyzed candle")
|
logger.debug("Skipping TA Analysis for already analyzed candle")
|
||||||
dataframe['buy'] = 0
|
dataframe['buy'] = 0
|
||||||
dataframe['sell'] = 0
|
dataframe['sell'] = 0
|
||||||
|
dataframe['buy_tag'] = None
|
||||||
|
|
||||||
# Other Defs in strategy that want to be called every loop here
|
# Other Defs in strategy that want to be called every loop here
|
||||||
# twitter_sell = self.watch_twitter_feed(dataframe, metadata)
|
# twitter_sell = self.watch_twitter_feed(dataframe, metadata)
|
||||||
@ -496,7 +497,12 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
else:
|
else:
|
||||||
raise StrategyError(message)
|
raise StrategyError(message)
|
||||||
|
|
||||||
def get_signal(self, pair: str, timeframe: str, dataframe: DataFrame) -> Tuple[bool, bool]:
|
def get_signal(
|
||||||
|
self,
|
||||||
|
pair: str,
|
||||||
|
timeframe: str,
|
||||||
|
dataframe: DataFrame
|
||||||
|
) -> Tuple[bool, bool, Optional[str]]:
|
||||||
"""
|
"""
|
||||||
Calculates current signal based based on the buy / sell columns of the dataframe.
|
Calculates current signal based based on the buy / sell columns of the dataframe.
|
||||||
Used by Bot to get the signal to buy or sell
|
Used by Bot to get the signal to buy or sell
|
||||||
@ -507,7 +513,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
"""
|
"""
|
||||||
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
||||||
logger.warning(f'Empty candle (OHLCV) data for pair {pair}')
|
logger.warning(f'Empty candle (OHLCV) data for pair {pair}')
|
||||||
return False, False
|
return False, False, None
|
||||||
|
|
||||||
latest_date = dataframe['date'].max()
|
latest_date = dataframe['date'].max()
|
||||||
latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1]
|
latest = dataframe.loc[dataframe['date'] == latest_date].iloc[-1]
|
||||||
@ -522,9 +528,12 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||||
pair, int((arrow.utcnow() - latest_date).total_seconds() // 60)
|
pair, int((arrow.utcnow() - latest_date).total_seconds() // 60)
|
||||||
)
|
)
|
||||||
return False, False
|
return False, False, None
|
||||||
|
|
||||||
|
buy = latest[SignalType.BUY.value] == 1
|
||||||
|
sell = latest[SignalType.SELL.value] == 1
|
||||||
|
buy_tag = latest.get(SignalTagType.BUY_TAG.value, None)
|
||||||
|
|
||||||
(buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1
|
|
||||||
logger.debug('trigger: %s (pair=%s) buy=%s sell=%s',
|
logger.debug('trigger: %s (pair=%s) buy=%s sell=%s',
|
||||||
latest['date'], pair, str(buy), str(sell))
|
latest['date'], pair, str(buy), str(sell))
|
||||||
timeframe_seconds = timeframe_to_seconds(timeframe)
|
timeframe_seconds = timeframe_to_seconds(timeframe)
|
||||||
@ -532,8 +541,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
current_time=datetime.now(timezone.utc),
|
current_time=datetime.now(timezone.utc),
|
||||||
timeframe_seconds=timeframe_seconds,
|
timeframe_seconds=timeframe_seconds,
|
||||||
buy=buy):
|
buy=buy):
|
||||||
return False, sell
|
return False, sell, buy_tag
|
||||||
return buy, sell
|
return buy, sell, buy_tag
|
||||||
|
|
||||||
def ignore_expired_candle(self, latest_date: datetime, current_time: datetime,
|
def ignore_expired_candle(self, latest_date: datetime, current_time: datetime,
|
||||||
timeframe_seconds: int, buy: bool):
|
timeframe_seconds: int, buy: bool):
|
||||||
|
@ -182,7 +182,7 @@ def get_patched_worker(mocker, config) -> Worker:
|
|||||||
return Worker(args=None, config=config)
|
return Worker(args=None, config=config)
|
||||||
|
|
||||||
|
|
||||||
def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
|
def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False, None)) -> None:
|
||||||
"""
|
"""
|
||||||
:param mocker: mocker to patch IStrategy class
|
:param mocker: mocker to patch IStrategy class
|
||||||
:param value: which value IStrategy.get_signal() must return
|
:param value: which value IStrategy.get_signal() must return
|
||||||
|
@ -29,7 +29,6 @@ from tests.optimize import (BTContainer, BTrade, _build_backtest_dataframe,
|
|||||||
|
|
||||||
tests_start_time = arrow.get(2018, 10, 3)
|
tests_start_time = arrow.get(2018, 10, 3)
|
||||||
timeframe_in_minute = 60
|
timeframe_in_minute = 60
|
||||||
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
|
|
||||||
|
|
||||||
# Helpers for this test file
|
# Helpers for this test file
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ class BTrade(NamedTuple):
|
|||||||
sell_reason: SellType
|
sell_reason: SellType
|
||||||
open_tick: int
|
open_tick: int
|
||||||
close_tick: int
|
close_tick: int
|
||||||
|
buy_tag: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class BTContainer(NamedTuple):
|
class BTContainer(NamedTuple):
|
||||||
@ -44,6 +45,7 @@ def _get_frame_time_from_offset(offset):
|
|||||||
|
|
||||||
def _build_backtest_dataframe(data):
|
def _build_backtest_dataframe(data):
|
||||||
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
|
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
|
||||||
|
columns = columns + ['buy_tag'] if len(data[0]) == 9 else columns
|
||||||
|
|
||||||
frame = DataFrame.from_records(data, columns=columns)
|
frame = DataFrame.from_records(data, columns=columns)
|
||||||
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
|
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
|
||||||
|
@ -516,6 +516,26 @@ tc32 = BTContainer(data=[
|
|||||||
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
trades=[BTrade(sell_reason=SellType.TRAILING_STOP_LOSS, open_tick=1, close_tick=1)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Test 33: trailing_stop should be triggered immediately on trade open candle.
|
||||||
|
# stop-loss: 1%, ROI: 10% (should not apply)
|
||||||
|
tc33 = BTContainer(data=[
|
||||||
|
# D O H L C V B S BT
|
||||||
|
[0, 5000, 5050, 4950, 5000, 6172, 1, 0, 'buy_signal_01'],
|
||||||
|
[1, 5000, 5500, 5000, 4900, 6172, 0, 0, None], # enter trade (signal on last candle) and stop
|
||||||
|
[2, 4900, 5250, 4500, 5100, 6172, 0, 0, None],
|
||||||
|
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, None],
|
||||||
|
[4, 4750, 4950, 4350, 4750, 6172, 0, 0, None]],
|
||||||
|
stop_loss=-0.01, roi={"0": 0.10}, profit_perc=-0.01, trailing_stop=True,
|
||||||
|
trailing_only_offset_is_reached=True, trailing_stop_positive_offset=0.02,
|
||||||
|
trailing_stop_positive=0.01, use_custom_stoploss=True,
|
||||||
|
trades=[BTrade(
|
||||||
|
sell_reason=SellType.TRAILING_STOP_LOSS,
|
||||||
|
open_tick=1,
|
||||||
|
close_tick=1,
|
||||||
|
buy_tag='buy_signal_01'
|
||||||
|
)]
|
||||||
|
)
|
||||||
|
|
||||||
TESTS = [
|
TESTS = [
|
||||||
tc0,
|
tc0,
|
||||||
tc1,
|
tc1,
|
||||||
@ -550,6 +570,7 @@ TESTS = [
|
|||||||
tc30,
|
tc30,
|
||||||
tc31,
|
tc31,
|
||||||
tc32,
|
tc32,
|
||||||
|
tc33,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -599,5 +620,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
|
|||||||
for c, trade in enumerate(data.trades):
|
for c, trade in enumerate(data.trades):
|
||||||
res = results.iloc[c]
|
res = results.iloc[c]
|
||||||
assert res.sell_reason == trade.sell_reason.value
|
assert res.sell_reason == trade.sell_reason.value
|
||||||
|
assert res.buy_tag == trade.buy_tag
|
||||||
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
assert res.open_date == _get_frame_time_from_offset(trade.open_tick)
|
||||||
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
assert res.close_date == _get_frame_time_from_offset(trade.close_tick)
|
||||||
|
@ -496,6 +496,7 @@ def test_backtest__enter_trade(default_conf, fee, mocker) -> None:
|
|||||||
0, # Sell
|
0, # Sell
|
||||||
0.00099, # Low
|
0.00099, # Low
|
||||||
0.0012, # High
|
0.0012, # High
|
||||||
|
'', # Buy Signal Name
|
||||||
]
|
]
|
||||||
trade = backtesting._enter_trade(pair, row=row)
|
trade = backtesting._enter_trade(pair, row=row)
|
||||||
assert isinstance(trade, LocalTrade)
|
assert isinstance(trade, LocalTrade)
|
||||||
@ -583,6 +584,7 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
|||||||
'min_rate': [0.1038, 0.10302485],
|
'min_rate': [0.1038, 0.10302485],
|
||||||
'max_rate': [0.10501, 0.1038888],
|
'max_rate': [0.10501, 0.1038888],
|
||||||
'is_open': [False, False],
|
'is_open': [False, False],
|
||||||
|
'buy_tag': [None, None],
|
||||||
})
|
})
|
||||||
pd.testing.assert_frame_equal(results, expected)
|
pd.testing.assert_frame_equal(results, expected)
|
||||||
data_pair = processed[pair]
|
data_pair = processed[pair]
|
||||||
@ -858,7 +860,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
|||||||
'locks': [],
|
'locks': [],
|
||||||
'rejected_signals': 20,
|
'rejected_signals': 20,
|
||||||
'final_balance': 1000,
|
'final_balance': 1000,
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist',
|
||||||
PropertyMock(return_value=['UNITTEST/BTC']))
|
PropertyMock(return_value=['UNITTEST/BTC']))
|
||||||
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
|
||||||
|
@ -35,7 +35,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
@ -69,6 +69,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'min_rate': ANY,
|
'min_rate': ANY,
|
||||||
'max_rate': ANY,
|
'max_rate': ANY,
|
||||||
'strategy': ANY,
|
'strategy': ANY,
|
||||||
|
'buy_tag': ANY,
|
||||||
'timeframe': 5,
|
'timeframe': 5,
|
||||||
'open_order_id': ANY,
|
'open_order_id': ANY,
|
||||||
'close_date': None,
|
'close_date': None,
|
||||||
@ -135,6 +136,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'min_rate': ANY,
|
'min_rate': ANY,
|
||||||
'max_rate': ANY,
|
'max_rate': ANY,
|
||||||
'strategy': ANY,
|
'strategy': ANY,
|
||||||
|
'buy_tag': ANY,
|
||||||
'timeframe': ANY,
|
'timeframe': ANY,
|
||||||
'open_order_id': ANY,
|
'open_order_id': ANY,
|
||||||
'close_date': None,
|
'close_date': None,
|
||||||
@ -190,7 +192,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
)
|
)
|
||||||
del default_conf['fiat_display_currency']
|
del default_conf['fiat_display_currency']
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
@ -237,7 +239,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_currency']
|
fiat_display_currency = default_conf['fiat_display_currency']
|
||||||
|
|
||||||
@ -369,7 +371,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_currency']
|
fiat_display_currency = default_conf['fiat_display_currency']
|
||||||
|
|
||||||
@ -457,7 +459,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_currency']
|
fiat_display_currency = default_conf['fiat_display_currency']
|
||||||
|
|
||||||
@ -524,7 +526,7 @@ def test_rpc_balance_handle_error(default_conf, mocker):
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
rpc._fiat_converter = CryptoToFiatConverter()
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
with pytest.raises(RPCException, match="Error getting current tickers."):
|
with pytest.raises(RPCException, match="Error getting current tickers."):
|
||||||
@ -565,7 +567,7 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
|||||||
)
|
)
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
rpc._fiat_converter = CryptoToFiatConverter()
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
|
|
||||||
@ -610,7 +612,7 @@ def test_rpc_start(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
|
|
||||||
@ -631,7 +633,7 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
|
|
||||||
@ -653,7 +655,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
|
|
||||||
@ -685,7 +687,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
|||||||
mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000)
|
mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -803,7 +805,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -836,7 +838,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
counts = rpc._rpc_count()
|
counts = rpc._rpc_count()
|
||||||
@ -861,7 +863,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
pair = 'ETH/BTC'
|
pair = 'ETH/BTC'
|
||||||
trade = rpc._rpc_forcebuy(pair, None)
|
trade = rpc._rpc_forcebuy(pair, None)
|
||||||
@ -887,7 +889,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
|||||||
# Test not buying
|
# Test not buying
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
freqtradebot.config['stake_amount'] = 0
|
freqtradebot.config['stake_amount'] = 0
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
pair = 'TKN/BTC'
|
pair = 'TKN/BTC'
|
||||||
trade = rpc._rpc_forcebuy(pair, None)
|
trade = rpc._rpc_forcebuy(pair, None)
|
||||||
@ -900,7 +902,7 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None:
|
|||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
pair = 'ETH/BTC'
|
pair = 'ETH/BTC'
|
||||||
with pytest.raises(RPCException, match=r'trader is not running'):
|
with pytest.raises(RPCException, match=r'trader is not running'):
|
||||||
@ -911,7 +913,7 @@ def test_rpcforcebuy_disabled(mocker, default_conf) -> None:
|
|||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
pair = 'ETH/BTC'
|
pair = 'ETH/BTC'
|
||||||
with pytest.raises(RPCException, match=r'Forcebuy not enabled.'):
|
with pytest.raises(RPCException, match=r'Forcebuy not enabled.'):
|
||||||
|
@ -442,7 +442,7 @@ def test_api_balance(botclient, mocker, rpc_balance):
|
|||||||
|
|
||||||
def test_api_count(botclient, mocker, ticker, fee, markets):
|
def test_api_count(botclient, mocker, ticker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -504,7 +504,7 @@ def test_api_locks(botclient):
|
|||||||
|
|
||||||
def test_api_show_config(botclient, mocker):
|
def test_api_show_config(botclient, mocker):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
|
|
||||||
rc = client_get(client, f"{BASE_URI}/show_config")
|
rc = client_get(client, f"{BASE_URI}/show_config")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
@ -522,7 +522,7 @@ def test_api_show_config(botclient, mocker):
|
|||||||
|
|
||||||
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -540,7 +540,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
|
|||||||
|
|
||||||
def test_api_trades(botclient, mocker, fee, markets):
|
def test_api_trades(botclient, mocker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
markets=PropertyMock(return_value=markets)
|
markets=PropertyMock(return_value=markets)
|
||||||
@ -568,7 +568,7 @@ def test_api_trades(botclient, mocker, fee, markets):
|
|||||||
|
|
||||||
def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
markets=PropertyMock(return_value=markets),
|
markets=PropertyMock(return_value=markets),
|
||||||
@ -588,7 +588,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
|||||||
|
|
||||||
def test_api_delete_trade(botclient, mocker, fee, markets):
|
def test_api_delete_trade(botclient, mocker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
stoploss_mock = MagicMock()
|
stoploss_mock = MagicMock()
|
||||||
cancel_mock = MagicMock()
|
cancel_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -662,7 +662,7 @@ def test_api_logs(botclient):
|
|||||||
|
|
||||||
def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -678,7 +678,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
|||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_api_profit(botclient, mocker, ticker, fee, markets):
|
def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -729,7 +729,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
|
|||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -757,7 +757,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
|||||||
|
|
||||||
def test_api_performance(botclient, fee):
|
def test_api_performance(botclient, fee):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
|
|
||||||
trade = Trade(
|
trade = Trade(
|
||||||
pair='LTC/ETH',
|
pair='LTC/ETH',
|
||||||
@ -803,7 +803,7 @@ def test_api_performance(botclient, fee):
|
|||||||
|
|
||||||
def test_api_status(botclient, mocker, ticker, fee, markets):
|
def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||||
ftbot, client = botclient
|
ftbot, client = botclient
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
get_balances=MagicMock(return_value=ticker),
|
get_balances=MagicMock(return_value=ticker),
|
||||||
@ -875,6 +875,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
|||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': 'DefaultStrategy',
|
'strategy': 'DefaultStrategy',
|
||||||
|
'buy_tag': None,
|
||||||
'timeframe': 5,
|
'timeframe': 5,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
}
|
}
|
||||||
@ -1029,6 +1030,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
|||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': 'DefaultStrategy',
|
'strategy': 'DefaultStrategy',
|
||||||
|
'buy_tag': None,
|
||||||
'timeframe': 5,
|
'timeframe': 5,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
}
|
}
|
||||||
@ -1044,7 +1046,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets):
|
|||||||
markets=PropertyMock(return_value=markets),
|
markets=PropertyMock(return_value=markets),
|
||||||
_is_dry_limit_order_filled=MagicMock(return_value=False),
|
_is_dry_limit_order_filled=MagicMock(return_value=False),
|
||||||
)
|
)
|
||||||
patch_get_signal(ftbot, (True, False))
|
patch_get_signal(ftbot, (True, False, None))
|
||||||
|
|
||||||
rc = client_post(client, f"{BASE_URI}/forcesell",
|
rc = client_post(client, f"{BASE_URI}/forcesell",
|
||||||
data='{"tradeid": "1"}')
|
data='{"tradeid": "1"}')
|
||||||
@ -1185,8 +1187,10 @@ def test_api_plot_config(botclient):
|
|||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json() == {}
|
assert rc.json() == {}
|
||||||
|
|
||||||
ftbot.strategy.plot_config = {'main_plot': {'sma': {}},
|
ftbot.strategy.plot_config = {
|
||||||
'subplots': {'RSI': {'rsi': {'color': 'red'}}}}
|
'main_plot': {'sma': {}},
|
||||||
|
'subplots': {'RSI': {'rsi': {'color': 'red'}}}
|
||||||
|
}
|
||||||
rc = client_get(client, f"{BASE_URI}/plot_config")
|
rc = client_get(client, f"{BASE_URI}/plot_config")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert rc.json() == ftbot.strategy.plot_config
|
assert rc.json() == ftbot.strategy.plot_config
|
||||||
|
@ -119,7 +119,7 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None:
|
|||||||
rpc = RPC(bot)
|
rpc = RPC(bot)
|
||||||
dummy = DummyCls(rpc, default_conf)
|
dummy = DummyCls(rpc, default_conf)
|
||||||
|
|
||||||
patch_get_signal(bot, (True, False))
|
patch_get_signal(bot, (True, False, None))
|
||||||
dummy.dummy_handler(update=update, context=MagicMock())
|
dummy.dummy_handler(update=update, context=MagicMock())
|
||||||
assert dummy.state['called'] is True
|
assert dummy.state['called'] is True
|
||||||
assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
|
assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
|
||||||
@ -139,7 +139,7 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
|||||||
rpc = RPC(bot)
|
rpc = RPC(bot)
|
||||||
dummy = DummyCls(rpc, default_conf)
|
dummy = DummyCls(rpc, default_conf)
|
||||||
|
|
||||||
patch_get_signal(bot, (True, False))
|
patch_get_signal(bot, (True, False, None))
|
||||||
dummy.dummy_handler(update=update, context=MagicMock())
|
dummy.dummy_handler(update=update, context=MagicMock())
|
||||||
assert dummy.state['called'] is False
|
assert dummy.state['called'] is False
|
||||||
assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog)
|
assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog)
|
||||||
@ -155,7 +155,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None
|
|||||||
bot = FreqtradeBot(default_conf)
|
bot = FreqtradeBot(default_conf)
|
||||||
rpc = RPC(bot)
|
rpc = RPC(bot)
|
||||||
dummy = DummyCls(rpc, default_conf)
|
dummy = DummyCls(rpc, default_conf)
|
||||||
patch_get_signal(bot, (True, False))
|
patch_get_signal(bot, (True, False, None))
|
||||||
|
|
||||||
dummy.dummy_exception(update=update, context=MagicMock())
|
dummy.dummy_exception(update=update, context=MagicMock())
|
||||||
assert dummy.state['called'] is False
|
assert dummy.state['called'] is False
|
||||||
@ -185,6 +185,7 @@ def test_telegram_status(default_conf, update, mocker) -> None:
|
|||||||
'current_rate': 1.098e-05,
|
'current_rate': 1.098e-05,
|
||||||
'amount': 90.99181074,
|
'amount': 90.99181074,
|
||||||
'stake_amount': 90.99181074,
|
'stake_amount': 90.99181074,
|
||||||
|
'buy_tag': None,
|
||||||
'close_profit_pct': None,
|
'close_profit_pct': None,
|
||||||
'profit': -0.0059,
|
'profit': -0.0059,
|
||||||
'profit_pct': -0.59,
|
'profit_pct': -0.59,
|
||||||
@ -228,7 +229,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
|
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
# Status is also enabled when stopped
|
# Status is also enabled when stopped
|
||||||
@ -285,7 +286,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
|
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
# Status table is also enabled when stopped
|
# Status table is also enabled when stopped
|
||||||
@ -329,7 +330,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
|
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
@ -400,7 +401,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Try invalid data
|
# Try invalid data
|
||||||
msg_mock.reset_mock()
|
msg_mock.reset_mock()
|
||||||
@ -432,7 +433,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
telegram._profit(update=update, context=MagicMock())
|
telegram._profit(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
@ -487,7 +488,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
telegram._stats(update=update, context=MagicMock())
|
telegram._stats(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
@ -513,7 +514,7 @@ def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tick
|
|||||||
side_effect=lambda a, b: f"{a}/{b}")
|
side_effect=lambda a, b: f"{a}/{b}")
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
telegram._balance(update=update, context=MagicMock())
|
telegram._balance(update=update, context=MagicMock())
|
||||||
result = msg_mock.call_args_list[0][0][0]
|
result = msg_mock.call_args_list[0][0][0]
|
||||||
@ -536,7 +537,7 @@ def test_balance_handle_empty_response(default_conf, update, mocker) -> None:
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
freqtradebot.config['dry_run'] = False
|
freqtradebot.config['dry_run'] = False
|
||||||
telegram._balance(update=update, context=MagicMock())
|
telegram._balance(update=update, context=MagicMock())
|
||||||
@ -549,7 +550,7 @@ def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
telegram._balance(update=update, context=MagicMock())
|
telegram._balance(update=update, context=MagicMock())
|
||||||
result = msg_mock.call_args_list[0][0][0]
|
result = msg_mock.call_args_list[0][0][0]
|
||||||
@ -578,7 +579,7 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None
|
|||||||
})
|
})
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
telegram._balance(update=update, context=MagicMock())
|
telegram._balance(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count > 1
|
assert msg_mock.call_count > 1
|
||||||
@ -677,7 +678,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
telegram = Telegram(rpc, default_conf)
|
telegram = Telegram(rpc, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
@ -736,7 +737,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
telegram = Telegram(rpc, default_conf)
|
telegram = Telegram(rpc, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
@ -797,7 +798,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
|||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
telegram = Telegram(rpc, default_conf)
|
telegram = Telegram(rpc, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
@ -838,7 +839,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
return_value=15000.0)
|
return_value=15000.0)
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Trader is not running
|
# Trader is not running
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -876,7 +877,7 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None:
|
|||||||
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||||
|
|
||||||
telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# /forcebuy ETH/BTC
|
# /forcebuy ETH/BTC
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
@ -905,7 +906,7 @@ def test_forcebuy_handle_exception(default_conf, update, mocker) -> None:
|
|||||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
update.message.text = '/forcebuy ETH/Nonepair'
|
update.message.text = '/forcebuy ETH/Nonepair'
|
||||||
telegram._forcebuy(update=update, context=MagicMock())
|
telegram._forcebuy(update=update, context=MagicMock())
|
||||||
@ -922,7 +923,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
|
|||||||
|
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
|
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
context = MagicMock()
|
context = MagicMock()
|
||||||
context.args = []
|
context.args = []
|
||||||
@ -950,7 +951,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtradebot.enter_positions()
|
freqtradebot.enter_positions()
|
||||||
@ -978,7 +979,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None:
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
telegram._count(update=update, context=MagicMock())
|
telegram._count(update=update, context=MagicMock())
|
||||||
@ -1007,7 +1008,7 @@ def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||||
patch_get_signal(freqtradebot, (True, False))
|
patch_get_signal(freqtradebot, (True, False, None))
|
||||||
telegram._locks(update=update, context=MagicMock())
|
telegram._locks(update=update, context=MagicMock())
|
||||||
assert msg_mock.call_count == 1
|
assert msg_mock.call_count == 1
|
||||||
assert 'No active locks.' in msg_mock.call_args_list[0][0][0]
|
assert 'No active locks.' in msg_mock.call_args_list[0][0][0]
|
||||||
@ -1253,6 +1254,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
|
|||||||
msg = {
|
msg = {
|
||||||
'type': RPCMessageType.BUY,
|
'type': RPCMessageType.BUY,
|
||||||
'trade_id': 1,
|
'trade_id': 1,
|
||||||
|
'buy_tag': 'buy_signal_01',
|
||||||
'exchange': 'Binance',
|
'exchange': 'Binance',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'limit': 1.099e-05,
|
'limit': 1.099e-05,
|
||||||
@ -1270,6 +1272,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
|
|||||||
telegram.send_msg(msg)
|
telegram.send_msg(msg)
|
||||||
assert msg_mock.call_args[0][0] \
|
assert msg_mock.call_args[0][0] \
|
||||||
== '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \
|
== '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \
|
||||||
|
'*Buy Tag:* `buy_signal_01`\n' \
|
||||||
'*Amount:* `1333.33333333`\n' \
|
'*Amount:* `1333.33333333`\n' \
|
||||||
'*Open Rate:* `0.00001099`\n' \
|
'*Open Rate:* `0.00001099`\n' \
|
||||||
'*Current Rate:* `0.00001099`\n' \
|
'*Current Rate:* `0.00001099`\n' \
|
||||||
@ -1297,6 +1300,7 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None:
|
|||||||
|
|
||||||
telegram.send_msg({
|
telegram.send_msg({
|
||||||
'type': RPCMessageType.BUY_CANCEL,
|
'type': RPCMessageType.BUY_CANCEL,
|
||||||
|
'buy_tag': 'buy_signal_01',
|
||||||
'trade_id': 1,
|
'trade_id': 1,
|
||||||
'exchange': 'Binance',
|
'exchange': 'Binance',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
@ -1314,6 +1318,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
|
|||||||
|
|
||||||
telegram.send_msg({
|
telegram.send_msg({
|
||||||
'type': RPCMessageType.BUY_FILL,
|
'type': RPCMessageType.BUY_FILL,
|
||||||
|
'buy_tag': 'buy_signal_01',
|
||||||
'trade_id': 1,
|
'trade_id': 1,
|
||||||
'exchange': 'Binance',
|
'exchange': 'Binance',
|
||||||
'pair': 'ETH/USDT',
|
'pair': 'ETH/USDT',
|
||||||
@ -1498,6 +1503,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
|||||||
|
|
||||||
telegram.send_msg({
|
telegram.send_msg({
|
||||||
'type': RPCMessageType.BUY,
|
'type': RPCMessageType.BUY,
|
||||||
|
'buy_tag': 'buy_signal_01',
|
||||||
'trade_id': 1,
|
'trade_id': 1,
|
||||||
'exchange': 'Binance',
|
'exchange': 'Binance',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
@ -1512,6 +1518,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
|||||||
'open_date': arrow.utcnow().shift(hours=-1)
|
'open_date': arrow.utcnow().shift(hours=-1)
|
||||||
})
|
})
|
||||||
assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n'
|
assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n'
|
||||||
|
'*Buy Tag:* `buy_signal_01`\n'
|
||||||
'*Amount:* `1333.33333333`\n'
|
'*Amount:* `1333.33333333`\n'
|
||||||
'*Open Rate:* `0.00001099`\n'
|
'*Open Rate:* `0.00001099`\n'
|
||||||
'*Current Rate:* `0.00001099`\n'
|
'*Current Rate:* `0.00001099`\n'
|
||||||
|
@ -38,15 +38,20 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history):
|
|||||||
mocked_history['buy'] = 0
|
mocked_history['buy'] = 0
|
||||||
mocked_history.loc[1, 'sell'] = 1
|
mocked_history.loc[1, 'sell'] = 1
|
||||||
|
|
||||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True)
|
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True, None)
|
||||||
mocked_history.loc[1, 'sell'] = 0
|
mocked_history.loc[1, 'sell'] = 0
|
||||||
mocked_history.loc[1, 'buy'] = 1
|
mocked_history.loc[1, 'buy'] = 1
|
||||||
|
|
||||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False)
|
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, None)
|
||||||
mocked_history.loc[1, 'sell'] = 0
|
mocked_history.loc[1, 'sell'] = 0
|
||||||
mocked_history.loc[1, 'buy'] = 0
|
mocked_history.loc[1, 'buy'] = 0
|
||||||
|
|
||||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False)
|
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False, None)
|
||||||
|
mocked_history.loc[1, 'sell'] = 0
|
||||||
|
mocked_history.loc[1, 'buy'] = 1
|
||||||
|
mocked_history.loc[1, 'buy_tag'] = 'buy_signal_01'
|
||||||
|
|
||||||
|
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False, 'buy_signal_01')
|
||||||
|
|
||||||
|
|
||||||
def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history):
|
def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history):
|
||||||
@ -63,15 +68,21 @@ def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history):
|
|||||||
|
|
||||||
|
|
||||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||||
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame())
|
assert (False, False, None) == _STRATEGY.get_signal(
|
||||||
|
'foo', default_conf['timeframe'], DataFrame()
|
||||||
|
)
|
||||||
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None)
|
assert (False, False, None) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None)
|
||||||
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
|
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
|
|
||||||
assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([]))
|
assert (False, False, None) == _STRATEGY.get_signal(
|
||||||
|
'baz',
|
||||||
|
default_conf['timeframe'],
|
||||||
|
DataFrame([])
|
||||||
|
)
|
||||||
assert log_has('Empty candle (OHLCV) data for pair baz', caplog)
|
assert log_has('Empty candle (OHLCV) data for pair baz', caplog)
|
||||||
|
|
||||||
|
|
||||||
@ -107,7 +118,11 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history):
|
|||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
mocker.patch.object(_STRATEGY, 'assert_df')
|
mocker.patch.object(_STRATEGY, 'assert_df')
|
||||||
|
|
||||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history)
|
assert (False, False, None) == _STRATEGY.get_signal(
|
||||||
|
'xyz',
|
||||||
|
default_conf['timeframe'],
|
||||||
|
mocked_history
|
||||||
|
)
|
||||||
assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog)
|
assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog)
|
||||||
|
|
||||||
|
|
||||||
|
@ -536,7 +536,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||||||
)
|
)
|
||||||
default_conf['stake_amount'] = 10
|
default_conf['stake_amount'] = 10
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade, value=(False, False))
|
patch_get_signal(freqtrade, value=(False, False, None))
|
||||||
|
|
||||||
Trade.query = MagicMock()
|
Trade.query = MagicMock()
|
||||||
Trade.query.filter = MagicMock()
|
Trade.query.filter = MagicMock()
|
||||||
@ -757,7 +757,10 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
|
|||||||
refresh_latest_ohlcv=refresh_mock,
|
refresh_latest_ohlcv=refresh_mock,
|
||||||
)
|
)
|
||||||
inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")])
|
inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")])
|
||||||
mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False))
|
mocker.patch(
|
||||||
|
'freqtrade.strategy.interface.IStrategy.get_signal',
|
||||||
|
return_value=(False, False, '')
|
||||||
|
)
|
||||||
mocker.patch('time.sleep', return_value=None)
|
mocker.patch('time.sleep', return_value=None)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
@ -1857,7 +1860,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order_open, limi
|
|||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.open_order_id == limit_sell_order['id']
|
assert trade.open_order_id == limit_sell_order['id']
|
||||||
|
|
||||||
@ -1882,7 +1885,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True, None))
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
@ -1893,7 +1896,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open,
|
|||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
|
|
||||||
# Buy is triggering, so buying ...
|
# Buy is triggering, so buying ...
|
||||||
patch_get_signal(freqtrade, value=(True, False))
|
patch_get_signal(freqtrade, value=(True, False, None))
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -1901,7 +1904,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open,
|
|||||||
assert trades[0].is_open is True
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Buy and Sell are not triggering, so doing nothing ...
|
# Buy and Sell are not triggering, so doing nothing ...
|
||||||
patch_get_signal(freqtrade, value=(False, False))
|
patch_get_signal(freqtrade, value=(False, False, None))
|
||||||
assert freqtrade.handle_trade(trades[0]) is False
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -1909,7 +1912,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open,
|
|||||||
assert trades[0].is_open is True
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Buy and Sell are triggering, so doing nothing ...
|
# Buy and Sell are triggering, so doing nothing ...
|
||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True, None))
|
||||||
assert freqtrade.handle_trade(trades[0]) is False
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
@ -1917,7 +1920,7 @@ def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order_open,
|
|||||||
assert trades[0].is_open is True
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Sell is triggering, guess what : we are Selling!
|
# Sell is triggering, guess what : we are Selling!
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
assert freqtrade.handle_trade(trades[0]) is True
|
assert freqtrade.handle_trade(trades[0]) is True
|
||||||
|
|
||||||
@ -1935,7 +1938,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
patch_get_signal(freqtrade, value=(True, False))
|
patch_get_signal(freqtrade, value=(True, False, None))
|
||||||
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
|
||||||
|
|
||||||
freqtrade.enter_positions()
|
freqtrade.enter_positions()
|
||||||
@ -1948,7 +1951,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order_open,
|
|||||||
# we might just want to check if we are in a sell condition without
|
# we might just want to check if we are in a sell condition without
|
||||||
# executing
|
# executing
|
||||||
# if ROI is reached we must sell
|
# if ROI is reached we must sell
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI",
|
assert log_has("ETH/BTC - Required profit reached. sell_type=SellType.ROI",
|
||||||
caplog)
|
caplog)
|
||||||
@ -1974,10 +1977,10 @@ def test_handle_trade_use_sell_signal(
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, False))
|
patch_get_signal(freqtrade, value=(False, False, None))
|
||||||
assert not freqtrade.handle_trade(trade)
|
assert not freqtrade.handle_trade(trade)
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade)
|
assert freqtrade.handle_trade(trade)
|
||||||
assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL",
|
assert log_has("ETH/BTC - Sell signal received. sell_type=SellType.SELL_SIGNAL",
|
||||||
caplog)
|
caplog)
|
||||||
@ -3013,7 +3016,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, limit_buy
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
freqtrade.strategy.sell_profit_offset = 0.0
|
freqtrade.strategy.sell_profit_offset = 0.0
|
||||||
@ -3048,7 +3051,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, limit_bu
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
|
|
||||||
@ -3079,7 +3082,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, limit_buy_o
|
|||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
|
|
||||||
@ -3111,7 +3114,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, limit_buy_
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
|
|
||||||
@ -3140,7 +3143,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, limit_buy_order_
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
amnt = trade.amount
|
amnt = trade.amount
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
|
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
|
||||||
|
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
@ -3259,11 +3262,11 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_buy_order
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
# Test if buy-signal is absent (should sell due to roi = true)
|
# Test if buy-signal is absent (should sell due to roi = true)
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.ROI.value
|
assert trade.sell_reason == SellType.ROI.value
|
||||||
|
|
||||||
@ -3524,11 +3527,11 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, limit_b
|
|||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
# Sell due to min_roi_reached
|
# Sell due to min_roi_reached
|
||||||
patch_get_signal(freqtrade, value=(True, True))
|
patch_get_signal(freqtrade, value=(True, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
# Test if buy-signal is absent
|
# Test if buy-signal is absent
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
assert trade.sell_reason == SellType.SELL_SIGNAL.value
|
||||||
|
|
||||||
@ -4056,7 +4059,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o
|
|||||||
freqtrade.wallets.update()
|
freqtrade.wallets.update()
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
|
|
||||||
patch_get_signal(freqtrade, value=(False, True))
|
patch_get_signal(freqtrade, value=(False, True, None))
|
||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0]
|
assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0]
|
||||||
|
|
||||||
|
@ -861,6 +861,7 @@ def test_to_json(default_conf, fee):
|
|||||||
open_date=arrow.utcnow().shift(hours=-2).datetime,
|
open_date=arrow.utcnow().shift(hours=-2).datetime,
|
||||||
open_rate=0.123,
|
open_rate=0.123,
|
||||||
exchange='binance',
|
exchange='binance',
|
||||||
|
buy_tag=None,
|
||||||
open_order_id='dry_run_buy_12345'
|
open_order_id='dry_run_buy_12345'
|
||||||
)
|
)
|
||||||
result = trade.to_json()
|
result = trade.to_json()
|
||||||
@ -910,6 +911,7 @@ def test_to_json(default_conf, fee):
|
|||||||
'min_rate': None,
|
'min_rate': None,
|
||||||
'max_rate': None,
|
'max_rate': None,
|
||||||
'strategy': None,
|
'strategy': None,
|
||||||
|
'buy_tag': None,
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
}
|
}
|
||||||
@ -926,6 +928,7 @@ def test_to_json(default_conf, fee):
|
|||||||
close_date=arrow.utcnow().shift(hours=-1).datetime,
|
close_date=arrow.utcnow().shift(hours=-1).datetime,
|
||||||
open_rate=0.123,
|
open_rate=0.123,
|
||||||
close_rate=0.125,
|
close_rate=0.125,
|
||||||
|
buy_tag='buys_signal_001',
|
||||||
exchange='binance',
|
exchange='binance',
|
||||||
)
|
)
|
||||||
result = trade.to_json()
|
result = trade.to_json()
|
||||||
@ -975,6 +978,7 @@ def test_to_json(default_conf, fee):
|
|||||||
'sell_reason': None,
|
'sell_reason': None,
|
||||||
'sell_order_status': None,
|
'sell_order_status': None,
|
||||||
'strategy': None,
|
'strategy': None,
|
||||||
|
'buy_tag': 'buys_signal_001',
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
}
|
}
|
||||||
@ -1319,7 +1323,7 @@ def test_Trade_object_idem():
|
|||||||
'get_open_trades_without_assigned_fees',
|
'get_open_trades_without_assigned_fees',
|
||||||
'get_open_order_trades',
|
'get_open_order_trades',
|
||||||
'get_trades',
|
'get_trades',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parent (LocalTrade) should have the same attributes
|
# Parent (LocalTrade) should have the same attributes
|
||||||
for item in trade:
|
for item in trade:
|
||||||
|
Loading…
Reference in New Issue
Block a user