Merge pull request #6670 from freqtrade/store_asset
store base and quote currency separately in the database
This commit is contained in:
commit
46c18dfce2
@ -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:
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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.')
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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"),
|
||||||
|
Loading…
Reference in New Issue
Block a user