Merge pull request #6670 from freqtrade/store_asset

store base and quote currency separately in the database
This commit is contained in:
Matthias 2022-04-10 08:42:28 +02:00 committed by GitHub
commit 46c18dfce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 62 additions and 18 deletions

View File

@ -341,15 +341,11 @@ class Exchange:
return sorted(set([x['quote'] for _, x in markets.items()])) return sorted(set([x['quote'] for _, x in markets.items()]))
def get_pair_quote_currency(self, pair: str) -> str: def get_pair_quote_currency(self, pair: str) -> str:
""" """ Return a pair's quote currency (base/quote:settlement) """
Return a pair's quote currency
"""
return self.markets.get(pair, {}).get('quote', '') return self.markets.get(pair, {}).get('quote', '')
def get_pair_base_currency(self, pair: str) -> str: def get_pair_base_currency(self, pair: str) -> str:
""" """ Return a pair's base currency (base/quote:settlement) """
Return a pair's base currency
"""
return self.markets.get(pair, {}).get('base', '') return self.markets.get(pair, {}).get('base', '')
def market_is_future(self, market: Dict[str, Any]) -> bool: def market_is_future(self, market: Dict[str, Any]) -> bool:

View File

@ -676,6 +676,7 @@ class FreqtradeBot(LoggingMixin):
# 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 = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
base_currency = self.exchange.get_pair_base_currency(pair)
open_date = datetime.now(timezone.utc) open_date = datetime.now(timezone.utc)
funding_fees = self.exchange.get_funding_fees( funding_fees = self.exchange.get_funding_fees(
pair=pair, amount=amount, is_short=is_short, open_date=open_date) pair=pair, amount=amount, is_short=is_short, open_date=open_date)
@ -683,6 +684,8 @@ class FreqtradeBot(LoggingMixin):
if trade is None: if trade is None:
trade = Trade( trade = Trade(
pair=pair, pair=pair,
base_currency=base_currency,
stake_currency=self.config['stake_currency'],
stake_amount=stake_amount, stake_amount=stake_amount,
amount=amount, amount=amount,
is_open=True, is_open=True,

View File

@ -726,6 +726,7 @@ class Backtesting:
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
self.order_id_counter += 1 self.order_id_counter += 1
base_currency = self.exchange.get_pair_base_currency(pair)
amount = round((stake_amount / propose_rate) * leverage, 8) amount = round((stake_amount / propose_rate) * leverage, 8)
is_short = (direction == 'short') is_short = (direction == 'short')
# Necessary for Margin trading. Disabled until support is enabled. # Necessary for Margin trading. Disabled until support is enabled.
@ -738,6 +739,8 @@ class Backtesting:
id=self.trade_id_counter, id=self.trade_id_counter,
open_order_id=self.order_id_counter, open_order_id=self.order_id_counter,
pair=pair, pair=pair,
base_currency=base_currency,
stake_currency=self.config['stake_currency'],
open_rate=propose_rate, open_rate=propose_rate,
open_rate_requested=propose_rate, open_rate_requested=propose_rate,
open_date=current_time, open_date=current_time,

View File

@ -58,6 +58,8 @@ def migrate_trades_and_orders_table(
decl_base, inspector, engine, decl_base, inspector, engine,
trade_back_name: str, cols: List, trade_back_name: str, cols: List,
order_back_name: str, cols_order: List): order_back_name: str, cols_order: List):
base_currency = get_column_def(cols, 'base_currency', 'null')
stake_currency = get_column_def(cols, 'stake_currency', 'null')
fee_open = get_column_def(cols, 'fee_open', 'fee') fee_open = get_column_def(cols, 'fee_open', 'fee')
fee_open_cost = get_column_def(cols, 'fee_open_cost', 'null') fee_open_cost = get_column_def(cols, 'fee_open_cost', 'null')
fee_open_currency = get_column_def(cols, 'fee_open_currency', 'null') fee_open_currency = get_column_def(cols, 'fee_open_currency', 'null')
@ -130,7 +132,7 @@ def migrate_trades_and_orders_table(
# Copy data back - following the correct schema # Copy data back - following the correct schema
with engine.begin() as connection: with engine.begin() as connection:
connection.execute(text(f"""insert into trades connection.execute(text(f"""insert into trades
(id, exchange, pair, is_open, (id, exchange, pair, base_currency, stake_currency, is_open,
fee_open, fee_open_cost, fee_open_currency, fee_open, fee_open_cost, fee_open_currency,
fee_close, fee_close_cost, fee_close_currency, open_rate, fee_close, fee_close_cost, fee_close_currency, open_rate,
open_rate_requested, close_rate, close_rate_requested, close_profit, open_rate_requested, close_rate, close_rate_requested, close_profit,
@ -142,7 +144,8 @@ def migrate_trades_and_orders_table(
trading_mode, leverage, liquidation_price, is_short, trading_mode, leverage, liquidation_price, is_short,
interest_rate, funding_fees interest_rate, funding_fees
) )
select id, lower(exchange), pair, select id, lower(exchange), pair, {base_currency} base_currency,
{stake_currency} stake_currency,
is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost, is_open, {fee_open} fee_open, {fee_open_cost} fee_open_cost,
{fee_open_currency} fee_open_currency, {fee_close} fee_close, {fee_open_currency} fee_open_currency, {fee_close} fee_close,
{fee_close_cost} fee_close_cost, {fee_close_currency} fee_close_currency, {fee_close_cost} fee_close_cost, {fee_close_currency} fee_close_currency,
@ -230,7 +233,7 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
""" """
inspector = inspect(engine) inspector = inspect(engine)
cols = inspector.get_columns('trades') cols_trades = inspector.get_columns('trades')
cols_orders = inspector.get_columns('orders') cols_orders = inspector.get_columns('orders')
tabs = get_table_names_for_table(inspector, 'trades') tabs = get_table_names_for_table(inspector, 'trades')
table_back_name = get_backup_name(tabs, 'trades_bak') table_back_name = get_backup_name(tabs, 'trades_bak')
@ -241,11 +244,12 @@ def check_migrate(engine, decl_base, previous_tables) -> None:
# Migrates both trades and orders table! # Migrates both trades and orders table!
# if ('orders' not in previous_tables # if ('orders' not in previous_tables
# or not has_column(cols_orders, 'leverage')): # or not has_column(cols_orders, 'leverage')):
if not has_column(cols, 'exit_order_status'): if not has_column(cols_trades, 'base_currency'):
logger.info(f"Running database migration for trades - " logger.info(f"Running database migration for trades - "
f"backup: {table_back_name}, {order_table_bak_name}") f"backup: {table_back_name}, {order_table_bak_name}")
migrate_trades_and_orders_table( migrate_trades_and_orders_table(
decl_base, inspector, engine, table_back_name, cols, order_table_bak_name, cols_orders) decl_base, inspector, engine, table_back_name, cols_trades,
order_table_bak_name, cols_orders)
if 'orders' not in previous_tables and 'trades' in previous_tables: if 'orders' not in previous_tables and 'trades' in previous_tables:
logger.info('Moving open orders to Orders table.') logger.info('Moving open orders to Orders table.')

View File

@ -279,6 +279,8 @@ class LocalTrade():
exchange: str = '' exchange: str = ''
pair: str = '' pair: str = ''
base_currency: str = ''
stake_currency: str = ''
is_open: bool = True is_open: bool = True
fee_open: float = 0.0 fee_open: float = 0.0
fee_open_cost: Optional[float] = None fee_open_cost: Optional[float] = None
@ -397,6 +399,26 @@ class LocalTrade():
else: else:
return "long" return "long"
@property
def safe_base_currency(self) -> str:
"""
Compatibility layer for asset - which can be empty for old trades.
"""
try:
return self.base_currency or self.pair.split('/')[0]
except IndexError:
return ''
@property
def safe_quote_currency(self) -> str:
"""
Compatibility layer for asset - which can be empty for old trades.
"""
try:
return self.stake_currency or self.pair.split('/')[1].split(':')[0]
except IndexError:
return ''
def __init__(self, **kwargs): def __init__(self, **kwargs):
for key in kwargs: for key in kwargs:
setattr(self, key, kwargs[key]) setattr(self, key, kwargs[key])
@ -423,6 +445,8 @@ class LocalTrade():
return { return {
'trade_id': self.id, 'trade_id': self.id,
'pair': self.pair, 'pair': self.pair,
'base_currency': self.safe_base_currency,
'quote_currency': self.safe_quote_currency,
'is_open': self.is_open, 'is_open': self.is_open,
'exchange': self.exchange, 'exchange': self.exchange,
'amount': round(self.amount, 8), 'amount': round(self.amount, 8),
@ -1051,6 +1075,8 @@ class Trade(_DECL_BASE, LocalTrade):
exchange = Column(String(25), nullable=False) exchange = Column(String(25), nullable=False)
pair = Column(String(25), nullable=False, index=True) pair = Column(String(25), nullable=False, index=True)
base_currency = Column(String(25), nullable=True)
stake_currency = Column(String(25), nullable=True)
is_open = Column(Boolean, nullable=False, default=True, index=True) is_open = Column(Boolean, nullable=False, default=True, index=True)
fee_open = Column(Float, nullable=False, default=0.0) fee_open = Column(Float, nullable=False, default=0.0)
fee_open_cost = Column(Float, nullable=True) fee_open_cost = Column(Float, nullable=True)

View File

@ -203,6 +203,8 @@ class OrderSchema(BaseModel):
class TradeSchema(BaseModel): class TradeSchema(BaseModel):
trade_id: int trade_id: int
pair: str pair: str
base_currency: str
quote_currency: str
is_open: bool is_open: bool
is_short: bool is_short: bool
exchange: str exchange: str

View File

@ -197,7 +197,6 @@ class RPC:
trade_dict = trade.to_json() trade_dict = trade.to_json()
trade_dict.update(dict( trade_dict.update(dict(
base_currency=self._freqtrade.config['stake_currency'],
close_profit=trade.close_profit if trade.close_profit is not None else None, close_profit=trade.close_profit if trade.close_profit is not None else None,
current_rate=current_rate, current_rate=current_rate,
current_profit=current_profit, # Deprecated current_profit=current_profit, # Deprecated

View File

@ -508,7 +508,7 @@ class Telegram(RPCHandler):
lines.append("*Open Order:* `{open_order}`") lines.append("*Open Order:* `{open_order}`")
lines_detail = self._prepare_entry_details( lines_detail = self._prepare_entry_details(
r['orders'], r['base_currency'], r['is_open']) r['orders'], r['quote_currency'], r['is_open'])
lines.extend(lines_detail if lines_detail else "") lines.extend(lines_detail if lines_detail else "")
# Filter empty lines using list-comprehension # Filter empty lines using list-comprehension

View File

@ -52,7 +52,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
assert results[0] == { assert results[0] == {
'trade_id': 1, 'trade_id': 1,
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'base_currency': 'BTC', 'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': ANY, 'open_date': ANY,
'open_timestamp': ANY, 'open_timestamp': ANY,
'is_open': ANY, 'is_open': ANY,
@ -135,7 +136,8 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
assert results[0] == { assert results[0] == {
'trade_id': 1, 'trade_id': 1,
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'base_currency': 'BTC', 'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': ANY, 'open_date': ANY,
'open_timestamp': ANY, 'open_timestamp': ANY,
'is_open': ANY, 'is_open': ANY,

View File

@ -931,6 +931,8 @@ def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
'open_order': None, 'open_order': None,
'open_rate': 0.123, 'open_rate': 0.123,
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'stake_amount': 0.001, 'stake_amount': 0.001,
'stop_loss_abs': ANY, 'stop_loss_abs': ANY,
'stop_loss_pct': ANY, 'stop_loss_pct': ANY,
@ -1097,7 +1099,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
# Test creating trade # Test creating trade
fbuy_mock = MagicMock(return_value=Trade( fbuy_mock = MagicMock(return_value=Trade(
pair='ETH/ETH', pair='ETH/BTC',
amount=1, amount=1,
amount_requested=1, amount_requested=1,
exchange='binance', exchange='binance',
@ -1130,7 +1132,9 @@ def test_api_force_entry(botclient, mocker, fee, endpoint):
'open_date': ANY, 'open_date': ANY,
'open_timestamp': ANY, 'open_timestamp': ANY,
'open_rate': 0.245441, 'open_rate': 0.245441,
'pair': 'ETH/ETH', 'pair': 'ETH/BTC',
'base_currency': 'ETH',
'quote_currency': 'BTC',
'stake_amount': 1, 'stake_amount': 1,
'stop_loss_abs': None, 'stop_loss_abs': None,
'stop_loss_pct': None, 'stop_loss_pct': None,

View File

@ -184,7 +184,8 @@ def test_telegram_status(default_conf, update, mocker) -> None:
_rpc_trade_status=MagicMock(return_value=[{ _rpc_trade_status=MagicMock(return_value=[{
'trade_id': 1, 'trade_id': 1,
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'base_currency': 'BTC', 'base_currency': 'ETH',
'quote_currency': 'BTC',
'open_date': arrow.utcnow(), 'open_date': arrow.utcnow(),
'close_date': None, 'close_date': None,
'open_rate': 1.099e-05, 'open_rate': 1.099e-05,

View File

@ -1561,6 +1561,8 @@ def test_to_json(fee):
assert result == {'trade_id': None, assert result == {'trade_id': None,
'pair': 'ADA/USDT', 'pair': 'ADA/USDT',
'base_currency': 'ADA',
'quote_currency': 'USDT',
'is_open': None, 'is_open': None,
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
'open_timestamp': int(trade.open_date.timestamp() * 1000), 'open_timestamp': int(trade.open_date.timestamp() * 1000),
@ -1637,6 +1639,8 @@ def test_to_json(fee):
assert result == {'trade_id': None, assert result == {'trade_id': None,
'pair': 'XRP/BTC', 'pair': 'XRP/BTC',
'base_currency': 'XRP',
'quote_currency': 'BTC',
'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"), 'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
'open_timestamp': int(trade.open_date.timestamp() * 1000), 'open_timestamp': int(trade.open_date.timestamp() * 1000),
'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"), 'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"),