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 = { CONF_SCHEMA = {
'type': 'object', 'type': 'object',
'properties': { 'properties': {
'bot_id': {'type': 'integer', 'minimum': 0},
'max_open_trades': {'type': 'integer', 'minimum': 0}, 'max_open_trades': {'type': 'integer', 'minimum': 0},
'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())}, 'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']}, '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'] stake_amount = self.config['stake_amount']
minimal_roi = self.config['minimal_roi'] minimal_roi = self.config['minimal_roi']
ticker_interval = self.config['ticker_interval'] ticker_interval = self.config['ticker_interval']
max_open_trades = self.config['max_open_trades']
exchange_name = self.config['exchange']['name'] exchange_name = self.config['exchange']['name']
strategy_name = self.config.get('strategy', '') strategy_name = self.config.get('strategy', '')
self.rpc.send_msg({ self.rpc.send_msg({
@ -130,10 +131,12 @@ class FreqtradeBot(object):
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'*Ticker Interval:* `{ticker_interval}`\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): 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 = '' specific_pairs = ''
else: else:
top_pairs = 'whitelisted' top_pairs = 'whitelisted'
@ -181,7 +184,8 @@ class FreqtradeBot(object):
self.config['exchange']['pair_whitelist'] = final_list self.config['exchange']['pair_whitelist'] = final_list
# Query trades from persistence layer # 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 # First process current opened trades
for trade in trades: for trade in trades:
@ -288,7 +292,8 @@ class FreqtradeBot(object):
avaliable_amount = self.exchange.get_balance(self.config['stake_currency']) avaliable_amount = self.exchange.get_balance(self.config['stake_currency'])
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: 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']: if open_trades >= self.config['max_open_trades']:
logger.warning('Can\'t open a new trade: max number of trades is reached') logger.warning('Can\'t open a new trade: max number of trades is reached')
return None return None
@ -354,7 +359,8 @@ class FreqtradeBot(object):
whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist']) whitelist = copy.deepcopy(self.config['exchange']['pair_whitelist'])
# Remove currently opened and latest pairs from 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: if trade.pair in whitelist:
whitelist.remove(trade.pair) whitelist.remove(trade.pair)
logger.debug('Ignoring %s in pair whitelist', 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 buy_timeoutthreashold = arrow.utcnow().shift(minutes=-buy_timeout).datetime
sell_timeoutthreashold = arrow.utcnow().shift(minutes=-sell_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: try:
# FIXME: Somehow the query above returns results # FIXME: Somehow the query above returns results
# where the open_order_id is in fact None. # 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 # Clean dry_run DB if the db is not in-memory
if config.get('dry_run', False) and db_url != 'sqlite://': 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: def has_column(columns, searchname: str) -> bool:
@ -129,6 +129,21 @@ def check_migrate(engine) -> None:
inspector = inspect(engine) inspector = inspect(engine)
cols = inspector.get_columns('trades') 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: def cleanup() -> None:
""" """
@ -138,12 +153,13 @@ def cleanup() -> None:
Trade.session.flush() 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 Remove open_order_id from a Dry_run DB
:return: None :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 # Check we are updating only a dry_run order not a prod one
if 'dry_run' in trade.open_order_id: if 'dry_run' in trade.open_order_id:
trade.open_order_id = None trade.open_order_id = None
@ -156,6 +172,7 @@ class Trade(_DECL_BASE):
__tablename__ = 'trades' __tablename__ = 'trades'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
bot_id = Column(Integer, default=0, index=True)
exchange = Column(String, nullable=False) exchange = Column(String, nullable=False)
pair = Column(String, nullable=False, index=True) pair = Column(String, nullable=False, index=True)
is_open = Column(Boolean, nullable=False, default=True, index=True) is_open = Column(Boolean, nullable=False, default=True, index=True)

View File

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