Merge branch 'develop' into trailing_only_offset

This commit is contained in:
misagh 2019-03-12 12:32:10 +01:00
commit f1f311e456
10 changed files with 188 additions and 92 deletions

View File

@ -23,10 +23,13 @@ install:
- pip install -r requirements-dev.txt
- pip install -e .
jobs:
include:
- stage: tests
script:
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
# Allow failure for coveralls
- coveralls || true
name: pytest
- script:
- cp config.json.example config.json
@ -47,9 +50,6 @@ jobs:
- build_helpers/publish_docker.sh
name: "Build and test and push docker image"
after_success:
- coveralls
notifications:
slack:
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=

View File

@ -39,6 +39,7 @@ Mandatory Parameters are marked as **Required**.
| `order_types` | None | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types). [Strategy Override](#parameters-in-strategy).
| `order_time_in_force` | None | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force). [Strategy Override](#parameters-in-strategy).
| `exchange.name` | bittrex | **Required.** Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
| `exchange.sandbox` | false | Use the 'sandbox' version of the exchange, where the exchange provides a sandbox for risk-free integration. See [here](sandbox-testing.md) in more details.
| `exchange.key` | key | API key to use for the exchange. Only required when you are in production mode.
| `exchange.secret` | secret | API secret to use for the exchange. Only required when you are in production mode.
| `exchange.pair_whitelist` | [] | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.

View File

@ -36,7 +36,7 @@ Complementary Loss Rate (*L*) is defined as
or, which is the same, as
R = 1 W
L = 1 W
### Risk Reward Ratio
Risk Reward Ratio (*R*) is a formula used to measure the expected gains of a given investment against the risk of loss. It is basically what you potentially win divided by what you potentially lose:

View File

@ -44,6 +44,14 @@ CREATE TABLE trades (
open_date DATETIME NOT NULL,
close_date DATETIME,
open_order_id VARCHAR,
stop_loss FLOAT,
initial_stop_loss FLOAT,
stoploss_order_id VARCHAR,
stoploss_last_update DATETIME,
max_rate FLOAT,
sell_reason VARCHAR,
strategy VARCHAR,
ticker_interval INTEGER,
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);
@ -55,38 +63,45 @@ CREATE TABLE trades (
SELECT * FROM trades;
```
## Fix trade still open after a /forcesell
## Fix trade still open after a manual sell on the exchange
!!! Warning:
Manually selling on the exchange should not be done by default, since the bot does not detect this and will try to sell anyway.
/foresell <tradeid> should accomplish the same thing.
!!! Note:
This should not be necessary after /forcesell, as forcesell orders are closed automatically by the bot on the next iteration.
```sql
UPDATE trades
SET is_open=0, close_date=<close_date>, close_rate=<close_rate>, close_profit=close_rate/open_rate-1
SET is_open=0, close_date=<close_date>, close_rate=<close_rate>, close_profit=close_rate/open_rate-1, sell_reason=<sell_reason>
WHERE id=<trade_ID_to_update>;
```
**Example:**
##### Example
```sql
UPDATE trades
SET is_open=0, close_date='2017-12-20 03:08:45.103418', close_rate=0.19638016, close_profit=0.0496
SET is_open=0, close_date='2017-12-20 03:08:45.103418', close_rate=0.19638016, close_profit=0.0496, sell_reason='force_sell'
WHERE id=31;
```
## Insert manually a new trade
```sql
INSERT
INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date)
VALUES ('BITTREX', 'BTC_<COIN>', 1, 0.0025, 0.0025, <open_rate>, <stake_amount>, <amount>, '<datetime>')
INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date)
VALUES ('bittrex', 'ETH/BTC', 1, 0.0025, 0.0025, <open_rate>, <stake_amount>, <amount>, '<datetime>')
```
**Example:**
##### Example:
```sql
INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date) VALUES ('BITTREX', 'BTC_ETC', 1, 0.0025, 0.0025, 0.00258580, 0.002, 0.7715262081, '2017-11-28 12:44:24.000000')
INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date)
VALUES ('bittrex', 'ETH/BTC', 1, 0.0025, 0.0025, 0.00258580, 0.002, 0.7715262081, '2017-11-28 12:44:24.000000')
```
## Fix wrong fees in the table
If your DB was created before
[PR#200](https://github.com/freqtrade/freqtrade/pull/200) was merged
(before 12/23/17).
If your DB was created before [PR#200](https://github.com/freqtrade/freqtrade/pull/200) was merged (before 12/23/17).
```sql
UPDATE trades SET fee=0.0025 WHERE fee=0.005;

View File

@ -298,7 +298,7 @@ class Exchange(object):
'amount': amount,
"cost": amount * rate,
'type': ordertype,
'side': 'buy',
'side': side,
'remaining': amount,
'datetime': arrow.utcnow().isoformat(),
'status': "open",
@ -352,7 +352,7 @@ class Exchange(object):
return dry_order
params = self._params.copy()
if time_in_force != 'gtc':
if time_in_force != 'gtc' and ordertype != 'market':
params.update({'timeInForce': time_in_force})
return self.create_order(pair, ordertype, 'buy', amount, rate, params)
@ -365,7 +365,7 @@ class Exchange(object):
return dry_order
params = self._params.copy()
if time_in_force != 'gtc':
if time_in_force != 'gtc' and ordertype != 'market':
params.update({'timeInForce': time_in_force})
return self.create_order(pair, ordertype, 'sell', amount, rate, params)

View File

@ -61,6 +61,8 @@ class RPCManager(object):
stake_currency = config['stake_currency']
stake_amount = config['stake_amount']
minimal_roi = config['minimal_roi']
stoploss = config['stoploss']
trailing_stop = config['trailing_stop']
ticker_interval = config['ticker_interval']
exchange_name = config['exchange']['name']
strategy_name = config.get('strategy', '')
@ -69,6 +71,7 @@ class RPCManager(object):
'status': f'*Exchange:* `{exchange_name}`\n'
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
f'*Minimum ROI:* `{minimal_roi}`\n'
f'*{"Trailing " if trailing_stop else ""}Stoploss:* `{stoploss}`\n'
f'*Ticker Interval:* `{ticker_interval}`\n'
f'*Strategy:* `{strategy_name}`'
})

View File

@ -4,7 +4,7 @@ import copy
import logging
from datetime import datetime
from random import randint
from unittest.mock import Mock, MagicMock, PropertyMock
from unittest.mock import MagicMock, Mock, PropertyMock
import arrow
import ccxt
@ -12,11 +12,10 @@ import pytest
from pandas import DataFrame
from freqtrade import DependencyException, OperationalException, TemporaryError
from freqtrade.exchange import Exchange, Kraken, Binance
from freqtrade.exchange import Binance, Exchange, Kraken
from freqtrade.exchange.exchange import API_RETRY_COUNT
from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
# Make sure to always keep one exchange here which is NOT subclassed!!
EXCHANGES = ['bittrex', 'binance', 'kraken', ]
@ -470,6 +469,9 @@ def test_dry_run_order(default_conf, mocker, side, exchange_name):
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
assert 'id' in order
assert f'dry_run_{side}_' in order["id"]
assert order["side"] == side
assert order["type"] == "limit"
assert order["pair"] == "ETH/BTC"
@pytest.mark.parametrize("side", [
@ -588,11 +590,10 @@ def test_buy_prod(default_conf, mocker, exchange_name):
amount=1, rate=200, time_in_force=time_in_force)
def test_buy_considers_time_in_force(default_conf, mocker):
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
order_type = 'market'
time_in_force = 'ioc'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
@ -602,7 +603,27 @@ def test_buy_considers_time_in_force(default_conf, mocker):
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
order_type = 'limit'
time_in_force = 'ioc'
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200
assert "timeInForce" in api_mock.create_order.call_args[0][5]
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force
order_type = 'market'
time_in_force = 'ioc'
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
@ -615,68 +636,8 @@ def test_buy_considers_time_in_force(default_conf, mocker):
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc'}
def test_buy_kraken_trading_agreement(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
order_type = 'market'
time_in_force = 'ioc'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc',
'trading_agreement': 'agree'}
def test_sell_kraken_trading_agreement(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
order_type = 'market'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
assert api_mock.create_order.call_args[0][5] == {'trading_agreement': 'agree'}
# Market orders should not send timeInForce!!
assert "timeInForce" not in api_mock.create_order.call_args[0][5]
def test_sell_dry_run(default_conf, mocker):
@ -747,6 +708,55 @@ def test_sell_prod(default_conf, mocker, exchange_name):
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
@pytest.mark.parametrize("exchange_name", EXCHANGES)
def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
api_mock = MagicMock()
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
order_type = 'limit'
time_in_force = 'ioc'
order = exchange.sell(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200
assert "timeInForce" in api_mock.create_order.call_args[0][5]
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force
order_type = 'market'
time_in_force = 'ioc'
order = exchange.sell(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
# Market orders should not send timeInForce!!
assert "timeInForce" not in api_mock.create_order.call_args[0][5]
def test_get_balance_dry_run(default_conf, mocker):
default_conf['dry_run'] = True

View File

@ -0,0 +1,67 @@
# pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement
# pragma pylint: disable=protected-access
from random import randint
from unittest.mock import MagicMock
from freqtrade.tests.conftest import get_patched_exchange
def test_buy_kraken_trading_agreement(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
order_type = 'limit'
time_in_force = 'ioc'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
amount=1, rate=200, time_in_force=time_in_force)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] == 200
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc',
'trading_agreement': 'agree'}
def test_sell_kraken_trading_agreement(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
order_type = 'market'
api_mock.create_order = MagicMock(return_value={
'id': order_id,
'info': {
'foo': 'bar'
}
})
default_conf['dry_run'] = False
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
assert 'id' in order
assert 'info' in order
assert order['id'] == order_id
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell'
assert api_mock.create_order.call_args[0][3] == 1
assert api_mock.create_order.call_args[0][4] is None
assert api_mock.create_order.call_args[0][5] == {'trading_agreement': 'agree'}

View File

@ -1,5 +1,5 @@
# Include all requirements to run the bot.
-r requirements.txt
plotly==3.6.1
plotly==3.7.0

View File

@ -1,5 +1,5 @@
ccxt==1.18.345
SQLAlchemy==1.3.0
ccxt==1.18.357
SQLAlchemy==1.3.1
python-telegram-bot==11.1.0
arrow==0.13.1
cachetools==3.1.0