bot_id - multiple bots connected to a single database server

This commit is contained in:
Nullart2 2018-08-16 17:28:00 +08:00
parent 1edbc494ee
commit 4c62589ebc
4 changed files with 46 additions and 15 deletions

View File

@ -43,6 +43,7 @@ SUPPORTED_FIAT = [
CONF_SCHEMA = {
'type': 'object',
'properties': {
'bot_id': {'type': 'integer', 'minimum': 0},
'max_open_trades': {'type': 'integer', 'minimum': 0},
'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']},

View File

@ -122,6 +122,7 @@ class FreqtradeBot(object):
stake_amount = self.config['stake_amount']
minimal_roi = self.config['minimal_roi']
ticker_interval = self.config['ticker_interval']
max_open_trades = self.config['max_open_trades']
exchange_name = self.config['exchange']['name']
strategy_name = self.config.get('strategy', '')
self.rpc.send_msg({
@ -130,10 +131,12 @@ class FreqtradeBot(object):
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
f'*Minimum ROI:* `{minimal_roi}`\n'
f'*Ticker Interval:* `{ticker_interval}`\n'
f'*Strategy:* `{strategy_name}`'
f'*Strategy:* `{strategy_name}`\n'
f'*Maximum Open Trades:* `{max_open_trades}`'
})
if self.config.get('dynamic_whitelist', False):
top_pairs = 'top ' + str(self.config.get('dynamic_whitelist', 20))
top_pairs = 'top ' + str(self.config.get('dynamic_whitelist', 20)) +\
' highest volume'
specific_pairs = ''
else:
top_pairs = 'whitelisted'
@ -181,7 +184,8 @@ class FreqtradeBot(object):
self.config['exchange']['pair_whitelist'] = final_list
# Query trades from persistence layer
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
trades = Trade.query.filter(Trade.bot_id == self.config.get('bot_id', 0)).\
filter(Trade.is_open.is_(True)).all()
# First process current opened trades
for trade in trades:
@ -288,7 +292,8 @@ class FreqtradeBot(object):
avaliable_amount = self.exchange.get_balance(self.config['stake_currency'])
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
open_trades = len(Trade.query.filter(Trade.bot_id == self.config.get('bot_id', 0)).
filter(Trade.is_open.is_(True)).all())
if open_trades >= self.config['max_open_trades']:
logger.warning('Can\'t open a new trade: max number of trades is reached')
return None
@ -354,7 +359,8 @@ class FreqtradeBot(object):
whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])
# Remove currently opened and latest pairs from whitelist
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
for trade in Trade.query.filter(Trade.bot_id == self.config.get('bot_id', 0)).\
filter(Trade.is_open.is_(True)).all():
if trade.pair in whitelist:
whitelist.remove(trade.pair)
logger.debug('Ignoring %s in pair whitelist', trade.pair)
@ -553,7 +559,8 @@ class FreqtradeBot(object):
buy_timeoutthreashold = arrow.utcnow().shift(minutes=-buy_timeout).datetime
sell_timeoutthreashold = arrow.utcnow().shift(minutes=-sell_timeout).datetime
for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
for trade in Trade.query.filter(Trade.bot_id == self.config.get('bot_id', 0)).\
filter(Trade.open_order_id.isnot(None)).all():
try:
# FIXME: Somehow the query above returns results
# where the open_order_id is in fact None.

View File

@ -57,7 +57,7 @@ def init(config: Dict) -> None:
# Clean dry_run DB if the db is not in-memory
if config.get('dry_run', False) and db_url != 'sqlite://':
clean_dry_run_db()
clean_dry_run_db(config.get('bot_id', 0))
def has_column(columns, searchname: str) -> bool:
@ -129,6 +129,21 @@ def check_migrate(engine) -> None:
inspector = inspect(engine)
cols = inspector.get_columns('trades')
# backwards compatible
if not has_column(cols, 'open_rate_requested'):
engine.execute("alter table trades add open_rate_requested float")
if not has_column(cols, 'close_rate_requested'):
engine.execute("alter table trades add close_rate_requested float")
if not has_column(cols, 'stop_loss'):
engine.execute("alter table trades add stop_loss float")
if not has_column(cols, 'initial_stop_loss'):
engine.execute("alter table trades add initial_stop_loss float")
if not has_column(cols, 'max_rate'):
engine.execute("alter table trades add max_rate float")
if not has_column(cols, 'bot_id'):
engine.execute("alter table trades add bot_id integer")
engine.execute("create index bot_id_idx ON trades (bot_id);")
def cleanup() -> None:
"""
@ -138,12 +153,13 @@ def cleanup() -> None:
Trade.session.flush()
def clean_dry_run_db() -> None:
def clean_dry_run_db(bot_id: int = 0) -> None:
"""
Remove open_order_id from a Dry_run DB
:return: None
"""
for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
for trade in Trade.query.filter(Trade.bot_id == bot_id).\
filter(Trade.open_order_id.isnot(None)).all():
# Check we are updating only a dry_run order not a prod one
if 'dry_run' in trade.open_order_id:
trade.open_order_id = None
@ -156,6 +172,7 @@ class Trade(_DECL_BASE):
__tablename__ = 'trades'
id = Column(Integer, primary_key=True)
bot_id = Column(Integer, default=0, index=True)
exchange = Column(String, nullable=False)
pair = Column(String, nullable=False, index=True)
is_open = Column(Boolean, nullable=False, default=True, index=True)

View File

@ -62,6 +62,7 @@ class RPC(object):
:return: None
"""
self._freqtrade = freqtrade
self.bot_id = freqtrade.config.get('bot_id', 0)
@property
def name(self) -> str:
@ -82,7 +83,8 @@ class RPC(object):
a remotely exposed function
"""
# Fetch open trade
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
trades = Trade.query.filter(Trade.bot_id == self.bot_id).\
filter(Trade.is_open.is_(True)).all()
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
elif not trades:
@ -116,7 +118,8 @@ class RPC(object):
return results
def _rpc_status_table(self) -> DataFrame:
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
trades = Trade.query.filter(Trade.bot_id == self.bot_id).\
filter(Trade.is_open.is_(True)).all()
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
elif not trades:
@ -188,7 +191,8 @@ class RPC(object):
def _rpc_trade_statistics(
self, stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]:
""" Returns cumulative profit statistics """
trades = Trade.query.order_by(Trade.id).all()
trades = Trade.query.filter(Trade.bot_id == self.bot_id).\
order_by(Trade.id).all()
profit_all_coin = []
profit_all_percent = []
@ -357,12 +361,13 @@ class RPC(object):
if trade_id == 'all':
# Execute sell for all open orders
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
for trade in Trade.query.filter(Trade.bot_id == self.bot_id).\
filter(Trade.is_open.is_(True)).all():
_exec_forcesell(trade)
return
# Query for trade
trade = Trade.query.filter(
trade = Trade.query.filter(Trade.bot_id == self.bot_id).filter(
sql.and_(
Trade.id == trade_id,
Trade.is_open.is_(True)
@ -400,4 +405,5 @@ class RPC(object):
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
return Trade.query.filter(Trade.is_open.is_(True)).all()
return Trade.query.filter(Trade.bot_id == self.bot_id).\
filter(Trade.is_open.is_(True)).all()