Merge branch 'release/0.10.0'

This commit is contained in:
gcarq 2017-09-28 19:17:01 +02:00
commit c20030783b
24 changed files with 214 additions and 132 deletions

View File

@ -1,28 +1,27 @@
sudo: false
os:
- linux
- linux
language: python
python:
- 3.6
- nightly
- 3.6
- nightly
matrix:
allow_failures:
- python: nightly
- python: nightly
addons:
apt:
packages:
- libelf-dev
- libdw-dev
- binutils-dev
- libelf-dev
- libdw-dev
- binutils-dev
install:
- wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
- tar zxvf ta-lib-0.4.0-src.tar.gz
- cd ta-lib && ./configure && sudo make && sudo make install && cd ..
- export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
- pip install -r requirements.txt
- wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
- tar zxvf ta-lib-0.4.0-src.tar.gz
- cd ta-lib && ./configure && sudo make && sudo make install && cd ..
- export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
- pip install -r requirements.txt
script:
- python -m unittest
- python -m unittest
notifications:
slack:
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=

View File

@ -44,6 +44,9 @@ profit dips below -10% for a given trade. This parameter is optional.
Possible values are `running` or `stopped`. (default=`running`)
If the value is `stopped` the bot has to be started with `/start` first.
`ask_last_balance` sets the bidding price. Value `0.0` will use `ask` price, `1.0` will
use the `last` price and values between those interpolate between ask and last price. Using `ask` price will guarantee quick success in bid, but bot will also end up paying more then would probably have been necessary.
The other values should be self-explanatory,
if not feel free to raise a github issue.

View File

@ -3,7 +3,6 @@ from datetime import timedelta
import logging
import arrow
import requests
from pandas.io.json import json_normalize
from pandas import DataFrame
import talib.abstract as ta
@ -23,7 +22,7 @@ def get_ticker(pair: str, minimum_date: arrow.Arrow) -> dict:
}
params = {
'marketName': pair.replace('_', '-'),
'tickInterval': 'OneMin',
'tickInterval': 'fiveMin',
'_': minimum_date.timestamp * 1000
}
data = requests.get(url, params=params, headers=headers).json()
@ -49,19 +48,9 @@ def populate_indicators(dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
"""
dataframe['close_30_ema'] = ta.EMA(dataframe, timeperiod=30)
dataframe['close_90_ema'] = ta.EMA(dataframe, timeperiod=90)
dataframe['sar'] = ta.SAR(dataframe, 0.02, 0.2)
# calculate StochRSI
stochrsi = ta.STOCHRSI(dataframe)
dataframe['stochrsi'] = stochrsi['fastd'] # values between 0-100, not 0-1
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macds'] = macd['macdsignal']
dataframe['macdh'] = macd['macdhist']
dataframe['ema'] = ta.EMA(dataframe, timeperiod=33)
dataframe['sar'] = ta.SAR(dataframe, 0.02, 0.22)
dataframe['adx'] = ta.ADX(dataframe)
return dataframe
@ -72,13 +61,29 @@ def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
prev_sar = dataframe['sar'].shift(1)
prev_close = dataframe['close'].shift(1)
prev_sar2 = dataframe['sar'].shift(2)
prev_close2 = dataframe['close'].shift(2)
# wait for stable turn from bearish to bullish market
dataframe.loc[
(dataframe['stochrsi'] < 20)
& (dataframe['macd'] > dataframe['macds'])
& (dataframe['close'] > dataframe['sar']),
'buy'
(dataframe['close'] > dataframe['sar']) &
(prev_close > prev_sar) &
(prev_close2 < prev_sar2),
'swap'
] = 1
# consider prices above ema to be in upswing
dataframe.loc[dataframe['ema'] <= dataframe['close'], 'upswing'] = 1
dataframe.loc[
(dataframe['upswing'] == 1) &
(dataframe['swap'] == 1) &
(dataframe['adx'] > 25), # adx over 25 tells there's enough momentum
'buy'] = 1
dataframe.loc[dataframe['buy'] == 1, 'buy_price'] = dataframe['close']
return dataframe
@ -127,27 +132,20 @@ def plot_dataframe(dataframe: DataFrame, pair: str) -> None:
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
# Three subplots sharing x axe
fig, (ax1, ax2, ax3) = plt.subplots(3, sharex=True)
# Two subplots sharing x axis
fig, (ax1, ax2) = plt.subplots(2, sharex=True)
fig.suptitle(pair, fontsize=14, fontweight='bold')
ax1.plot(dataframe.index.values, dataframe['sar'], 'g_', label='pSAR')
ax1.plot(dataframe.index.values, dataframe['close'], label='close')
ax1.plot(dataframe.index.values, dataframe['close_30_ema'], label='EMA(30)')
ax1.plot(dataframe.index.values, dataframe['close_90_ema'], label='EMA(90)')
# ax1.plot(dataframe.index.values, dataframe['sell'], 'ro', label='sell')
ax1.plot(dataframe.index.values, dataframe['buy_price'], 'bo', label='buy')
ax1.plot(dataframe.index.values, dataframe['ema'], '--', label='EMA(20)')
ax1.plot(dataframe.index.values, dataframe['buy'], 'bo', label='buy')
ax1.legend()
ax2.plot(dataframe.index.values, dataframe['macd'], label='MACD')
ax2.plot(dataframe.index.values, dataframe['macds'], label='MACDS')
ax2.plot(dataframe.index.values, dataframe['macdh'], label='MACD Histogram')
ax2.plot(dataframe.index.values, [0] * len(dataframe.index.values))
ax2.plot(dataframe.index.values, dataframe['adx'], label='ADX')
ax2.plot(dataframe.index.values, [25] * len(dataframe.index.values))
ax2.legend()
ax3.plot(dataframe.index.values, dataframe['stochrsi'], label='StochRSI')
ax3.plot(dataframe.index.values, [80] * len(dataframe.index.values))
ax3.plot(dataframe.index.values, [20] * len(dataframe.index.values))
ax3.legend()
# Fine-tune figure; make subplots close to each other and hide x ticks for
# all but bottom plot.
fig.subplots_adjust(hspace=0)
@ -158,8 +156,8 @@ def plot_dataframe(dataframe: DataFrame, pair: str) -> None:
if __name__ == '__main__':
# Install PYQT5==5.9 manually if you want to test this helper function
while True:
pair = 'BTC_ANT'
test_pair = 'BTC_ANT'
#for pair in ['BTC_ANT', 'BTC_ETH', 'BTC_GNT', 'BTC_ETC']:
# get_buy_signal(pair)
plot_dataframe(analyze_ticker(pair), pair)
plot_dataframe(analyze_ticker(test_pair), test_pair)
time.sleep(60)

View File

@ -9,11 +9,8 @@
"0": 0.02
},
"stoploss": -0.10,
"poloniex": {
"enabled": false,
"key": "key",
"secret": "secret",
"pair_whitelist": []
"bid_strategy": {
"ask_last_balance": 0.0
},
"bittrex": {
"enabled": true,

View File

@ -3,7 +3,6 @@ import logging
from typing import List
from bittrex.bittrex import Bittrex
from poloniex import Poloniex
logger = logging.getLogger(__name__)
@ -14,7 +13,6 @@ _CONF = {}
class Exchange(enum.Enum):
POLONIEX = 0
BITTREX = 1
@ -33,13 +31,8 @@ def init(config: dict) -> None:
if config['dry_run']:
logger.info('Instance is running with dry_run enabled')
use_poloniex = config.get('poloniex', {}).get('enabled', False)
use_bittrex = config.get('bittrex', {}).get('enabled', False)
if use_poloniex:
EXCHANGE = Exchange.POLONIEX
_API = Poloniex(key=config['poloniex']['key'], secret=config['poloniex']['secret'])
elif use_bittrex:
if use_bittrex:
EXCHANGE = Exchange.BITTREX
_API = Bittrex(api_key=config['bittrex']['key'], api_secret=config['bittrex']['secret'])
else:
@ -47,9 +40,10 @@ def init(config: dict) -> None:
# Check if all pairs are available
markets = get_markets()
for pair in config[EXCHANGE.name.lower()]['pair_whitelist']:
exchange_name = EXCHANGE.name.lower()
for pair in config[exchange_name]['pair_whitelist']:
if pair not in markets:
raise RuntimeError('Pair {} is not available at Poloniex'.format(pair))
raise RuntimeError('Pair {} is not available at {}'.format(pair, exchange_name))
def buy(pair: str, rate: float, amount: float) -> str:
@ -62,9 +56,6 @@ def buy(pair: str, rate: float, amount: float) -> str:
"""
if _CONF['dry_run']:
return 'dry_run'
elif EXCHANGE == Exchange.POLONIEX:
_API.buy(pair, rate, amount)
# TODO: return order id
elif EXCHANGE == Exchange.BITTREX:
data = _API.buy_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
@ -82,9 +73,6 @@ def sell(pair: str, rate: float, amount: float) -> str:
"""
if _CONF['dry_run']:
return 'dry_run'
elif EXCHANGE == Exchange.POLONIEX:
_API.sell(pair, rate, amount)
# TODO: return order id
elif EXCHANGE == Exchange.BITTREX:
data = _API.sell_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
@ -100,9 +88,6 @@ def get_balance(currency: str) -> float:
"""
if _CONF['dry_run']:
return 999.9
elif EXCHANGE == Exchange.POLONIEX:
data = _API.returnBalances()
return float(data[currency])
elif EXCHANGE == Exchange.BITTREX:
data = _API.get_balance(currency)
if not data['success']:
@ -116,14 +101,7 @@ def get_ticker(pair: str) -> dict:
:param pair: Pair as str, format: BTC_ETC
:return: dict
"""
if EXCHANGE == Exchange.POLONIEX:
data = _API.returnTicker()
return {
'bid': float(data[pair]['highestBid']),
'ask': float(data[pair]['lowestAsk']),
'last': float(data[pair]['last'])
}
elif EXCHANGE == Exchange.BITTREX:
if EXCHANGE == Exchange.BITTREX:
data = _API.get_ticker(pair.replace('_', '-'))
if not data['success']:
raise RuntimeError('BITTREX: {}'.format(data['message']))
@ -142,8 +120,6 @@ def cancel_order(order_id: str) -> None:
"""
if _CONF['dry_run']:
pass
elif EXCHANGE == Exchange.POLONIEX:
raise NotImplemented('Not implemented')
elif EXCHANGE == Exchange.BITTREX:
data = _API.cancel(order_id)
if not data['success']:
@ -158,8 +134,6 @@ def get_open_orders(pair: str) -> List[dict]:
"""
if _CONF['dry_run']:
return []
elif EXCHANGE == Exchange.POLONIEX:
raise NotImplemented('Not implemented')
elif EXCHANGE == Exchange.BITTREX:
data = _API.get_open_orders(pair.replace('_', '-'))
if not data['success']:
@ -180,9 +154,7 @@ def get_pair_detail_url(pair: str) -> str:
:param pair: pair as str, format: BTC_ANT
:return: url as str
"""
if EXCHANGE == Exchange.POLONIEX:
raise NotImplemented('Not implemented')
elif EXCHANGE == Exchange.BITTREX:
if EXCHANGE == Exchange.BITTREX:
return 'https://bittrex.com/Market/Index?MarketName={}'.format(pair.replace('_', '-'))
@ -191,10 +163,7 @@ def get_markets() -> List[str]:
Returns all available markets
:return: list of all available pairs
"""
if EXCHANGE == Exchange.POLONIEX:
# TODO: implement
raise NotImplemented('Not implemented')
elif EXCHANGE == Exchange. BITTREX:
if EXCHANGE == Exchange. BITTREX:
data = _API.get_markets()
if not data['success']:
raise RuntimeError('BITTREX: {}'.format(data['message']))

57
main.py
View File

@ -4,7 +4,7 @@ import logging
import time
import traceback
from datetime import datetime
from typing import Optional
from typing import Dict, Optional
from jsonschema import validate
@ -22,7 +22,7 @@ logger = logging.getLogger(__name__)
__author__ = "gcarq"
__copyright__ = "gcarq 2017"
__license__ = "GPLv3"
__version__ = "0.9.0"
__version__ = "0.10.0"
_CONF = {}
@ -94,8 +94,10 @@ def execute_sell(trade: Trade, current_rate: float) -> None:
# Get available balance
currency = trade.pair.split('_')[1]
balance = exchange.get_balance(currency)
whitelist = _CONF[trade.exchange.name.lower()]['pair_whitelist']
profit = trade.exec_sell_order(current_rate, balance)
whitelist.append(trade.pair)
message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format(
trade.exchange.name,
trade.pair.replace('_', '/'),
@ -107,6 +109,28 @@ def execute_sell(trade: Trade, current_rate: float) -> None:
telegram.send_msg(message)
def should_sell(trade: Trade, current_rate: float, current_time: datetime) -> bool:
"""
Based an earlier trade and current price and configuration, decides whether bot should sell
:return True if bot should sell at current rate
"""
current_profit = (current_rate - trade.open_rate) / trade.open_rate
if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']):
logger.debug('Stop loss hit.')
return True
for duration, threshold in sorted(_CONF['minimal_roi'].items()):
duration, threshold = float(duration), float(threshold)
# Check if time matches and current rate is above threshold
time_diff = (current_time - trade.open_date).total_seconds() / 60
if time_diff > duration and current_profit > threshold:
return True
logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit * 100.0)
return False
def handle_trade(trade: Trade) -> None:
"""
Sells the current pair if the threshold is reached and updates the trade record.
@ -117,27 +141,22 @@ def handle_trade(trade: Trade) -> None:
raise ValueError('attempt to handle closed trade: {}'.format(trade))
logger.debug('Handling open trade %s ...', trade)
# Get current rate
current_rate = exchange.get_ticker(trade.pair)['bid']
current_profit = 100.0 * ((current_rate - trade.open_rate) / trade.open_rate)
if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']) * 100.0:
logger.debug('Stop loss hit.')
current_rate = exchange.get_ticker(trade.pair)['bid']
if should_sell(trade, current_rate, datetime.utcnow()):
execute_sell(trade, current_rate)
return
for duration, threshold in sorted(_CONF['minimal_roi'].items()):
duration, threshold = float(duration), float(threshold)
# Check if time matches and current rate is above threshold
time_diff = (datetime.utcnow() - trade.open_date).total_seconds() / 60
if time_diff > duration and current_rate > (1 + threshold) * trade.open_rate:
execute_sell(trade, current_rate)
return
logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit)
except ValueError:
logger.exception('Unable to handle open order')
def get_target_bid(ticker: Dict[str, float]) -> float:
""" Calculates bid target between current ask price and last price """
if ticker['ask'] < ticker['last']:
return ticker['ask']
balance = _CONF['bid_strategy']['ask_last_balance']
return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
def create_trade(stake_amount: float, _exchange: exchange.Exchange) -> Optional[Trade]:
"""
@ -148,7 +167,7 @@ def create_trade(stake_amount: float, _exchange: exchange.Exchange) -> Optional[
"""
logger.info('Creating new trade with stake_amount: %f ...', stake_amount)
whitelist = _CONF[_exchange.name.lower()]['pair_whitelist']
# Check if btc_amount is fulfilled
# Check if stake_amount is fulfilled
if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
raise ValueError(
'stake amount is not fulfilled (currency={}'.format(_CONF['stake_currency'])
@ -174,7 +193,7 @@ def create_trade(stake_amount: float, _exchange: exchange.Exchange) -> Optional[
else:
return None
open_rate = exchange.get_ticker(pair)['ask']
open_rate = get_target_bid(exchange.get_ticker(pair))
amount = stake_amount / open_rate
order_id = exchange.buy(pair, open_rate, amount)
@ -188,7 +207,7 @@ def create_trade(stake_amount: float, _exchange: exchange.Exchange) -> Optional[
logger.info(message)
telegram.send_msg(message)
return Trade(pair=pair,
btc_amount=stake_amount,
stake_amount=stake_amount,
open_rate=open_rate,
open_date=datetime.utcnow(),
amount=amount,

15
misc.py
View File

@ -48,7 +48,18 @@ CONF_SCHEMA = {
'minProperties': 1
},
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
'poloniex': {'$ref': '#/definitions/exchange'},
'bid_strategy': {
'type': 'object',
'properties': {
'ask_last_balance': {
'type': 'number',
'minimum': 0,
'maximum': 1,
'exclusiveMaximum': False
},
},
'required': ['ask_last_balance']
},
'bittrex': {'$ref': '#/definitions/exchange'},
'telegram': {
'type': 'object',
@ -78,7 +89,6 @@ CONF_SCHEMA = {
}
},
'anyOf': [
{'required': ['poloniex']},
{'required': ['bittrex']}
],
'required': [
@ -87,6 +97,7 @@ CONF_SCHEMA = {
'stake_amount',
'dry_run',
'minimal_roi',
'bid_strategy',
'telegram'
]
}

View File

@ -49,7 +49,7 @@ class Trade(Base):
open_rate = Column(Float, nullable=False)
close_rate = Column(Float)
close_profit = Column(Float)
btc_amount = Column(Float, nullable=False)
stake_amount = Column(Float, name='btc_amount', nullable=False)
amount = Column(Float, nullable=False)
open_date = Column(DateTime, nullable=False, default=datetime.utcnow)
close_date = Column(DateTime)

View File

@ -1,4 +1,3 @@
-e git+https://github.com/s4w3d0ff/python-poloniex.git#egg=Poloniex
-e git+https://github.com/ericsomdahl/python-bittrex.git#egg=python-bittrex
SQLAlchemy==1.1.13
python-telegram-bot==7.0.1

View File

@ -157,7 +157,7 @@ def _profit(bot: Bot, update: Update) -> None:
current_rate = exchange.get_ticker(trade.pair)['bid']
profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate)
profit_amounts.append((profit / 100) * trade.btc_amount)
profit_amounts.append((profit / 100) * trade.stake_amount)
profits.append(profit)
best_pair = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \

71
test/test_backtesting.py Normal file
View File

@ -0,0 +1,71 @@
# pragma pylint: disable=missing-docstring
import unittest
from unittest.mock import patch
import os
import json
import logging
import arrow
from pandas import DataFrame
from analyze import analyze_ticker
from persistence import Trade
from main import should_sell
def print_results(results):
print('Made {} buys. Average profit {:.1f}%. Total profit was {:.3f}. Average duration {:.1f} mins.'.format(
len(results.index),
results.profit.mean() * 100.0,
results.profit.sum(),
results.duration.mean()*5
))
class TestMain(unittest.TestCase):
pairs = ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay', 'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc']
conf = {
"minimal_roi": {
"2880": 0.005,
"720": 0.01,
"0": 0.02
},
"stoploss": -0.10
}
@classmethod
def setUpClass(cls):
logging.disable(logging.DEBUG) # disable debug logs that slow backtesting a lot
@unittest.skipIf(not os.environ.get('BACKTEST', False), "slow, should be run manually")
def test_backtest(self):
trades = []
with patch.dict('main._CONF', self.conf):
for pair in self.pairs:
with open('test/testdata/'+pair+'.json') as data_file:
data = json.load(data_file)
with patch('analyze.get_ticker', return_value=data):
with patch('arrow.utcnow', return_value=arrow.get('2017-08-20T14:50:00')):
ticker = analyze_ticker(pair)
# for each buy point
for index, row in ticker[ticker.buy == 1].iterrows():
trade = Trade(
open_rate=row['close'],
open_date=arrow.get(row['date']).datetime,
amount=1,
)
# calculate win/lose forwards from buy point
for index2, row2 in ticker[index:].iterrows():
if should_sell(trade, row2['close'], arrow.get(row2['date']).datetime):
current_profit = (row2['close'] - trade.open_rate) / trade.open_rate
trades.append((pair, current_profit, index2 - index))
break
labels = ['currency', 'profit', 'duration']
results = DataFrame.from_records(trades, columns=labels)
print('====================== BACKTESTING REPORT ================================')
for pair in self.pairs:
print('For currency {}:'.format(pair))
print_results(results[results.currency == pair])
print('TOTAL OVER ALL TRADES:')
print_results(results)

View File

@ -4,7 +4,7 @@ from unittest.mock import patch, MagicMock
from jsonschema import validate
import exchange
from main import create_trade, handle_trade, close_trade_if_fulfilled, init
from main import create_trade, handle_trade, close_trade_if_fulfilled, init, get_target_bid
from misc import CONF_SCHEMA
from persistence import Trade
@ -20,11 +20,8 @@ class TestMain(unittest.TestCase):
"720": 0.01,
"0": 0.02
},
"poloniex": {
"enabled": False,
"key": "key",
"secret": "secret",
"pair_whitelist": []
"bid_strategy": {
"ask_last_balance": 0.0
},
"bittrex": {
"enabled": True,
@ -61,7 +58,7 @@ class TestMain(unittest.TestCase):
self.assertEqual(trade.pair, 'BTC_ETH')
self.assertEqual(trade.exchange, exchange.Exchange.BITTREX)
self.assertEqual(trade.amount, 206.43811673387373)
self.assertEqual(trade.btc_amount, 15.0)
self.assertEqual(trade.stake_amount, 15.0)
self.assertEqual(trade.is_open, True)
self.assertIsNotNone(trade.open_date)
buy_signal.assert_called_once_with('BTC_ETH')
@ -96,6 +93,18 @@ class TestMain(unittest.TestCase):
self.assertTrue(closed)
self.assertEqual(trade.is_open, False)
def test_balance_fully_ask_side(self):
with patch.dict('main._CONF', {'bid_strategy': {'ask_last_balance': 0.0}}):
self.assertEqual(get_target_bid({'ask': 20, 'last': 10}), 20)
def test_balance_fully_last_side(self):
with patch.dict('main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}):
self.assertEqual(get_target_bid({'ask': 20, 'last': 10}), 10)
def test_balance_when_last_bigger_than_ask(self):
with patch.dict('main._CONF', {'bid_strategy': {'ask_last_balance': 1.0}}):
self.assertEqual(get_target_bid({'ask': 5, 'last': 10}), 5)
@classmethod
def setUpClass(cls):
validate(cls.conf, CONF_SCHEMA)

View File

@ -10,7 +10,7 @@ class TestTrade(unittest.TestCase):
with patch('main.exchange.sell', side_effect='mocked_order_id') as api_mock:
trade = Trade(
pair='BTC_ETH',
btc_amount=1.00,
stake_amount=1.00,
open_rate=0.50,
amount=10.00,
exchange=Exchange.BITTREX,

View File

@ -28,11 +28,8 @@ class TestTelegram(unittest.TestCase):
"720": 0.01,
"0": 0.02
},
"poloniex": {
"enabled": False,
"key": "key",
"secret": "secret",
"pair_whitelist": []
"bid_strategy": {
"ask_last_balance": 0.0
},
"bittrex": {
"enabled": True,

1
test/testdata/btc-edg.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-etc.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-eth.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-ltc.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-mtl.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-neo.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-omg.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-pay.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-pivx.json vendored Normal file

File diff suppressed because one or more lines are too long

1
test/testdata/btc-qtum.json vendored Normal file

File diff suppressed because one or more lines are too long