bot_id - multiple bots connected to a single database server
This commit is contained in:
parent
1edbc494ee
commit
4c62589ebc
@ -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']},
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user