Merge branch 'feat/objectify-ccxt' into feature/catch-exchange-errors

This commit is contained in:
gcarq 2018-05-02 20:03:13 +02:00
commit a76ed88496
8 changed files with 404 additions and 33 deletions

View File

@ -3,6 +3,7 @@
import logging import logging
from random import randint from random import randint
from typing import List, Dict, Any, Optional from typing import List, Dict, Any, Optional
from datetime import datetime
import ccxt import ccxt
import arrow import arrow
@ -134,7 +135,8 @@ def buy(pair: str, rate: float, amount: float) -> Dict:
'side': 'buy', 'side': 'buy',
'remaining': 0.0, 'remaining': 0.0,
'datetime': arrow.utcnow().isoformat(), 'datetime': arrow.utcnow().isoformat(),
'status': 'closed' 'status': 'closed',
'fee': None
} }
return {'id': order_id} return {'id': order_id}
@ -324,6 +326,25 @@ def get_order(order_id: str, pair: str) -> Dict:
@retrier @retrier
def get_trades_for_order(order_id: str, pair: str, since: datetime) -> List:
if _CONF['dry_run']:
return []
if not exchange_has('fetchMyTrades'):
return []
try:
my_trades = _API.fetch_my_trades(pair, since.timestamp())
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
return matched_trades
except ccxt.NetworkError as e:
raise TemporaryError(
'Could not get trades due to networking error. Message: {}'.format(e)
)
except ccxt.BaseError as e:
raise OperationalException(e)
def get_pair_detail_url(pair: str) -> str: def get_pair_detail_url(pair: str) -> str:
try: try:
url_base = _API.urls.get('www') url_base = _API.urls.get('www')
@ -371,3 +392,13 @@ def get_fee(symbol='ETH/BTC', type='', side='', amount=1,
e.__class__.__name__, e)) e.__class__.__name__, e))
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) raise OperationalException(e)
def get_amount_lots(pair: str, amount: float) -> float:
"""
get buyable amount rounding, ..
"""
# validate that markets are loaded before trying to get fee
if not _API.markets:
_API.load_markets()
return _API.amount_to_lots(pair, amount)

View File

@ -287,7 +287,7 @@ class FreqtradeBot(object):
if not whitelist: if not whitelist:
raise DependencyException('No currency pairs in whitelist') raise DependencyException('No currency pairs in whitelist')
# Pick pair based on StochRSI buy signals # Pick pair based on buy signals
for _pair in whitelist: for _pair in whitelist:
(buy, sell) = self.analyze.get_signal(_pair, interval) (buy, sell) = self.analyze.get_signal(_pair, interval)
if buy and not sell: if buy and not sell:
@ -323,11 +323,13 @@ class FreqtradeBot(object):
) )
) )
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = exchange.get_fee(symbol=pair, taker_or_maker='maker')
trade = Trade( trade = Trade(
pair=pair, pair=pair,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=amount, amount=amount,
fee=exchange.get_fee(taker_or_maker='maker'), fee_open=fee,
fee_close=fee,
open_rate=buy_limit, open_rate=buy_limit,
open_date=datetime.utcnow(), open_date=datetime.utcnow(),
exchange=exchange.get_id(), exchange=exchange.get_id(),
@ -363,7 +365,19 @@ class FreqtradeBot(object):
if trade.open_order_id: if trade.open_order_id:
# Update trade with order values # Update trade with order values
logger.info('Found open order for %s', trade) logger.info('Found open order for %s', trade)
trade.update(exchange.get_order(trade.open_order_id, trade.pair)) order = exchange.get_order(trade.open_order_id, trade.pair)
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order)
if order['amount'] != new_amount:
order['amount'] = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
except OperationalException as exception:
logger.warning("could not update trade amount: %s", exception)
trade.update(order)
if trade.is_open and trade.open_order_id is None: if trade.is_open and trade.open_order_id is None:
# Check if we can sell our current pair # Check if we can sell our current pair
@ -372,6 +386,48 @@ class FreqtradeBot(object):
logger.warning('Unable to sell trade: %s', exception) logger.warning('Unable to sell trade: %s', exception)
return False return False
def get_real_amount(self, trade: Trade, order: Dict) -> float:
"""
Get real amount for the trade
Necessary for exchanges which charge fees in base currency (e.g. binance)
"""
order_amount = order['amount']
# Only run for closed orders
if trade.fee_open == 0 or order['status'] == 'open':
return order_amount
# use fee from order-dict if possible
if 'fee' in order and order['fee']:
if trade.pair.startswith(order['fee']['currency']):
new_amount = order_amount - order['fee']['cost']
logger.info("Applying fee on amount for %s (from %s to %s) from Order",
trade, order['amount'], new_amount)
return new_amount
# Fallback to Trades
trades = exchange.get_trades_for_order(trade.open_order_id, trade.pair, trade.open_date)
if len(trades) == 0:
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
return order_amount
amount = 0
fee_abs = 0
for exectrade in trades:
amount += exectrade['amount']
if "fee" in exectrade:
# only applies if fee is in quote currency!
if trade.pair.startswith(exectrade['fee']['currency']):
fee_abs += exectrade['fee']['cost']
if amount != order_amount:
logger.warning("amount {} does not match amount {}".format(amount, trade.amount))
raise OperationalException("Half bought? Amounts don't match")
real_amount = amount - fee_abs
if fee_abs != 0:
logger.info("Applying fee on amount for {} (from {} to {}) from Trades".format(
trade, order['amount'], real_amount))
return real_amount
def handle_trade(self, trade: Trade) -> bool: def handle_trade(self, trade: Trade) -> bool:
""" """
Sells the current pair if the threshold is reached and updates the trade record. Sells the current pair if the threshold is reached and updates the trade record.

View File

@ -113,12 +113,14 @@ class Backtesting(object):
stake_amount = args['stake_amount'] stake_amount = args['stake_amount']
max_open_trades = args.get('max_open_trades', 0) max_open_trades = args.get('max_open_trades', 0)
fee = exchange.get_fee()
trade = Trade( trade = Trade(
open_rate=buy_row.close, open_rate=buy_row.close,
open_date=buy_row.date, open_date=buy_row.date,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=stake_amount / buy_row.open, amount=stake_amount / buy_row.open,
fee=exchange.get_fee() fee_open=fee,
fee_close=fee
) )
# calculate win/lose forwards from buy point # calculate win/lose forwards from buy point

View File

@ -85,7 +85,8 @@ class Trade(_DECL_BASE):
exchange = Column(String, nullable=False) exchange = Column(String, nullable=False)
pair = Column(String, nullable=False) pair = Column(String, nullable=False)
is_open = Column(Boolean, nullable=False, default=True) is_open = Column(Boolean, nullable=False, default=True)
fee = Column(Float, nullable=False, default=0.0) fee_open = Column(Float, nullable=False, default=0.0)
fee_close = Column(Float, nullable=False, default=0.0)
open_rate = Column(Float) open_rate = Column(Float)
close_rate = Column(Float) close_rate = Column(Float)
close_profit = Column(Float) close_profit = Column(Float)
@ -156,7 +157,7 @@ class Trade(_DECL_BASE):
getcontext().prec = 8 getcontext().prec = 8
buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
fees = buy_trade * Decimal(fee or self.fee) fees = buy_trade * Decimal(fee or self.fee_open)
return float(buy_trade + fees) return float(buy_trade + fees)
def calc_close_trade_price( def calc_close_trade_price(
@ -177,7 +178,7 @@ class Trade(_DECL_BASE):
return 0.0 return 0.0
sell_trade = (Decimal(self.amount) * Decimal(rate or self.close_rate)) sell_trade = (Decimal(self.amount) * Decimal(rate or self.close_rate))
fees = sell_trade * Decimal(fee or self.fee) fees = sell_trade * Decimal(fee or self.fee_close)
return float(sell_trade - fees) return float(sell_trade - fees)
def calc_profit( def calc_profit(
@ -195,7 +196,7 @@ class Trade(_DECL_BASE):
open_trade_price = self.calc_open_trade_price() open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price( close_trade_price = self.calc_close_trade_price(
rate=(rate or self.close_rate), rate=(rate or self.close_rate),
fee=(fee or self.fee) fee=(fee or self.fee_close)
) )
return float("{0:.8f}".format(close_trade_price - open_trade_price)) return float("{0:.8f}".format(close_trade_price - open_trade_price))
@ -215,7 +216,7 @@ class Trade(_DECL_BASE):
open_trade_price = self.calc_open_trade_price() open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price( close_trade_price = self.calc_close_trade_price(
rate=(rate or self.close_rate), rate=(rate or self.close_rate),
fee=(fee or self.fee) fee=(fee or self.fee_close)
) )
return float("{0:.8f}".format((close_trade_price / open_trade_price) - 1)) return float("{0:.8f}".format((close_trade_price / open_trade_price) - 1))

View File

@ -208,7 +208,7 @@ def markets_empty():
return MagicMock(return_value=[]) return MagicMock(return_value=[])
@pytest.fixture @pytest.fixture(scope='function')
def limit_buy_order(): def limit_buy_order():
return { return {
'id': 'mocked_limit_buy', 'id': 'mocked_limit_buy',
@ -499,3 +499,90 @@ def result():
# that inserts a trade of some type and open-status # that inserts a trade of some type and open-status
# return the open-order-id # return the open-order-id
# See tests in rpc/main that could use this # See tests in rpc/main that could use this
@pytest.fixture(scope="function")
def trades_for_order():
return [{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 8.0,
'fee': {'cost': 0.008, 'currency': 'LTC'}}]
@pytest.fixture(scope="function")
def trades_for_order2():
return [{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 4.0,
'fee': {'cost': 0.004, 'currency': 'LTC'}},
{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 4.0,
'fee': {'cost': 0.004, 'currency': 'LTC'}}]
@pytest.fixture
def buy_order_fee():
return {
'id': 'mocked_limit_buy_old',
'type': 'limit',
'side': 'buy',
'pair': 'mocked',
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
'price': 0.245441,
'amount': 8.0,
'remaining': 90.99181073,
'status': 'closed',
'fee': None
}

View File

@ -4,14 +4,15 @@ import logging
from copy import deepcopy from copy import deepcopy
from random import randint from random import randint
from unittest.mock import MagicMock, PropertyMock from unittest.mock import MagicMock, PropertyMock
import ccxt
import ccxt
import pytest import pytest
from freqtrade import OperationalException, DependencyException, TemporaryError
from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get_balances, \
get_ticker, get_ticker_history, cancel_order, get_name, get_fee, get_id, get_pair_detail_url
import freqtrade.exchange as exchange import freqtrade.exchange as exchange
from freqtrade import OperationalException, DependencyException, TemporaryError
from freqtrade.exchange import (init, validate_pairs, buy, sell, get_balance, get_balances,
get_ticker, get_ticker_history, cancel_order, get_name, get_fee,
get_id, get_pair_detail_url, get_amount_lots)
from freqtrade.tests.conftest import log_has from freqtrade.tests.conftest import log_has
API_INIT = False API_INIT = False
@ -507,3 +508,10 @@ def test_get_fee(default_conf, mocker):
}) })
mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch('freqtrade.exchange._API', api_mock)
assert get_fee() == 0.025 assert get_fee() == 0.025
def test_get_amount_lots(default_conf, mocker):
api_mock = MagicMock()
api_mock.amount_to_lots = MagicMock(return_value=1.0)
mocker.patch('freqtrade.exchange._API', api_mock)
assert get_amount_lots('LTC/BTC', 1.54) == 1

View File

@ -568,18 +568,30 @@ def test_process_maybe_execute_buy_exception(mocker, default_conf, caplog) -> No
log_has('Unable to create trade:', caplog.record_tuples) log_has('Unable to create trade:', caplog.record_tuples)
def test_process_maybe_execute_sell(mocker, default_conf) -> None: def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None:
""" """
Test process_maybe_execute_sell() method Test process_maybe_execute_sell() method
""" """
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=1) mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=limit_buy_order)
mocker.patch('freqtrade.freqtradebot.exchange.get_trades_for_order', return_value=[])
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
return_value=limit_buy_order['amount'])
trade = MagicMock() trade = MagicMock()
trade.open_order_id = '123' trade.open_order_id = '123'
trade.open_fee = 0.001
assert not freqtrade.process_maybe_execute_sell(trade) assert not freqtrade.process_maybe_execute_sell(trade)
# Test amount not modified by fee-logic
assert not log_has('Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(
trade), caplog.record_tuples)
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
# test amount modified by fee-logic
assert not freqtrade.process_maybe_execute_sell(trade)
trade.is_open = True trade.is_open = True
trade.open_order_id = None trade.open_order_id = None
# Assert we call handle_trade() if trade is feasible for execution # Assert we call handle_trade() if trade is feasible for execution
@ -812,7 +824,8 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fe
exchange='bittrex', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee_open=0.0,
fee_close=0.0,
stake_amount=1, stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime, open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True is_open=True
@ -851,7 +864,8 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
exchange='bittrex', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee_open=0.0,
fee_close=0.0,
stake_amount=1, stake_amount=1,
open_date=arrow.utcnow().shift(hours=-5).datetime, open_date=arrow.utcnow().shift(hours=-5).datetime,
close_date=arrow.utcnow().shift(minutes=-601).datetime, close_date=arrow.utcnow().shift(minutes=-601).datetime,
@ -890,7 +904,8 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
exchange='bittrex', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee_open=0.0,
fee_close=0.0,
stake_amount=1, stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime, open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True is_open=True
@ -937,7 +952,8 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
exchange='bittrex', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee_open=0.0,
fee_close=0.0,
stake_amount=1, stake_amount=1,
open_date=arrow.utcnow().shift(minutes=-601).datetime, open_date=arrow.utcnow().shift(minutes=-601).datetime,
is_open=True is_open=True
@ -1299,3 +1315,161 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke
trade.update(limit_buy_order) trade.update(limit_buy_order)
patch_get_signal(mocker, value=(False, True)) patch_get_signal(mocker, value=(False, True))
assert freqtrade.handle_trade(trade) is True assert freqtrade.handle_trade(trade) is True
def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount - fee in quote currency
"""
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples)
def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
"""
Test get_real_amount - fee in quote currency
"""
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[])
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
amount = buy_order_fee['amount']
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found',
caplog.record_tuples)
def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount - fees in Stake currency
"""
trades_for_order[0]['fee']['currency'] = 'ETH'
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mocker):
"""
Test get_real_amount - Fees in BNB
"""
trades_for_order[0]['fee']['currency'] = 'BNB'
trades_for_order[0]['fee']['cost'] = 0.00094518
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
amount = sum(x['amount'] for x in trades_for_order)
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount does not change
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, caplog, mocker):
"""
Test get_real_amount with split trades (multiple trades for this order)
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order2)
amount = float(sum(x['amount'] for x in trades_for_order2))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992) from Trades',
caplog.record_tuples)
def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker):
"""
Test get_real_amount with split trades (multiple trades for this order)
"""
limit_buy_order = deepcopy(buy_order_fee)
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order)
amount = float(sum(x['amount'] for x in trades_for_order))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456"
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
# Amount is reduced by "fee"
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996) from Order',
caplog.record_tuples)

View File

@ -126,7 +126,8 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
assert trade.open_order_id is None assert trade.open_order_id is None
@ -154,7 +155,8 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
@ -177,7 +179,8 @@ def test_calc_close_trade_price_exception(limit_buy_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
@ -191,7 +194,8 @@ def test_update_open_order(limit_buy_order):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=1.00, stake_amount=1.00,
fee=0.1, fee_open=0.1,
fee_close=0.1,
exchange='bittrex', exchange='bittrex',
) )
@ -214,7 +218,8 @@ def test_update_invalid_order(limit_buy_order):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=1.00, stake_amount=1.00,
fee=0.1, fee_open=0.1,
fee_close=0.1,
exchange='bittrex', exchange='bittrex',
) )
limit_buy_order['type'] = 'invalid' limit_buy_order['type'] = 'invalid'
@ -227,7 +232,8 @@ def test_calc_open_trade_price(limit_buy_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'open_trade' trade.open_order_id = 'open_trade'
@ -245,7 +251,8 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'close_trade' trade.open_order_id = 'close_trade'
@ -267,7 +274,8 @@ def test_calc_profit(limit_buy_order, limit_sell_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' trade.open_order_id = 'profit_percent'
@ -298,7 +306,8 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee):
trade = Trade( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' trade.open_order_id = 'profit_percent'
@ -326,7 +335,8 @@ def test_clean_dry_run_db(default_conf, fee):
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_buy_12345' open_order_id='dry_run_buy_12345'
@ -337,7 +347,8 @@ def test_clean_dry_run_db(default_conf, fee):
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_sell_12345' open_order_id='dry_run_sell_12345'
@ -349,7 +360,8 @@ def test_clean_dry_run_db(default_conf, fee):
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=fee.return_value, fee_open=fee.return_value,
fee_close=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='prod_buy_12345' open_order_id='prod_buy_12345'