Merge branch 'margin-db' into margin-commands
This commit is contained in:
commit
1b82646078
27
TODO
27
TODO
@ -12,6 +12,33 @@ Files to edit
|
|||||||
|
|
||||||
Tests
|
Tests
|
||||||
tests/test_persistence.pys
|
tests/test_persistence.pys
|
||||||
|
init with
|
||||||
|
lev & bor
|
||||||
|
lev
|
||||||
|
bor
|
||||||
|
neither lev nor bor
|
||||||
|
adjust_stop_loss
|
||||||
|
short
|
||||||
|
leverage
|
||||||
|
is_opening_trade
|
||||||
|
short
|
||||||
|
long
|
||||||
|
shortBuy
|
||||||
|
longSell
|
||||||
|
is_closing_trade
|
||||||
|
short
|
||||||
|
long
|
||||||
|
shortBuy
|
||||||
|
longSell
|
||||||
|
update, close, update fee
|
||||||
|
possible to test?
|
||||||
|
calc_profit
|
||||||
|
* * create a few shorts, a few leveraged longs test correct ratio
|
||||||
|
calc_profit_ratio
|
||||||
|
* create a few shorts, a few leveraged longs test correct ratio
|
||||||
|
get_open_trades
|
||||||
|
* create a short, check if exists
|
||||||
|
|
||||||
tests/test_freqtradebot.py
|
tests/test_freqtradebot.py
|
||||||
|
|
||||||
later
|
later
|
||||||
|
@ -60,7 +60,7 @@ OS Specific steps are listed first, the [Common](#common) section below is neces
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
|
|
||||||
# install packages
|
# install packages
|
||||||
sudo apt install -y python3-pip python3-venv python3-pandas git
|
sudo apt install -y python3-pip python3-venv python3-dev python3-pandas git
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "RaspberryPi/Raspbian"
|
=== "RaspberryPi/Raspbian"
|
||||||
|
@ -268,7 +268,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Updating open orders in dry-run does not make sense and will fail.
|
# Updating open orders in dry-run does not make sense and will fail.
|
||||||
return
|
return
|
||||||
|
|
||||||
trades: List[Trade] = Trade.get_sold_trades_without_assigned_fees()
|
trades: List[Trade] = Trade.get_closed_trades_without_assigned_fees()
|
||||||
for trade in trades:
|
for trade in trades:
|
||||||
|
|
||||||
if not trade.is_open and not trade.fee_updated('sell'):
|
if not trade.is_open and not trade.fee_updated('sell'):
|
||||||
|
@ -66,7 +66,8 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
close_profit_abs = get_column_def(
|
close_profit_abs = get_column_def(
|
||||||
cols, 'close_profit_abs',
|
cols, 'close_profit_abs',
|
||||||
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_value}")
|
f"(amount * close_rate * (1 - {fee_close})) - {open_trade_value}")
|
||||||
close_order_status = get_column_def(cols, 'close_order_status', 'null')
|
# TODO-mg: update to exit order status
|
||||||
|
sell_order_status = get_column_def(cols, 'sell_order_status', 'null')
|
||||||
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
|
amount_requested = get_column_def(cols, 'amount_requested', 'amount')
|
||||||
|
|
||||||
# Schema migration necessary
|
# Schema migration necessary
|
||||||
@ -88,7 +89,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
|
stake_amount, amount, amount_requested, open_date, close_date, open_order_id,
|
||||||
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
stop_loss, stop_loss_pct, initial_stop_loss, initial_stop_loss_pct,
|
||||||
stoploss_order_id, stoploss_last_update,
|
stoploss_order_id, stoploss_last_update,
|
||||||
max_rate, min_rate, sell_reason, close_order_status, strategy,
|
max_rate, min_rate, sell_reason, sell_order_status, strategy,
|
||||||
timeframe, open_trade_value, close_profit_abs,
|
timeframe, open_trade_value, close_profit_abs,
|
||||||
leverage, borrowed, borrowed_currency, collateral_currency, interest_rate, liquidation_price, is_short
|
leverage, borrowed, borrowed_currency, collateral_currency, interest_rate, liquidation_price, is_short
|
||||||
)
|
)
|
||||||
@ -111,7 +112,7 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
{initial_stop_loss_pct} initial_stop_loss_pct,
|
{initial_stop_loss_pct} initial_stop_loss_pct,
|
||||||
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
|
{stoploss_order_id} stoploss_order_id, {stoploss_last_update} stoploss_last_update,
|
||||||
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
{max_rate} max_rate, {min_rate} min_rate, {sell_reason} sell_reason,
|
||||||
{close_order_status} close_order_status,
|
{sell_order_status} sell_order_status,
|
||||||
{strategy} strategy, {timeframe} timeframe,
|
{strategy} strategy, {timeframe} timeframe,
|
||||||
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
|
{open_trade_value} open_trade_value, {close_profit_abs} close_profit_abs,
|
||||||
{leverage} leverage, {borrowed} borrowed, {borrowed_currency} borrowed_currency,
|
{leverage} leverage, {borrowed} borrowed, {borrowed_currency} borrowed_currency,
|
||||||
@ -120,7 +121,9 @@ def migrate_trades_table(decl_base, inspector, engine, table_back_name: str, col
|
|||||||
from {table_back_name}
|
from {table_back_name}
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
#TODO: Does leverage go in here?
|
# TODO: Does leverage go in here?
|
||||||
|
|
||||||
|
|
||||||
def migrate_open_orders_to_trades(engine):
|
def migrate_open_orders_to_trades(engine):
|
||||||
with engine.begin() as connection:
|
with engine.begin() as connection:
|
||||||
connection.execute(text("""
|
connection.execute(text("""
|
||||||
|
@ -132,7 +132,7 @@ class Order(_DECL_BASE):
|
|||||||
order_filled_date = Column(DateTime, nullable=True)
|
order_filled_date = Column(DateTime, nullable=True)
|
||||||
order_update_date = Column(DateTime, nullable=True)
|
order_update_date = Column(DateTime, nullable=True)
|
||||||
|
|
||||||
leverage = Column(Float, nullable=True, default=0.0)
|
leverage = Column(Float, nullable=True, default=1.0)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, '
|
return (f'Order(id={self.id}, order_id={self.order_id}, trade_id={self.ft_trade_id}, '
|
||||||
@ -258,12 +258,12 @@ class LocalTrade():
|
|||||||
# Lowest price reached
|
# Lowest price reached
|
||||||
min_rate: float = 0.0
|
min_rate: float = 0.0
|
||||||
sell_reason: str = ''
|
sell_reason: str = ''
|
||||||
close_order_status: str = ''
|
sell_order_status: str = ''
|
||||||
strategy: str = ''
|
strategy: str = ''
|
||||||
timeframe: Optional[int] = None
|
timeframe: Optional[int] = None
|
||||||
|
|
||||||
# Margin trading properties
|
# Margin trading properties
|
||||||
leverage: Optional[float] = 0.0
|
leverage: Optional[float] = 1.0
|
||||||
borrowed: float = 0.0
|
borrowed: float = 0.0
|
||||||
borrowed_currency: str = None
|
borrowed_currency: str = None
|
||||||
collateral_currency: str = None
|
collateral_currency: str = None
|
||||||
@ -287,6 +287,8 @@ class LocalTrade():
|
|||||||
|
|
||||||
for key in kwargs:
|
for key in kwargs:
|
||||||
setattr(self, key, kwargs[key])
|
setattr(self, key, kwargs[key])
|
||||||
|
if not self.is_short:
|
||||||
|
self.is_short = False
|
||||||
self.recalc_open_trade_value()
|
self.recalc_open_trade_value()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -348,7 +350,7 @@ class LocalTrade():
|
|||||||
'profit_abs': self.close_profit_abs,
|
'profit_abs': self.close_profit_abs,
|
||||||
|
|
||||||
'sell_reason': self.sell_reason,
|
'sell_reason': self.sell_reason,
|
||||||
'close_order_status': self.close_order_status,
|
'sell_order_status': self.sell_order_status,
|
||||||
'stop_loss_abs': self.stop_loss,
|
'stop_loss_abs': self.stop_loss,
|
||||||
'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None,
|
'stop_loss_ratio': self.stop_loss_pct if self.stop_loss_pct else None,
|
||||||
'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None,
|
'stop_loss_pct': (self.stop_loss_pct * 100) if self.stop_loss_pct else None,
|
||||||
@ -371,7 +373,7 @@ class LocalTrade():
|
|||||||
'collateral_currency': self.collateral_currency,
|
'collateral_currency': self.collateral_currency,
|
||||||
'interest_rate': self.interest_rate,
|
'interest_rate': self.interest_rate,
|
||||||
'liquidation_price': self.liquidation_price,
|
'liquidation_price': self.liquidation_price,
|
||||||
'leverage': self.leverage,
|
'is_short': self.is_short,
|
||||||
|
|
||||||
'open_order_id': self.open_order_id,
|
'open_order_id': self.open_order_id,
|
||||||
}
|
}
|
||||||
@ -474,12 +476,12 @@ class LocalTrade():
|
|||||||
self.recalc_open_trade_value()
|
self.recalc_open_trade_value()
|
||||||
if self.is_open:
|
if self.is_open:
|
||||||
payment = "SELL" if self.is_short else "BUY"
|
payment = "SELL" if self.is_short else "BUY"
|
||||||
logger.info(f'{order_type.upper()}_{payment} order has been fulfilled for {self}.')
|
logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.')
|
||||||
self.open_order_id = None
|
self.open_order_id = None
|
||||||
elif order_type in ('market', 'limit') and self.is_closing_trade(order['side']):
|
elif order_type in ('market', 'limit') and self.is_closing_trade(order['side']):
|
||||||
if self.is_open:
|
if self.is_open:
|
||||||
payment = "BUY" if self.is_short else "SELL"
|
payment = "BUY" if self.is_short else "SELL"
|
||||||
logger.info(f'{order_type.upper()}_{payment} order has been fulfilled for {self}.')
|
logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.')
|
||||||
self.close(safe_value_fallback(order, 'average', 'price')) # TODO: Double check this
|
self.close(safe_value_fallback(order, 'average', 'price')) # TODO: Double check this
|
||||||
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
|
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
|
||||||
self.stoploss_order_id = None
|
self.stoploss_order_id = None
|
||||||
@ -502,7 +504,7 @@ class LocalTrade():
|
|||||||
self.close_profit = self.calc_profit_ratio()
|
self.close_profit = self.calc_profit_ratio()
|
||||||
self.close_profit_abs = self.calc_profit()
|
self.close_profit_abs = self.calc_profit()
|
||||||
self.is_open = False
|
self.is_open = False
|
||||||
self.close_order_status = 'closed'
|
self.sell_order_status = 'closed'
|
||||||
self.open_order_id = None
|
self.open_order_id = None
|
||||||
if show_msg:
|
if show_msg:
|
||||||
logger.info(
|
logger.info(
|
||||||
@ -561,6 +563,42 @@ class LocalTrade():
|
|||||||
"""
|
"""
|
||||||
self.open_trade_value = self._calc_open_trade_value()
|
self.open_trade_value = self._calc_open_trade_value()
|
||||||
|
|
||||||
|
def calculate_interest(self) -> Decimal:
|
||||||
|
# TODO-mg: Need to set other conditions because sometimes self.open_date is not defined, but why would it ever not be set
|
||||||
|
if not self.interest_rate or not (self.borrowed):
|
||||||
|
return Decimal(0.0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
open_date = self.open_date.replace(tzinfo=None)
|
||||||
|
now = datetime.now()
|
||||||
|
secPerDay = 86400
|
||||||
|
days = Decimal((now - open_date).total_seconds()/secPerDay) or 0.0
|
||||||
|
hours = days/24
|
||||||
|
except:
|
||||||
|
raise OperationalException("Time isn't calculated properly")
|
||||||
|
|
||||||
|
rate = Decimal(self.interest_rate)
|
||||||
|
borrowed = Decimal(self.borrowed)
|
||||||
|
|
||||||
|
if self.exchange == 'binance':
|
||||||
|
# Rate is per day but accrued hourly or something
|
||||||
|
# binance: https://www.binance.com/en-AU/support/faq/360030157812
|
||||||
|
return borrowed * (rate/24) * max(hours, 1.0) # TODO-mg: Is hours rounded?
|
||||||
|
elif self.exchange == 'kraken':
|
||||||
|
# https://support.kraken.com/hc/en-us/articles/206161568-What-are-the-fees-for-margin-trading-
|
||||||
|
opening_fee = borrowed * rate
|
||||||
|
roll_over_fee = borrowed * rate * max(0, (hours-4)/4)
|
||||||
|
return opening_fee + roll_over_fee
|
||||||
|
elif self.exchange == 'binance_usdm_futures':
|
||||||
|
# ! TODO-mg: This is incorrect, I didn't look it up
|
||||||
|
return borrowed * (rate/24) * max(hours, 1.0)
|
||||||
|
elif self.exchange == 'binance_coinm_futures':
|
||||||
|
# ! TODO-mg: This is incorrect, I didn't look it up
|
||||||
|
return borrowed * (rate/24) * max(hours, 1.0)
|
||||||
|
else:
|
||||||
|
# TODO-mg: make sure this breaks and can't be squelched
|
||||||
|
raise OperationalException("Leverage not available on this exchange")
|
||||||
|
|
||||||
def calc_close_trade_value(self, rate: Optional[float] = None,
|
def calc_close_trade_value(self, rate: Optional[float] = None,
|
||||||
fee: Optional[float] = None) -> float:
|
fee: Optional[float] = None) -> float:
|
||||||
"""
|
"""
|
||||||
@ -576,8 +614,8 @@ class LocalTrade():
|
|||||||
|
|
||||||
close_trade = Decimal(self.amount) * Decimal(rate or self.close_rate) # type: ignore
|
close_trade = Decimal(self.amount) * Decimal(rate or self.close_rate) # type: ignore
|
||||||
fees = close_trade * Decimal(fee or self.fee_close)
|
fees = close_trade * Decimal(fee or self.fee_close)
|
||||||
#TODO: Interest rate could be hourly instead of daily
|
interest = self.calculate_interest()
|
||||||
interest = ((Decimal(self.interest_rate) * Decimal(self.borrowed)) * Decimal((datetime.utcnow() - self.open_date).days)) or 0 # Interest/day * num of days
|
|
||||||
if (self.is_short):
|
if (self.is_short):
|
||||||
return float(close_trade + fees + interest)
|
return float(close_trade + fees + interest)
|
||||||
else:
|
else:
|
||||||
@ -617,12 +655,17 @@ class LocalTrade():
|
|||||||
rate=(rate or self.close_rate),
|
rate=(rate or self.close_rate),
|
||||||
fee=(fee or self.fee_close)
|
fee=(fee or self.fee_close)
|
||||||
)
|
)
|
||||||
if self.open_trade_value == 0.0:
|
|
||||||
return 0.0
|
|
||||||
if self.is_short:
|
if self.is_short:
|
||||||
profit_ratio = (close_trade_value / self.open_trade_value) - 1
|
if close_trade_value == 0.0:
|
||||||
|
return 0.0
|
||||||
|
else:
|
||||||
|
profit_ratio = (self.open_trade_value / close_trade_value) - 1
|
||||||
|
|
||||||
else:
|
else:
|
||||||
profit_ratio = (self.open_trade_value / close_trade_value) - 1
|
if self.open_trade_value == 0.0:
|
||||||
|
return 0.0
|
||||||
|
else:
|
||||||
|
profit_ratio = (close_trade_value / self.open_trade_value) - 1
|
||||||
return float(f"{profit_ratio:.8f}")
|
return float(f"{profit_ratio:.8f}")
|
||||||
|
|
||||||
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
|
def select_order(self, order_side: str, is_open: Optional[bool]) -> Optional[Order]:
|
||||||
@ -640,7 +683,7 @@ class LocalTrade():
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
|
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
|
||||||
open_date: datetime = None, close_date: datetime = None,
|
open_date: datetime = None, close_date: datetime = None,
|
||||||
) -> List['LocalTrade']:
|
) -> List['LocalTrade']:
|
||||||
@ -674,27 +717,27 @@ class LocalTrade():
|
|||||||
|
|
||||||
return sel_trades
|
return sel_trades
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def close_bt_trade(trade):
|
def close_bt_trade(trade):
|
||||||
LocalTrade.trades_open.remove(trade)
|
LocalTrade.trades_open.remove(trade)
|
||||||
LocalTrade.trades.append(trade)
|
LocalTrade.trades.append(trade)
|
||||||
LocalTrade.total_profit += trade.close_profit_abs
|
LocalTrade.total_profit += trade.close_profit_abs
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def add_bt_trade(trade):
|
def add_bt_trade(trade):
|
||||||
if trade.is_open:
|
if trade.is_open:
|
||||||
LocalTrade.trades_open.append(trade)
|
LocalTrade.trades_open.append(trade)
|
||||||
else:
|
else:
|
||||||
LocalTrade.trades.append(trade)
|
LocalTrade.trades.append(trade)
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_open_trades() -> List[Any]:
|
def get_open_trades() -> List[Any]:
|
||||||
"""
|
"""
|
||||||
Query trades from persistence layer
|
Query trades from persistence layer
|
||||||
"""
|
"""
|
||||||
return Trade.get_trades_proxy(is_open=True)
|
return Trade.get_trades_proxy(is_open=True)
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def stoploss_reinitialization(desired_stoploss):
|
def stoploss_reinitialization(desired_stoploss):
|
||||||
"""
|
"""
|
||||||
Adjust initial Stoploss to desired stoploss for all open trades.
|
Adjust initial Stoploss to desired stoploss for all open trades.
|
||||||
@ -768,13 +811,13 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
max_rate = Column(Float, nullable=True, default=0.0)
|
max_rate = Column(Float, nullable=True, default=0.0)
|
||||||
# Lowest price reached
|
# Lowest price reached
|
||||||
min_rate = Column(Float, nullable=True)
|
min_rate = Column(Float, nullable=True)
|
||||||
sell_reason = Column(String(100), nullable=True) #TODO: Change to close_reason
|
sell_reason = Column(String(100), nullable=True) # TODO: Change to close_reason
|
||||||
close_order_status = Column(String(100), nullable=True)
|
sell_order_status = Column(String(100), nullable=True)
|
||||||
strategy = Column(String(100), nullable=True)
|
strategy = Column(String(100), nullable=True)
|
||||||
timeframe = Column(Integer, nullable=True)
|
timeframe = Column(Integer, nullable=True)
|
||||||
|
|
||||||
# Margin trading properties
|
# Margin trading properties
|
||||||
leverage = Column(Float, nullable=True, default=0.0)
|
leverage = Column(Float, nullable=True, default=1.0)
|
||||||
borrowed = Column(Float, nullable=False, default=0.0)
|
borrowed = Column(Float, nullable=False, default=0.0)
|
||||||
borrowed_currency = Column(Float, nullable=True)
|
borrowed_currency = Column(Float, nullable=True)
|
||||||
collateral_currency = Column(String(25), nullable=True)
|
collateral_currency = Column(String(25), nullable=True)
|
||||||
@ -795,11 +838,11 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
Trade.query.session.delete(self)
|
Trade.query.session.delete(self)
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def commit():
|
def commit():
|
||||||
Trade.query.session.commit()
|
Trade.query.session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
|
def get_trades_proxy(*, pair: str = None, is_open: bool = None,
|
||||||
open_date: datetime = None, close_date: datetime = None,
|
open_date: datetime = None, close_date: datetime = None,
|
||||||
) -> List['LocalTrade']:
|
) -> List['LocalTrade']:
|
||||||
@ -829,7 +872,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
close_date=close_date
|
close_date=close_date
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_trades(trade_filter=None) -> Query:
|
def get_trades(trade_filter=None) -> Query:
|
||||||
"""
|
"""
|
||||||
Helper function to query Trades using filters.
|
Helper function to query Trades using filters.
|
||||||
@ -849,7 +892,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
else:
|
else:
|
||||||
return Trade.query
|
return Trade.query
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_open_order_trades():
|
def get_open_order_trades():
|
||||||
"""
|
"""
|
||||||
Returns all open trades
|
Returns all open trades
|
||||||
@ -857,7 +900,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
"""
|
"""
|
||||||
return Trade.get_trades(Trade.open_order_id.isnot(None)).all()
|
return Trade.get_trades(Trade.open_order_id.isnot(None)).all()
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_open_trades_without_assigned_fees():
|
def get_open_trades_without_assigned_fees():
|
||||||
"""
|
"""
|
||||||
Returns all open trades which don't have open fees set correctly
|
Returns all open trades which don't have open fees set correctly
|
||||||
@ -868,7 +911,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
Trade.is_open.is_(True),
|
Trade.is_open.is_(True),
|
||||||
]).all()
|
]).all()
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_closed_trades_without_assigned_fees():
|
def get_closed_trades_without_assigned_fees():
|
||||||
"""
|
"""
|
||||||
Returns all closed trades which don't have fees set correctly
|
Returns all closed trades which don't have fees set correctly
|
||||||
@ -879,7 +922,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
Trade.is_open.is_(False),
|
Trade.is_open.is_(False),
|
||||||
]).all()
|
]).all()
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def total_open_trades_stakes() -> float:
|
def total_open_trades_stakes() -> float:
|
||||||
"""
|
"""
|
||||||
Calculates total invested amount in open trades
|
Calculates total invested amount in open trades
|
||||||
@ -893,7 +936,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True))
|
t.stake_amount for t in LocalTrade.get_trades_proxy(is_open=True))
|
||||||
return total_open_stake_amount or 0
|
return total_open_stake_amount or 0
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_overall_performance() -> List[Dict[str, Any]]:
|
def get_overall_performance() -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Returns List of dicts containing all Trades, including profit and trade count
|
Returns List of dicts containing all Trades, including profit and trade count
|
||||||
@ -918,7 +961,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
|||||||
for pair, profit, profit_abs, count in pair_rates
|
for pair, profit, profit_abs, count in pair_rates
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def get_best_pair():
|
def get_best_pair():
|
||||||
"""
|
"""
|
||||||
Get best pair with closed trade.
|
Get best pair with closed trade.
|
||||||
@ -956,7 +999,7 @@ class PairLock(_DECL_BASE):
|
|||||||
return (f'PairLock(id={self.id}, pair={self.pair}, lock_time={lock_time}, '
|
return (f'PairLock(id={self.id}, pair={self.pair}, lock_time={lock_time}, '
|
||||||
f'lock_end_time={lock_end_time})')
|
f'lock_end_time={lock_end_time})')
|
||||||
|
|
||||||
@staticmethod
|
@ staticmethod
|
||||||
def query_pair_locks(pair: Optional[str], now: datetime) -> Query:
|
def query_pair_locks(pair: Optional[str], now: datetime) -> Query:
|
||||||
"""
|
"""
|
||||||
Get all currently active locks for this pair
|
Get all currently active locks for this pair
|
||||||
|
@ -19,7 +19,7 @@ isort==5.8.0
|
|||||||
nbconvert==6.0.7
|
nbconvert==6.0.7
|
||||||
|
|
||||||
# mypy types
|
# mypy types
|
||||||
types-cachetools==0.1.7
|
types-cachetools==0.1.8
|
||||||
types-filelock==0.1.3
|
types-filelock==0.1.4
|
||||||
types-requests==0.1.11
|
types-requests==0.1.13
|
||||||
types-tabulate==0.1.0
|
types-tabulate==0.1.1
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
-r requirements.txt
|
-r requirements.txt
|
||||||
|
|
||||||
# Required for hyperopt
|
# Required for hyperopt
|
||||||
scipy==1.6.3
|
scipy==1.7.0
|
||||||
scikit-learn==0.24.2
|
scikit-learn==0.24.2
|
||||||
scikit-optimize==0.8.1
|
scikit-optimize==0.8.1
|
||||||
filelock==3.0.12
|
filelock==3.0.12
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
numpy==1.20.3
|
numpy==1.20.3
|
||||||
pandas==1.2.4
|
pandas==1.2.4
|
||||||
|
|
||||||
ccxt==1.51.40
|
ccxt==1.51.77
|
||||||
# Pin cryptography for now due to rust build errors with piwheels
|
# Pin cryptography for now due to rust build errors with piwheels
|
||||||
cryptography==3.4.7
|
cryptography==3.4.7
|
||||||
aiohttp==3.7.4.post0
|
aiohttp==3.7.4.post0
|
||||||
@ -16,7 +16,7 @@ jsonschema==3.2.0
|
|||||||
TA-Lib==0.4.20
|
TA-Lib==0.4.20
|
||||||
technical==1.3.0
|
technical==1.3.0
|
||||||
tabulate==0.8.9
|
tabulate==0.8.9
|
||||||
pycoingecko==2.1.0
|
pycoingecko==2.2.0
|
||||||
jinja2==3.0.1
|
jinja2==3.0.1
|
||||||
tables==3.6.1
|
tables==3.6.1
|
||||||
blosc==1.10.4
|
blosc==1.10.4
|
||||||
@ -40,4 +40,4 @@ aiofiles==0.7.0
|
|||||||
colorama==0.4.4
|
colorama==0.4.4
|
||||||
# Building config files interactively
|
# Building config files interactively
|
||||||
questionary==1.9.0
|
questionary==1.9.0
|
||||||
prompt-toolkit==3.0.18
|
prompt-toolkit==3.0.19
|
||||||
|
@ -24,7 +24,7 @@ from freqtrade.persistence import LocalTrade, Trade, init_db
|
|||||||
from freqtrade.resolvers import ExchangeResolver
|
from freqtrade.resolvers import ExchangeResolver
|
||||||
from freqtrade.worker import Worker
|
from freqtrade.worker import Worker
|
||||||
from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4,
|
from tests.conftest_trades import (mock_trade_1, mock_trade_2, mock_trade_3, mock_trade_4,
|
||||||
mock_trade_5, mock_trade_6)
|
mock_trade_5, mock_trade_6, short_trade, leverage_trade)
|
||||||
|
|
||||||
|
|
||||||
logging.getLogger('').setLevel(logging.INFO)
|
logging.getLogger('').setLevel(logging.INFO)
|
||||||
@ -221,6 +221,13 @@ def create_mock_trades(fee, use_db: bool = True):
|
|||||||
trade = mock_trade_6(fee)
|
trade = mock_trade_6(fee)
|
||||||
add_trade(trade)
|
add_trade(trade)
|
||||||
|
|
||||||
|
# TODO: margin trades
|
||||||
|
# trade = short_trade(fee)
|
||||||
|
# add_trade(trade)
|
||||||
|
|
||||||
|
# trade = leverage_trade(fee)
|
||||||
|
# add_trade(trade)
|
||||||
|
|
||||||
if use_db:
|
if use_db:
|
||||||
Trade.query.session.flush()
|
Trade.query.session.flush()
|
||||||
|
|
||||||
@ -250,6 +257,7 @@ def patch_coingekko(mocker) -> None:
|
|||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def init_persistence(default_conf):
|
def init_persistence(default_conf):
|
||||||
init_db(default_conf['db_url'], default_conf['dry_run'])
|
init_db(default_conf['db_url'], default_conf['dry_run'])
|
||||||
|
# TODO-mg: trade with leverage and/or borrowed?
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
@ -812,7 +820,7 @@ def shitcoinmarkets(markets):
|
|||||||
"future": False,
|
"future": False,
|
||||||
"active": True
|
"active": True
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return shitmarkets
|
return shitmarkets
|
||||||
|
|
||||||
|
|
||||||
@ -914,18 +922,17 @@ def limit_sell_order_old():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def limit_buy_order_old_partial():
|
def limit_buy_order_old_partial():
|
||||||
return {
|
return {'id': 'mocked_limit_buy_old_partial',
|
||||||
'id': 'mocked_limit_buy_old_partial',
|
'type': 'limit',
|
||||||
'type': 'limit',
|
'side': 'buy',
|
||||||
'side': 'buy',
|
'symbol': 'ETH/BTC',
|
||||||
'symbol': 'ETH/BTC',
|
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
||||||
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
|
'price': 0.00001099,
|
||||||
'price': 0.00001099,
|
'amount': 90.99181073,
|
||||||
'amount': 90.99181073,
|
'filled': 23.0,
|
||||||
'filled': 23.0,
|
'remaining': 67.99181073,
|
||||||
'remaining': 67.99181073,
|
'status': 'open'
|
||||||
'status': 'open'
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -1728,13 +1735,14 @@ def rpc_balance():
|
|||||||
'total': 0.1,
|
'total': 0.1,
|
||||||
'free': 0.01,
|
'free': 0.01,
|
||||||
'used': 0.0
|
'used': 0.0
|
||||||
},
|
},
|
||||||
'EUR': {
|
'EUR': {
|
||||||
'total': 10.0,
|
'total': 10.0,
|
||||||
'free': 10.0,
|
'free': 10.0,
|
||||||
'used': 0.0
|
'used': 0.0
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
# TODO-mg: Add shorts and leverage?
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -2049,3 +2057,96 @@ def saved_hyperopt_results():
|
|||||||
].total_seconds()
|
].total_seconds()
|
||||||
|
|
||||||
return hyperopt_res
|
return hyperopt_res
|
||||||
|
|
||||||
|
|
||||||
|
# * Margin Tests
|
||||||
|
# TODO-mg: fill in these tests with something useful
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def leveraged_fee():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def short_fee():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ticker_short():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ticker_exit_short_up():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ticker_exit_short_down():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def leveraged_markets():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def limit_short_order_open():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def limit_short_order(limit_short_order_open):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def market_short_order():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def market_short_exit_order():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_short_order_old():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_exit_short_order_old():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_short_order_old_partial():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_short_order_old_partial_canceled(limit_short_order_old_partial):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='function')
|
||||||
|
def limit_short_order_canceled_empty(request):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_exit_short_order_open():
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limit_exit_short_order(limit_sell_order_open):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def short_order_fee():
|
||||||
|
return
|
||||||
|
@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone
|
|||||||
from freqtrade.persistence.models import Order, Trade
|
from freqtrade.persistence.models import Order, Trade
|
||||||
|
|
||||||
|
|
||||||
MOCK_TRADE_COUNT = 6
|
MOCK_TRADE_COUNT = 6 # TODO-mg: Increase for short and leverage
|
||||||
|
|
||||||
|
|
||||||
def mock_order_1():
|
def mock_order_1():
|
||||||
@ -303,3 +303,134 @@ def mock_trade_6(fee):
|
|||||||
o = Order.parse_from_ccxt_object(mock_order_6_sell(), 'LTC/BTC', 'sell')
|
o = Order.parse_from_ccxt_object(mock_order_6_sell(), 'LTC/BTC', 'sell')
|
||||||
trade.orders.append(o)
|
trade.orders.append(o)
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
|
|
||||||
|
#! TODO Currently the following short_trade test and leverage_trade test will fail
|
||||||
|
|
||||||
|
|
||||||
|
def short_order():
|
||||||
|
return {
|
||||||
|
'id': '1235',
|
||||||
|
'symbol': 'ETC/BTC',
|
||||||
|
'status': 'closed',
|
||||||
|
'side': 'sell',
|
||||||
|
'type': 'limit',
|
||||||
|
'price': 0.123,
|
||||||
|
'amount': 123.0,
|
||||||
|
'filled': 123.0,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'leverage': 5.0,
|
||||||
|
'isShort': True
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def exit_short_order():
|
||||||
|
return {
|
||||||
|
'id': '12366',
|
||||||
|
'symbol': 'ETC/BTC',
|
||||||
|
'status': 'closed',
|
||||||
|
'side': 'buy',
|
||||||
|
'type': 'limit',
|
||||||
|
'price': 0.128,
|
||||||
|
'amount': 123.0,
|
||||||
|
'filled': 123.0,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'leverage': 5.0,
|
||||||
|
'isShort': True
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def short_trade(fee):
|
||||||
|
"""
|
||||||
|
Closed trade...
|
||||||
|
"""
|
||||||
|
trade = Trade(
|
||||||
|
pair='ETC/BTC',
|
||||||
|
stake_amount=0.001,
|
||||||
|
amount=123.0, # TODO-mg: In BTC?
|
||||||
|
amount_requested=123.0,
|
||||||
|
fee_open=fee.return_value,
|
||||||
|
fee_close=fee.return_value,
|
||||||
|
open_rate=0.123,
|
||||||
|
close_rate=0.128,
|
||||||
|
close_profit=0.005, # TODO-mg: Would this be -0.005 or -0.025
|
||||||
|
close_profit_abs=0.000584127,
|
||||||
|
exchange='binance',
|
||||||
|
is_open=False,
|
||||||
|
open_order_id='dry_run_exit_short_12345',
|
||||||
|
strategy='DefaultStrategy',
|
||||||
|
timeframe=5,
|
||||||
|
sell_reason='sell_signal', # TODO-mg: Update to exit/close reason
|
||||||
|
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
|
||||||
|
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
|
||||||
|
# borrowed=
|
||||||
|
isShort=True
|
||||||
|
)
|
||||||
|
o = Order.parse_from_ccxt_object(short_order(), 'ETC/BTC', 'sell')
|
||||||
|
trade.orders.append(o)
|
||||||
|
o = Order.parse_from_ccxt_object(exit_short_order(), 'ETC/BTC', 'sell')
|
||||||
|
trade.orders.append(o)
|
||||||
|
return trade
|
||||||
|
|
||||||
|
|
||||||
|
def leverage_order():
|
||||||
|
return {
|
||||||
|
'id': '1235',
|
||||||
|
'symbol': 'ETC/BTC',
|
||||||
|
'status': 'closed',
|
||||||
|
'side': 'buy',
|
||||||
|
'type': 'limit',
|
||||||
|
'price': 0.123,
|
||||||
|
'amount': 123.0,
|
||||||
|
'filled': 123.0,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'leverage': 5.0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def leverage_order_sell():
|
||||||
|
return {
|
||||||
|
'id': '12366',
|
||||||
|
'symbol': 'ETC/BTC',
|
||||||
|
'status': 'closed',
|
||||||
|
'side': 'sell',
|
||||||
|
'type': 'limit',
|
||||||
|
'price': 0.128,
|
||||||
|
'amount': 123.0,
|
||||||
|
'filled': 123.0,
|
||||||
|
'remaining': 0.0,
|
||||||
|
'leverage': 5.0,
|
||||||
|
'isShort': True
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def leverage_trade(fee):
|
||||||
|
"""
|
||||||
|
Closed trade...
|
||||||
|
"""
|
||||||
|
trade = Trade(
|
||||||
|
pair='ETC/BTC',
|
||||||
|
stake_amount=0.001,
|
||||||
|
amount=615.0,
|
||||||
|
amount_requested=615.0,
|
||||||
|
fee_open=fee.return_value,
|
||||||
|
fee_close=fee.return_value,
|
||||||
|
open_rate=0.123,
|
||||||
|
close_rate=0.128,
|
||||||
|
close_profit=0.005, # TODO-mg: Would this be -0.005 or -0.025
|
||||||
|
close_profit_abs=0.000584127,
|
||||||
|
exchange='binance',
|
||||||
|
is_open=False,
|
||||||
|
open_order_id='dry_run_leverage_sell_12345',
|
||||||
|
strategy='DefaultStrategy',
|
||||||
|
timeframe=5,
|
||||||
|
sell_reason='sell_signal', # TODO-mg: Update to exit/close reason
|
||||||
|
open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20),
|
||||||
|
close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2),
|
||||||
|
# borrowed=
|
||||||
|
)
|
||||||
|
o = Order.parse_from_ccxt_object(leverage_order(), 'ETC/BTC', 'sell')
|
||||||
|
trade.orders.append(o)
|
||||||
|
o = Order.parse_from_ccxt_object(leverage_order_sell(), 'ETC/BTC', 'sell')
|
||||||
|
trade.orders.append(o)
|
||||||
|
return trade
|
||||||
|
@ -107,6 +107,14 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'stoploss_entry_dist_ratio': -0.10448878,
|
'stoploss_entry_dist_ratio': -0.10448878,
|
||||||
'open_order': None,
|
'open_order': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': 1.0,
|
||||||
|
'borrowed': 0.0,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': 0.0,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': False,
|
||||||
}
|
}
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate',
|
||||||
@ -173,6 +181,15 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'stoploss_entry_dist_ratio': -0.10448878,
|
'stoploss_entry_dist_ratio': -0.10448878,
|
||||||
'open_order': None,
|
'open_order': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': 1.0,
|
||||||
|
'borrowed': 0.0,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': 0.0,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': False,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -129,6 +129,9 @@ def test_update_with_binance(limit_buy_order, limit_sell_order, fee, caplog):
|
|||||||
r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).",
|
r"pair=ETH/BTC, amount=90.99181073, open_rate=0.00001099, open_since=.*\).",
|
||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
|
# TODO-mg: create a short order
|
||||||
|
# TODO-mg: create a leveraged long order
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
|
def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
|
||||||
@ -167,6 +170,9 @@ def test_update_market_order(market_buy_order, market_sell_order, fee, caplog):
|
|||||||
r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).",
|
r"pair=ETH/BTC, amount=91.99181073, open_rate=0.00004099, open_since=.*\).",
|
||||||
caplog)
|
caplog)
|
||||||
|
|
||||||
|
# TODO-mg: market short
|
||||||
|
# TODO-mg: market leveraged long
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("init_persistence")
|
@pytest.mark.usefixtures("init_persistence")
|
||||||
def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
|
def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
|
||||||
@ -659,11 +665,13 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
|||||||
order_date DATETIME,
|
order_date DATETIME,
|
||||||
order_filled_date DATETIME,
|
order_filled_date DATETIME,
|
||||||
order_update_date DATETIME,
|
order_update_date DATETIME,
|
||||||
|
leverage FLOAT,
|
||||||
PRIMARY KEY (id),
|
PRIMARY KEY (id),
|
||||||
CONSTRAINT _order_pair_order_id UNIQUE (ft_pair, order_id),
|
CONSTRAINT _order_pair_order_id UNIQUE (ft_pair, order_id),
|
||||||
FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
|
FOREIGN KEY(ft_trade_id) REFERENCES trades (id)
|
||||||
)
|
)
|
||||||
"""))
|
"""))
|
||||||
|
# TODO-mg: Had to add field leverage to this table, check that this is correct
|
||||||
|
|
||||||
connection.execute(text("""
|
connection.execute(text("""
|
||||||
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
|
insert into orders ( id, ft_trade_id, ft_order_side, ft_pair, ft_is_open, order_id, status,
|
||||||
@ -912,6 +920,14 @@ def test_to_json(default_conf, fee):
|
|||||||
'strategy': None,
|
'strategy': None,
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': None,
|
||||||
|
'borrowed': None,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': None,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Simulate dry_run entries
|
# Simulate dry_run entries
|
||||||
@ -977,6 +993,14 @@ def test_to_json(default_conf, fee):
|
|||||||
'strategy': None,
|
'strategy': None,
|
||||||
'timeframe': None,
|
'timeframe': None,
|
||||||
'exchange': 'binance',
|
'exchange': 'binance',
|
||||||
|
|
||||||
|
'leverage': None,
|
||||||
|
'borrowed': None,
|
||||||
|
'borrowed_currency': None,
|
||||||
|
'collateral_currency': None,
|
||||||
|
'interest_rate': None,
|
||||||
|
'liquidation_price': None,
|
||||||
|
'is_short': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1299,11 +1323,11 @@ def test_Trade_object_idem():
|
|||||||
'get_best_pair',
|
'get_best_pair',
|
||||||
'get_overall_performance',
|
'get_overall_performance',
|
||||||
'total_open_trades_stakes',
|
'total_open_trades_stakes',
|
||||||
'get_sold_trades_without_assigned_fees',
|
'get_closed_trades_without_assigned_fees',
|
||||||
'get_open_trades_without_assigned_fees',
|
'get_open_trades_without_assigned_fees',
|
||||||
'get_open_order_trades',
|
'get_open_order_trades',
|
||||||
'get_trades',
|
'get_trades',
|
||||||
)
|
)
|
||||||
|
|
||||||
# Parent (LocalTrade) should have the same attributes
|
# Parent (LocalTrade) should have the same attributes
|
||||||
for item in trade:
|
for item in trade:
|
||||||
|
Loading…
Reference in New Issue
Block a user