mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 18:23:55 +00:00
Merge branch 'develop' into trailing_only_offset
This commit is contained in:
commit
f1f311e456
|
@ -23,10 +23,13 @@ install:
|
||||||
- pip install -r requirements-dev.txt
|
- pip install -r requirements-dev.txt
|
||||||
- pip install -e .
|
- pip install -e .
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- stage: tests
|
- stage: tests
|
||||||
script:
|
script:
|
||||||
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
|
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
|
||||||
|
# Allow failure for coveralls
|
||||||
|
- coveralls || true
|
||||||
name: pytest
|
name: pytest
|
||||||
- script:
|
- script:
|
||||||
- cp config.json.example config.json
|
- cp config.json.example config.json
|
||||||
|
@ -47,9 +50,6 @@ jobs:
|
||||||
- build_helpers/publish_docker.sh
|
- build_helpers/publish_docker.sh
|
||||||
name: "Build and test and push docker image"
|
name: "Build and test and push docker image"
|
||||||
|
|
||||||
after_success:
|
|
||||||
- coveralls
|
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
slack:
|
slack:
|
||||||
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=
|
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=
|
||||||
|
|
|
@ -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_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).
|
| `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.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.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.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.
|
| `exchange.pair_whitelist` | [] | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.
|
||||||
|
|
|
@ -36,7 +36,7 @@ Complementary Loss Rate (*L*) is defined as
|
||||||
|
|
||||||
or, which is the same, as
|
or, which is the same, as
|
||||||
|
|
||||||
R = 1 – W
|
L = 1 – W
|
||||||
|
|
||||||
### Risk Reward Ratio
|
### 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:
|
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:
|
||||||
|
|
|
@ -44,6 +44,14 @@ CREATE TABLE trades (
|
||||||
open_date DATETIME NOT NULL,
|
open_date DATETIME NOT NULL,
|
||||||
close_date DATETIME,
|
close_date DATETIME,
|
||||||
open_order_id VARCHAR,
|
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),
|
PRIMARY KEY (id),
|
||||||
CHECK (is_open IN (0, 1))
|
CHECK (is_open IN (0, 1))
|
||||||
);
|
);
|
||||||
|
@ -55,38 +63,45 @@ CREATE TABLE trades (
|
||||||
SELECT * FROM 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
|
```sql
|
||||||
UPDATE trades
|
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>;
|
WHERE id=<trade_ID_to_update>;
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example:**
|
##### Example
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
UPDATE trades
|
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;
|
WHERE id=31;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Insert manually a new trade
|
## Insert manually a new trade
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
INSERT
|
INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date)
|
||||||
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>')
|
||||||
VALUES ('BITTREX', 'BTC_<COIN>', 1, 0.0025, 0.0025, <open_rate>, <stake_amount>, <amount>, '<datetime>')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example:**
|
##### Example:
|
||||||
|
|
||||||
```sql
|
```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
|
## Fix wrong fees in the table
|
||||||
If your DB was created before
|
If your DB was created before [PR#200](https://github.com/freqtrade/freqtrade/pull/200) was merged (before 12/23/17).
|
||||||
[PR#200](https://github.com/freqtrade/freqtrade/pull/200) was merged
|
|
||||||
(before 12/23/17).
|
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
UPDATE trades SET fee=0.0025 WHERE fee=0.005;
|
UPDATE trades SET fee=0.0025 WHERE fee=0.005;
|
||||||
|
|
|
@ -298,7 +298,7 @@ class Exchange(object):
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
"cost": amount * rate,
|
"cost": amount * rate,
|
||||||
'type': ordertype,
|
'type': ordertype,
|
||||||
'side': 'buy',
|
'side': side,
|
||||||
'remaining': amount,
|
'remaining': amount,
|
||||||
'datetime': arrow.utcnow().isoformat(),
|
'datetime': arrow.utcnow().isoformat(),
|
||||||
'status': "open",
|
'status': "open",
|
||||||
|
@ -352,7 +352,7 @@ class Exchange(object):
|
||||||
return dry_order
|
return dry_order
|
||||||
|
|
||||||
params = self._params.copy()
|
params = self._params.copy()
|
||||||
if time_in_force != 'gtc':
|
if time_in_force != 'gtc' and ordertype != 'market':
|
||||||
params.update({'timeInForce': time_in_force})
|
params.update({'timeInForce': time_in_force})
|
||||||
|
|
||||||
return self.create_order(pair, ordertype, 'buy', amount, rate, params)
|
return self.create_order(pair, ordertype, 'buy', amount, rate, params)
|
||||||
|
@ -365,7 +365,7 @@ class Exchange(object):
|
||||||
return dry_order
|
return dry_order
|
||||||
|
|
||||||
params = self._params.copy()
|
params = self._params.copy()
|
||||||
if time_in_force != 'gtc':
|
if time_in_force != 'gtc' and ordertype != 'market':
|
||||||
params.update({'timeInForce': time_in_force})
|
params.update({'timeInForce': time_in_force})
|
||||||
|
|
||||||
return self.create_order(pair, ordertype, 'sell', amount, rate, params)
|
return self.create_order(pair, ordertype, 'sell', amount, rate, params)
|
||||||
|
|
|
@ -61,6 +61,8 @@ class RPCManager(object):
|
||||||
stake_currency = config['stake_currency']
|
stake_currency = config['stake_currency']
|
||||||
stake_amount = config['stake_amount']
|
stake_amount = config['stake_amount']
|
||||||
minimal_roi = config['minimal_roi']
|
minimal_roi = config['minimal_roi']
|
||||||
|
stoploss = config['stoploss']
|
||||||
|
trailing_stop = config['trailing_stop']
|
||||||
ticker_interval = config['ticker_interval']
|
ticker_interval = config['ticker_interval']
|
||||||
exchange_name = config['exchange']['name']
|
exchange_name = config['exchange']['name']
|
||||||
strategy_name = config.get('strategy', '')
|
strategy_name = config.get('strategy', '')
|
||||||
|
@ -69,6 +71,7 @@ class RPCManager(object):
|
||||||
'status': f'*Exchange:* `{exchange_name}`\n'
|
'status': f'*Exchange:* `{exchange_name}`\n'
|
||||||
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
|
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
|
||||||
f'*Minimum ROI:* `{minimal_roi}`\n'
|
f'*Minimum ROI:* `{minimal_roi}`\n'
|
||||||
|
f'*{"Trailing " if trailing_stop else ""}Stoploss:* `{stoploss}`\n'
|
||||||
f'*Ticker Interval:* `{ticker_interval}`\n'
|
f'*Ticker Interval:* `{ticker_interval}`\n'
|
||||||
f'*Strategy:* `{strategy_name}`'
|
f'*Strategy:* `{strategy_name}`'
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,7 @@ import copy
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from random import randint
|
from random import randint
|
||||||
from unittest.mock import Mock, MagicMock, PropertyMock
|
from unittest.mock import MagicMock, Mock, PropertyMock
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
import ccxt
|
import ccxt
|
||||||
|
@ -12,11 +12,10 @@ import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import DependencyException, OperationalException, TemporaryError
|
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.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.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!!
|
# Make sure to always keep one exchange here which is NOT subclassed!!
|
||||||
EXCHANGES = ['bittrex', 'binance', 'kraken', ]
|
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)
|
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
|
||||||
assert 'id' in order
|
assert 'id' in order
|
||||||
assert f'dry_run_{side}_' in order["id"]
|
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", [
|
@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)
|
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()
|
api_mock = MagicMock()
|
||||||
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
||||||
order_type = 'market'
|
|
||||||
time_in_force = 'ioc'
|
|
||||||
api_mock.create_order = MagicMock(return_value={
|
api_mock.create_order = MagicMock(return_value={
|
||||||
'id': order_id,
|
'id': order_id,
|
||||||
'info': {
|
'info': {
|
||||||
|
@ -602,7 +603,27 @@ def test_buy_considers_time_in_force(default_conf, mocker):
|
||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
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)
|
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,
|
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
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][2] == 'buy'
|
||||||
assert api_mock.create_order.call_args[0][3] == 1
|
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][4] is None
|
||||||
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc'}
|
# Market orders should not send timeInForce!!
|
||||||
|
assert "timeInForce" not in api_mock.create_order.call_args[0][5]
|
||||||
|
|
||||||
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'}
|
|
||||||
|
|
||||||
|
|
||||||
def test_sell_dry_run(default_conf, mocker):
|
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)
|
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):
|
def test_get_balance_dry_run(default_conf, mocker):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
|
|
||||||
|
|
67
freqtrade/tests/exchange/test_kraken.py
Normal file
67
freqtrade/tests/exchange/test_kraken.py
Normal 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'}
|
|
@ -1,5 +1,5 @@
|
||||||
# Include all requirements to run the bot.
|
# Include all requirements to run the bot.
|
||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
|
|
||||||
plotly==3.6.1
|
plotly==3.7.0
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
ccxt==1.18.345
|
ccxt==1.18.357
|
||||||
SQLAlchemy==1.3.0
|
SQLAlchemy==1.3.1
|
||||||
python-telegram-bot==11.1.0
|
python-telegram-bot==11.1.0
|
||||||
arrow==0.13.1
|
arrow==0.13.1
|
||||||
cachetools==3.1.0
|
cachetools==3.1.0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user