Merge branch 'develop' into money_mgt
This commit is contained in:
commit
714ac6dd08
@ -74,6 +74,7 @@
|
|||||||
"chat_id": "your_telegram_chat_id"
|
"chat_id": "your_telegram_chat_id"
|
||||||
},
|
},
|
||||||
"initial_state": "running",
|
"initial_state": "running",
|
||||||
|
"forcebuy_enable": false,
|
||||||
"internals": {
|
"internals": {
|
||||||
"process_throttle_secs": 5
|
"process_throttle_secs": 5
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
},
|
},
|
||||||
"db_url": "sqlite:///tradesv3.sqlite",
|
"db_url": "sqlite:///tradesv3.sqlite",
|
||||||
"initial_state": "running",
|
"initial_state": "running",
|
||||||
|
"forcebuy_enable": false,
|
||||||
"internals": {
|
"internals": {
|
||||||
"process_throttle_secs": 5
|
"process_throttle_secs": 5
|
||||||
},
|
},
|
||||||
|
@ -53,13 +53,14 @@ The table below will list all configuration parameters.
|
|||||||
| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram.
|
| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram.
|
||||||
| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
|
| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
|
||||||
| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`.
|
| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`.
|
||||||
| `webhook.enabled` | false | No | Enable useage of Webhook notifications
|
| `webhook.enabled` | false | No | Enable usage of Webhook notifications
|
||||||
| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details.
|
| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details.
|
||||||
| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
||||||
| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
||||||
| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
|
||||||
| `db_url` | `sqlite:///tradesv3.sqlite` | No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`.
|
| `db_url` | `sqlite:///tradesv3.sqlite` | No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`.
|
||||||
| `initial_state` | running | No | Defines the initial application state. More information below.
|
| `initial_state` | running | No | Defines the initial application state. More information below.
|
||||||
|
| `forcebuy_enable` | false | No | Enables the RPC Commands to force a buy. More information below.
|
||||||
| `strategy` | DefaultStrategy | No | Defines Strategy class to use.
|
| `strategy` | DefaultStrategy | No | Defines Strategy class to use.
|
||||||
| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder).
|
| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder).
|
||||||
| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second.
|
| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second.
|
||||||
@ -113,6 +114,15 @@ Go to the [trailing stoploss Documentation](stoploss.md) for details on trailing
|
|||||||
Possible values are `running` or `stopped`. (default=`running`)
|
Possible values are `running` or `stopped`. (default=`running`)
|
||||||
If the value is `stopped` the bot has to be started with `/start` first.
|
If the value is `stopped` the bot has to be started with `/start` first.
|
||||||
|
|
||||||
|
### Understand forcebuy_enable
|
||||||
|
|
||||||
|
`forcebuy_enable` enables the usage of forcebuy commands via Telegram.
|
||||||
|
This is disabled for security reasons by default, and will show a warning message on startup if enabled.
|
||||||
|
You send `/forcebuy ETH/BTC` to the bot, who buys the pair and holds it until a regular sell-signal appears (ROI, stoploss, /forcesell).
|
||||||
|
|
||||||
|
Can be dangerous with some strategies, so use with care
|
||||||
|
See [the telegram documentation](telegram-usage.md) for details on usage.
|
||||||
|
|
||||||
### Understand process_throttle_secs
|
### Understand process_throttle_secs
|
||||||
|
|
||||||
`process_throttle_secs` is an optional field that defines in seconds how long the bot should wait
|
`process_throttle_secs` is an optional field that defines in seconds how long the bot should wait
|
||||||
|
@ -23,6 +23,7 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
| `/profit` | | Display a summary of your profit/loss from close trades and some stats about your performance
|
| `/profit` | | Display a summary of your profit/loss from close trades and some stats about your performance
|
||||||
| `/forcesell <trade_id>` | | Instantly sells the given trade (Ignoring `minimum_roi`).
|
| `/forcesell <trade_id>` | | Instantly sells the given trade (Ignoring `minimum_roi`).
|
||||||
| `/forcesell all` | | Instantly sells all open trades (Ignoring `minimum_roi`).
|
| `/forcesell all` | | Instantly sells all open trades (Ignoring `minimum_roi`).
|
||||||
|
| `/forcebuy <pair> [rate]` | | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True)
|
||||||
| `/performance` | | Show performance of each finished trade grouped by pair
|
| `/performance` | | Show performance of each finished trade grouped by pair
|
||||||
| `/balance` | | Show account balance per currency
|
| `/balance` | | Show account balance per currency
|
||||||
| `/daily <n>` | 7 | Shows profit or loss per day, over the last n days
|
| `/daily <n>` | 7 | Shows profit or loss per day, over the last n days
|
||||||
@ -30,16 +31,20 @@ official commands. You can ask at any moment for help with `/help`.
|
|||||||
| `/version` | | Show version
|
| `/version` | | Show version
|
||||||
|
|
||||||
## Telegram commands in action
|
## Telegram commands in action
|
||||||
|
|
||||||
Below, example of Telegram message you will receive for each command.
|
Below, example of Telegram message you will receive for each command.
|
||||||
|
|
||||||
### /start
|
### /start
|
||||||
|
|
||||||
> **Status:** `running`
|
> **Status:** `running`
|
||||||
|
|
||||||
### /stop
|
### /stop
|
||||||
|
|
||||||
> `Stopping trader ...`
|
> `Stopping trader ...`
|
||||||
> **Status:** `stopped`
|
> **Status:** `stopped`
|
||||||
|
|
||||||
## /status
|
## /status
|
||||||
|
|
||||||
For each open trade, the bot will send you the following message.
|
For each open trade, the bot will send you the following message.
|
||||||
|
|
||||||
> **Trade ID:** `123`
|
> **Trade ID:** `123`
|
||||||
@ -54,6 +59,7 @@ For each open trade, the bot will send you the following message.
|
|||||||
> **Open Order:** `None`
|
> **Open Order:** `None`
|
||||||
|
|
||||||
## /status table
|
## /status table
|
||||||
|
|
||||||
Return the status of all open trades in a table format.
|
Return the status of all open trades in a table format.
|
||||||
```
|
```
|
||||||
ID Pair Since Profit
|
ID Pair Since Profit
|
||||||
@ -63,6 +69,7 @@ Return the status of all open trades in a table format.
|
|||||||
```
|
```
|
||||||
|
|
||||||
## /count
|
## /count
|
||||||
|
|
||||||
Return the number of trades used and available.
|
Return the number of trades used and available.
|
||||||
```
|
```
|
||||||
current max
|
current max
|
||||||
@ -71,6 +78,7 @@ current max
|
|||||||
```
|
```
|
||||||
|
|
||||||
## /profit
|
## /profit
|
||||||
|
|
||||||
Return a summary of your profit/loss and performance.
|
Return a summary of your profit/loss and performance.
|
||||||
|
|
||||||
> **ROI:** Close trades
|
> **ROI:** Close trades
|
||||||
@ -90,7 +98,14 @@ Return a summary of your profit/loss and performance.
|
|||||||
|
|
||||||
> **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)`
|
> **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)`
|
||||||
|
|
||||||
|
## /forcebuy <pair>
|
||||||
|
|
||||||
|
> **BITTREX**: Buying ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`)
|
||||||
|
|
||||||
|
Note that for this to work, `forcebuy_enable` needs to be set to true.
|
||||||
|
|
||||||
## /performance
|
## /performance
|
||||||
|
|
||||||
Return the performance of each crypto-currency the bot has sold.
|
Return the performance of each crypto-currency the bot has sold.
|
||||||
> Performance:
|
> Performance:
|
||||||
> 1. `RCN/BTC 57.77%`
|
> 1. `RCN/BTC 57.77%`
|
||||||
@ -101,6 +116,7 @@ Return the performance of each crypto-currency the bot has sold.
|
|||||||
> ...
|
> ...
|
||||||
|
|
||||||
## /balance
|
## /balance
|
||||||
|
|
||||||
Return the balance of all crypto-currency your have on the exchange.
|
Return the balance of all crypto-currency your have on the exchange.
|
||||||
|
|
||||||
> **Currency:** BTC
|
> **Currency:** BTC
|
||||||
@ -114,6 +130,7 @@ Return the balance of all crypto-currency your have on the exchange.
|
|||||||
> **Pending:** 0.0
|
> **Pending:** 0.0
|
||||||
|
|
||||||
## /daily <n>
|
## /daily <n>
|
||||||
|
|
||||||
Per default `/daily` will return the 7 last days.
|
Per default `/daily` will return the 7 last days.
|
||||||
The example below if for `/daily 3`:
|
The example below if for `/daily 3`:
|
||||||
|
|
||||||
@ -127,5 +144,6 @@ Day Profit BTC Profit USD
|
|||||||
```
|
```
|
||||||
|
|
||||||
## /version
|
## /version
|
||||||
|
|
||||||
> **Version:** `0.14.3`
|
> **Version:** `0.14.3`
|
||||||
|
|
||||||
|
@ -127,6 +127,9 @@ class Configuration(object):
|
|||||||
config['db_url'] = constants.DEFAULT_DB_PROD_URL
|
config['db_url'] = constants.DEFAULT_DB_PROD_URL
|
||||||
logger.info('Dry run is disabled')
|
logger.info('Dry run is disabled')
|
||||||
|
|
||||||
|
if config.get('forcebuy_enable', False):
|
||||||
|
logger.warning('`forcebuy` RPC message enabled.')
|
||||||
|
|
||||||
logger.info(f'Using DB: "{config["db_url"]}"')
|
logger.info(f'Using DB: "{config["db_url"]}"')
|
||||||
|
|
||||||
# Check if the exchange set by the user is supported
|
# Check if the exchange set by the user is supported
|
||||||
|
@ -131,6 +131,7 @@ CONF_SCHEMA = {
|
|||||||
},
|
},
|
||||||
'db_url': {'type': 'string'},
|
'db_url': {'type': 'string'},
|
||||||
'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
|
'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
|
||||||
|
'forcebuy_enable': {'type': 'boolean'},
|
||||||
'internals': {
|
'internals': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -451,7 +451,7 @@ class FreqtradeBot(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def execute_buy(self, pair: str, stake_amount: float) -> bool:
|
def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = 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
|
||||||
@ -462,6 +462,9 @@ class FreqtradeBot(object):
|
|||||||
stake_currency = self.config['stake_currency']
|
stake_currency = self.config['stake_currency']
|
||||||
fiat_currency = self.config.get('fiat_display_currency', None)
|
fiat_currency = self.config.get('fiat_display_currency', None)
|
||||||
|
|
||||||
|
if price:
|
||||||
|
buy_limit = price
|
||||||
|
else:
|
||||||
# Calculate amount
|
# Calculate amount
|
||||||
buy_limit = self.get_target_bid(pair, self.exchange.get_ticker(pair))
|
buy_limit = self.get_target_bid(pair, self.exchange.get_ticker(pair))
|
||||||
|
|
||||||
|
@ -385,6 +385,40 @@ class RPC(object):
|
|||||||
_exec_forcesell(trade)
|
_exec_forcesell(trade)
|
||||||
Trade.session.flush()
|
Trade.session.flush()
|
||||||
|
|
||||||
|
def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]:
|
||||||
|
"""
|
||||||
|
Handler for forcebuy <asset> <price>
|
||||||
|
Buys a pair trade at the given or current price
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self._freqtrade.config.get('forcebuy_enable', False):
|
||||||
|
raise RPCException('Forcebuy not enabled.')
|
||||||
|
|
||||||
|
if self._freqtrade.state != State.RUNNING:
|
||||||
|
raise RPCException('trader is not running')
|
||||||
|
|
||||||
|
# Check pair is in stake currency
|
||||||
|
stake_currency = self._freqtrade.config.get('stake_currency')
|
||||||
|
if not pair.endswith(stake_currency):
|
||||||
|
raise RPCException(
|
||||||
|
f'Wrong pair selected. Please pairs with stake {stake_currency} pairs only')
|
||||||
|
# check if valid pair
|
||||||
|
|
||||||
|
# check if pair already has an open pair
|
||||||
|
trade = Trade.query.filter(Trade.is_open.is_(True)).filter(Trade.pair.is_(pair)).first()
|
||||||
|
if trade:
|
||||||
|
raise RPCException(f'position for {pair} already open - id: {trade.id}')
|
||||||
|
|
||||||
|
# gen stake amount
|
||||||
|
stakeamount = self._freqtrade._get_trade_stake_amount()
|
||||||
|
|
||||||
|
# execute buy
|
||||||
|
if self._freqtrade.execute_buy(pair, stakeamount, price):
|
||||||
|
trade = Trade.query.filter(Trade.is_open.is_(True)).filter(Trade.pair.is_(pair)).first()
|
||||||
|
return trade
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def _rpc_performance(self) -> List[Dict]:
|
def _rpc_performance(self) -> List[Dict]:
|
||||||
"""
|
"""
|
||||||
Handler for performance.
|
Handler for performance.
|
||||||
|
@ -86,6 +86,7 @@ class Telegram(RPC):
|
|||||||
CommandHandler('start', self._start),
|
CommandHandler('start', self._start),
|
||||||
CommandHandler('stop', self._stop),
|
CommandHandler('stop', self._stop),
|
||||||
CommandHandler('forcesell', self._forcesell),
|
CommandHandler('forcesell', self._forcesell),
|
||||||
|
CommandHandler('forcebuy', self._forcebuy),
|
||||||
CommandHandler('performance', self._performance),
|
CommandHandler('performance', self._performance),
|
||||||
CommandHandler('daily', self._daily),
|
CommandHandler('daily', self._daily),
|
||||||
CommandHandler('count', self._count),
|
CommandHandler('count', self._count),
|
||||||
@ -375,6 +376,24 @@ class Telegram(RPC):
|
|||||||
except RPCException as e:
|
except RPCException as e:
|
||||||
self._send_msg(str(e), bot=bot)
|
self._send_msg(str(e), bot=bot)
|
||||||
|
|
||||||
|
@authorized_only
|
||||||
|
def _forcebuy(self, bot: Bot, update: Update) -> None:
|
||||||
|
"""
|
||||||
|
Handler for /forcebuy <asset> <price>.
|
||||||
|
Buys a pair trade at the given or current price
|
||||||
|
:param bot: telegram bot
|
||||||
|
:param update: message update
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
message = update.message.text.replace('/forcebuy', '').strip().split()
|
||||||
|
pair = message[0]
|
||||||
|
price = float(message[1]) if len(message) > 1 else None
|
||||||
|
try:
|
||||||
|
self._rpc_forcebuy(pair, price)
|
||||||
|
except RPCException as e:
|
||||||
|
self._send_msg(str(e), bot=bot)
|
||||||
|
|
||||||
@authorized_only
|
@authorized_only
|
||||||
def _performance(self, bot: Bot, update: Update) -> None:
|
def _performance(self, bot: Bot, update: Update) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -569,3 +569,79 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||||||
trades = rpc._rpc_count()
|
trades = rpc._rpc_count()
|
||||||
nb_trades = len(trades)
|
nb_trades = len(trades)
|
||||||
assert nb_trades == 1
|
assert nb_trades == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpcforcebuy(mocker, default_conf, ticker, fee, markets, limit_buy_order) -> None:
|
||||||
|
default_conf['forcebuy_enable'] = True
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
get_balances=MagicMock(return_value=ticker),
|
||||||
|
get_ticker=ticker,
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets,
|
||||||
|
buy=buy_mm
|
||||||
|
)
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
pair = 'ETH/BTC'
|
||||||
|
trade = rpc._rpc_forcebuy(pair, None)
|
||||||
|
assert isinstance(trade, Trade)
|
||||||
|
assert trade.pair == pair
|
||||||
|
assert trade.open_rate == ticker()['ask']
|
||||||
|
|
||||||
|
# Test buy duplicate
|
||||||
|
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
||||||
|
rpc._rpc_forcebuy(pair, 0.0001)
|
||||||
|
pair = 'XRP/BTC'
|
||||||
|
trade = rpc._rpc_forcebuy(pair, 0.0001)
|
||||||
|
assert isinstance(trade, Trade)
|
||||||
|
assert trade.pair == pair
|
||||||
|
assert trade.open_rate == 0.0001
|
||||||
|
|
||||||
|
# Test buy pair not with stakes
|
||||||
|
with pytest.raises(RPCException, match=r'Wrong pair selected. Please pairs with stake.*'):
|
||||||
|
rpc._rpc_forcebuy('XRP/ETH', 0.0001)
|
||||||
|
pair = 'XRP/BTC'
|
||||||
|
|
||||||
|
# Test not buying
|
||||||
|
default_conf['stake_amount'] = 0.0000001
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
pair = 'TKN/BTC'
|
||||||
|
trade = rpc._rpc_forcebuy(pair, None)
|
||||||
|
assert trade is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpcforcebuy_stopped(mocker, default_conf) -> None:
|
||||||
|
default_conf['forcebuy_enable'] = True
|
||||||
|
default_conf['initial_state'] = 'stopped'
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
pair = 'ETH/BTC'
|
||||||
|
with pytest.raises(RPCException, match=r'trader is not running'):
|
||||||
|
rpc._rpc_forcebuy(pair, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_rpcforcebuy_disabled(mocker, default_conf) -> None:
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
rpc = RPC(freqtradebot)
|
||||||
|
pair = 'ETH/BTC'
|
||||||
|
with pytest.raises(RPCException, match=r'Forcebuy not enabled.'):
|
||||||
|
rpc._rpc_forcebuy(pair, None)
|
||||||
|
@ -71,8 +71,8 @@ def test_init(default_conf, mocker, caplog) -> None:
|
|||||||
assert start_polling.start_polling.call_count == 1
|
assert start_polling.start_polling.call_count == 1
|
||||||
|
|
||||||
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
|
message_str = "rpc.telegram is listening for following commands: [['status'], ['profit'], " \
|
||||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['performance'], ['daily'], " \
|
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], " \
|
||||||
"['count'], ['reload_conf'], ['help'], ['version']]"
|
"['performance'], ['daily'], ['count'], ['reload_conf'], ['help'], ['version']]"
|
||||||
|
|
||||||
assert log_has(message_str, caplog.record_tuples)
|
assert log_has(message_str, caplog.record_tuples)
|
||||||
|
|
||||||
@ -868,6 +868,63 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
|
assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_forcebuy_handle(default_conf, update, markets, mocker) -> None:
|
||||||
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
|
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
_load_markets=MagicMock(return_value={}),
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
fbuy_mock = MagicMock(return_value=None)
|
||||||
|
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||||
|
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
|
update.message.text = '/forcebuy ETH/BTC'
|
||||||
|
telegram._forcebuy(bot=MagicMock(), update=update)
|
||||||
|
|
||||||
|
assert fbuy_mock.call_count == 1
|
||||||
|
assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
|
||||||
|
assert fbuy_mock.call_args_list[0][0][1] is None
|
||||||
|
|
||||||
|
# Reset and retry with specified price
|
||||||
|
fbuy_mock = MagicMock(return_value=None)
|
||||||
|
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||||
|
update.message.text = '/forcebuy ETH/BTC 0.055'
|
||||||
|
telegram._forcebuy(bot=MagicMock(), update=update)
|
||||||
|
|
||||||
|
assert fbuy_mock.call_count == 1
|
||||||
|
assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
|
||||||
|
assert isinstance(fbuy_mock.call_args_list[0][0][1], float)
|
||||||
|
assert fbuy_mock.call_args_list[0][0][1] == 0.055
|
||||||
|
|
||||||
|
|
||||||
|
def test_forcebuy_handle_exception(default_conf, update, markets, mocker) -> None:
|
||||||
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
|
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
|
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram._send_msg', MagicMock())
|
||||||
|
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
_load_markets=MagicMock(return_value={}),
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
|
update.message.text = '/forcebuy ETH/Nonepair'
|
||||||
|
telegram._forcebuy(bot=MagicMock(), update=update)
|
||||||
|
|
||||||
|
assert rpc_mock.call_count == 1
|
||||||
|
assert rpc_mock.call_args_list[0][0][0] == 'Forcebuy not enabled.'
|
||||||
|
|
||||||
|
|
||||||
def test_performance_handle(default_conf, update, ticker, fee,
|
def test_performance_handle(default_conf, update, ticker, fee,
|
||||||
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
limit_buy_order, limit_sell_order, markets, mocker) -> None:
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
|
@ -455,5 +455,19 @@ def test_set_loggers() -> None:
|
|||||||
assert logging.getLogger('telegram').level is logging.INFO
|
assert logging.getLogger('telegram').level is logging.INFO
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_config_warn_forcebuy(default_conf, mocker, caplog) -> None:
|
||||||
|
default_conf['forcebuy_enable'] = True
|
||||||
|
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||||
|
read_data=json.dumps(default_conf)
|
||||||
|
))
|
||||||
|
|
||||||
|
args = Arguments([], '').get_parsed_arg()
|
||||||
|
configuration = Configuration(args)
|
||||||
|
validated_conf = configuration.load_config()
|
||||||
|
|
||||||
|
assert validated_conf.get('forcebuy_enable')
|
||||||
|
assert log_has('`forcebuy` RPC message enabled.', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_default_conf(default_conf) -> None:
|
def test_validate_default_conf(default_conf) -> None:
|
||||||
validate(default_conf, constants.CONF_SCHEMA)
|
validate(default_conf, constants.CONF_SCHEMA)
|
||||||
|
@ -831,6 +831,54 @@ def test_balance_bigger_last_ask(mocker, default_conf) -> None:
|
|||||||
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 5, 'last': 10}) == 5
|
assert freqtrade.get_target_bid('ETH/BTC', {'ask': 5, 'last': 10}) == 5
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_buy(mocker, default_conf, fee, markets, limit_buy_order) -> None:
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_exchange(mocker)
|
||||||
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
stake_amount = 2
|
||||||
|
bid = 0.11
|
||||||
|
get_bid = MagicMock(return_value=bid)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.freqtradebot.FreqtradeBot',
|
||||||
|
get_target_bid=get_bid,
|
||||||
|
_get_min_pair_stake_amount=MagicMock(return_value=1)
|
||||||
|
)
|
||||||
|
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
get_ticker=MagicMock(return_value={
|
||||||
|
'bid': 0.00001172,
|
||||||
|
'ask': 0.00001173,
|
||||||
|
'last': 0.00001172
|
||||||
|
}),
|
||||||
|
buy=buy_mm,
|
||||||
|
get_fee=fee,
|
||||||
|
get_markets=markets
|
||||||
|
)
|
||||||
|
pair = 'ETH/BTC'
|
||||||
|
print(buy_mm.call_args_list)
|
||||||
|
|
||||||
|
assert freqtrade.execute_buy(pair, stake_amount)
|
||||||
|
assert get_bid.call_count == 1
|
||||||
|
assert buy_mm.call_count == 1
|
||||||
|
call_args = buy_mm.call_args_list[0][0]
|
||||||
|
assert call_args[0] == pair
|
||||||
|
assert call_args[1] == bid
|
||||||
|
assert call_args[2] == stake_amount / bid
|
||||||
|
|
||||||
|
# Test calling with price
|
||||||
|
fix_price = 0.06
|
||||||
|
assert freqtrade.execute_buy(pair, stake_amount, fix_price)
|
||||||
|
# Make sure get_target_bid wasn't called again
|
||||||
|
assert get_bid.call_count == 1
|
||||||
|
|
||||||
|
assert buy_mm.call_count == 2
|
||||||
|
call_args = buy_mm.call_args_list[1][0]
|
||||||
|
assert call_args[0] == pair
|
||||||
|
assert call_args[1] == fix_price
|
||||||
|
assert call_args[2] == stake_amount / fix_price
|
||||||
|
|
||||||
|
|
||||||
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
|
def test_process_maybe_execute_buy(mocker, default_conf) -> None:
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
ccxt==1.17.455
|
ccxt==1.17.464
|
||||||
SQLAlchemy==1.2.13
|
SQLAlchemy==1.2.13
|
||||||
python-telegram-bot==11.1.0
|
python-telegram-bot==11.1.0
|
||||||
arrow==0.12.1
|
arrow==0.12.1
|
||||||
cachetools==2.1.0
|
cachetools==2.1.0
|
||||||
requests==2.20.0
|
requests==2.20.0
|
||||||
urllib3==1.24
|
urllib3==1.24.1
|
||||||
wrapt==1.10.11
|
wrapt==1.10.11
|
||||||
pandas==0.23.4
|
pandas==0.23.4
|
||||||
scikit-learn==0.20.0
|
scikit-learn==0.20.0
|
||||||
|
Loading…
Reference in New Issue
Block a user