Some code improvements. Still some bugs.
This commit is contained in:
parent
88792852e4
commit
fde6779873
@ -450,98 +450,84 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
#
|
#
|
||||||
# BUY / increase / decrease positions / DCA logic and methods
|
# BUY / increase / decrease positions / DCA logic and methods
|
||||||
#
|
#
|
||||||
def process_open_trade_positions(self) -> int:
|
def process_open_trade_positions(self):
|
||||||
"""
|
"""
|
||||||
Tries to execute additional buy orders for open trades (positions)
|
Tries to execute additional buy or sell orders for open trades (positions)
|
||||||
"""
|
"""
|
||||||
orders_created = 0
|
# Walk through each pair and check if it needs changes
|
||||||
|
|
||||||
# Remove pairs for currently opened trades from the whitelist
|
|
||||||
for trade in Trade.get_open_trades():
|
for trade in Trade.get_open_trades():
|
||||||
try:
|
try:
|
||||||
orders_created += self.adjust_trade_position(trade)
|
self.adjust_trade_position(trade)
|
||||||
except DependencyException as exception:
|
except DependencyException as exception:
|
||||||
logger.warning('Unable to adjust position of trade for %s: %s', trade.pair, exception)
|
logger.warning('Unable to adjust position of trade for %s: %s', trade.pair, exception)
|
||||||
|
|
||||||
if not orders_created:
|
|
||||||
logger.debug("Found no trades to modify. Trying again...")
|
|
||||||
|
|
||||||
return orders_created
|
def adjust_trade_position(self, trade: Trade):
|
||||||
|
|
||||||
|
|
||||||
def adjust_trade_position(self, trade: Trade) -> int:
|
|
||||||
"""
|
"""
|
||||||
Check the implemented trading strategy for adjustment command.
|
Check the implemented trading strategy for adjustment command.
|
||||||
|
If the strategy triggers the adjustment a new buy/sell-order gets issued.
|
||||||
If the pair triggers the adjustment a new buy-order gets issued towards the exchange.
|
|
||||||
Once that completes, the existing trade is modified to match new data.
|
Once that completes, the existing trade is modified to match new data.
|
||||||
:return: True if a order has been created.
|
|
||||||
"""
|
"""
|
||||||
logger.debug(f"adjust_trade_position for pair {trade.pair}")
|
logger.debug(f"adjust_trade_position for pair {trade.pair}")
|
||||||
for order in trade.orders:
|
for order in trade.orders:
|
||||||
if order.ft_is_open:
|
if order.ft_is_open:
|
||||||
logger.debug(f"Order {order} is still open.")
|
logger.debug(f"Order {order} is still open, skipping pair.")
|
||||||
return 0
|
return
|
||||||
|
|
||||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(trade.pair, self.strategy.timeframe)
|
|
||||||
|
|
||||||
sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
|
sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
|
||||||
current_profit = trade.calc_profit_ratio(sell_rate)
|
current_profit = trade.calc_profit_ratio(sell_rate)
|
||||||
amount_to_adjust = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=0.0)(
|
amount_to_adjust = strategy_safe_wrapper(self.strategy.adjust_trade_position, default_retval=None)(
|
||||||
pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc),
|
pair=trade.pair, trade=trade, current_time=datetime.now(timezone.utc),
|
||||||
current_rate=sell_rate, current_profit=current_profit)
|
current_rate=sell_rate, current_profit=current_profit)
|
||||||
|
|
||||||
if amount_to_adjust > 0.0:
|
if amount_to_adjust != None and amount_to_adjust > 0.0:
|
||||||
# We should increase our position
|
# We should increase our position
|
||||||
is_order_success = self.execute_trade_position_change(trade.pair, amount_to_adjust, trade)
|
self.execute_trade_position_change(trade.pair, amount_to_adjust, trade)
|
||||||
if is_order_success:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if amount_to_adjust < 0.0:
|
if amount_to_adjust != None and amount_to_adjust < 0.0:
|
||||||
# We should decrease our position
|
# We should decrease our position
|
||||||
# TODO: Selling part of the trade not implemented yet.
|
# TODO: Selling part of the trade not implemented yet.
|
||||||
return 0
|
return
|
||||||
|
|
||||||
return 0
|
return
|
||||||
|
|
||||||
|
|
||||||
def execute_trade_position_change(self, pair: str, amount: float, trade: Trade) -> bool:
|
def execute_trade_position_change(self, pair: str, amount: float, trade: Trade):
|
||||||
"""
|
"""
|
||||||
Executes a limit buy for the given pair
|
Executes a buy order for the given pair using specific amount
|
||||||
:param pair: pair for which we want to create a LIMIT_BUY
|
:param pair: pair for which we want to create a buy order
|
||||||
:param stake_amount: amount of stake-currency for the pair
|
:param amount: amount of tradable pair to buy
|
||||||
:return: True if a buy order is created, false if it fails.
|
|
||||||
"""
|
"""
|
||||||
time_in_force = self.strategy.order_time_in_force['buy']
|
time_in_force = self.strategy.order_time_in_force['buy']
|
||||||
|
|
||||||
# Calculate price
|
# Calculate price
|
||||||
proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy")
|
proposed_enter_rate = self.exchange.get_rate(pair, refresh=True, side="buy")
|
||||||
enter_limit_requested = self.get_valid_price(proposed_enter_rate, proposed_enter_rate)
|
custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price,
|
||||||
|
default_retval=proposed_enter_rate)(
|
||||||
|
pair=pair, current_time=datetime.now(timezone.utc),
|
||||||
|
proposed_rate=proposed_enter_rate)
|
||||||
|
|
||||||
|
enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate)
|
||||||
|
|
||||||
if not enter_limit_requested:
|
if not enter_limit_requested:
|
||||||
raise PricingError('Could not determine buy price.')
|
raise PricingError('Could not determine buy price.')
|
||||||
|
|
||||||
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested,
|
min_stake_amount = self.exchange.get_min_pair_stake_amount(pair, enter_limit_requested,
|
||||||
self.strategy.stoploss)
|
self.strategy.stoploss)
|
||||||
|
stake_amount = self.wallets.validate_stake_amount(pair, (amount * enter_limit_requested), min_stake_amount)
|
||||||
stake_amount = amount * enter_limit_requested
|
|
||||||
|
|
||||||
stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount)
|
|
||||||
|
|
||||||
logger.error(f'Executing DCA buy: amount={amount}, stake={stake_amount}')
|
|
||||||
|
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
|
logger.info(f'Additional order failed to get stake amount for pair {pair}, amount={amount}, price={enter_limit_requested}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
logger.debug(f'Executing additional order: amount={amount}, stake={stake_amount}, price={enter_limit_requested}')
|
||||||
|
|
||||||
amount = self.exchange.amount_to_precision(pair, amount)
|
amount = self.exchange.amount_to_precision(pair, amount)
|
||||||
order = self.exchange.create_order(pair=pair, ordertype='market', side="buy",
|
order = self.exchange.create_order(pair=pair, ordertype="market", side="buy",
|
||||||
amount=amount, rate=enter_limit_requested,
|
amount=amount, rate=enter_limit_requested,
|
||||||
time_in_force=time_in_force)
|
time_in_force=time_in_force)
|
||||||
order_obj = Order.parse_from_ccxt_object(order, pair, 'buy')
|
order_obj = Order.parse_from_ccxt_object(order, pair, 'buy')
|
||||||
order_id = order['id']
|
|
||||||
order_status = order.get('status', None)
|
order_status = order.get('status', None)
|
||||||
# we assume the order is executed at the price requested
|
|
||||||
enter_limit_filled_price = enter_limit_requested
|
|
||||||
amount_requested = amount
|
|
||||||
|
|
||||||
if order_status == 'expired' or order_status == 'rejected':
|
if order_status == 'expired' or order_status == 'rejected':
|
||||||
order_tif = self.strategy.order_time_in_force['buy']
|
order_tif = self.strategy.order_time_in_force['buy']
|
||||||
@ -563,44 +549,19 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
)
|
)
|
||||||
stake_amount = order['cost']
|
stake_amount = order['cost']
|
||||||
amount = safe_value_fallback(order, 'filled', 'amount')
|
amount = safe_value_fallback(order, 'filled', 'amount')
|
||||||
enter_limit_filled_price = safe_value_fallback(order, 'average', 'price')
|
|
||||||
|
|
||||||
# in case of FOK the order may be filled immediately and fully
|
# in case of FOK the order may be filled immediately and fully
|
||||||
elif order_status == 'closed':
|
elif order_status == 'closed':
|
||||||
stake_amount = order['cost']
|
stake_amount = order['cost']
|
||||||
amount = safe_value_fallback(order, 'filled', 'amount')
|
amount = safe_value_fallback(order, 'filled', 'amount')
|
||||||
enter_limit_filled_price = safe_value_fallback(order, 'average', 'price')
|
|
||||||
|
|
||||||
# Fee is applied only once because we make a LIMIT_BUY but the final trade will apply the sell fee.
|
# Fee is applied only once because we make a LIMIT_BUY but the final trade will apply the sell fee.
|
||||||
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
|
||||||
|
|
||||||
logger.info(f"Trade {pair} DCA order status {order_status}")
|
logger.info(f"Trade {pair} adjustment order status {order_status}")
|
||||||
|
|
||||||
total_amount = 0.0
|
|
||||||
total_stake = 0.0
|
|
||||||
for tempOrder in trade.orders:
|
|
||||||
if tempOrder.ft_is_open:
|
|
||||||
continue
|
|
||||||
if tempOrder.ft_order_side != 'buy':
|
|
||||||
# Skip not buy sides
|
|
||||||
continue
|
|
||||||
|
|
||||||
if tempOrder.status == "closed":
|
|
||||||
tempOrderAmount = safe_value_fallback(order, 'filled', 'amount')
|
|
||||||
total_amount += tempOrderAmount
|
|
||||||
total_stake += tempOrder.average * tempOrderAmount
|
|
||||||
|
|
||||||
total_amount += amount
|
|
||||||
total_stake += stake_amount
|
|
||||||
|
|
||||||
trade.open_rate = total_stake / total_amount
|
|
||||||
|
|
||||||
trade.fee_open += fee
|
|
||||||
trade.stake_amount = total_stake
|
|
||||||
trade.amount = total_amount
|
|
||||||
|
|
||||||
trade.orders.append(order_obj)
|
trade.orders.append(order_obj)
|
||||||
trade.recalc_open_trade_value()
|
trade.recalc_trade_from_orders()
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Updating wallets
|
# Updating wallets
|
||||||
@ -1459,6 +1420,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.warning("Could not update trade amount: %s", exception)
|
logger.warning("Could not update trade amount: %s", exception)
|
||||||
|
|
||||||
trade.update(order)
|
trade.update(order)
|
||||||
|
trade.recalc_trade_from_orders()
|
||||||
Trade.commit()
|
Trade.commit()
|
||||||
|
|
||||||
# Updating wallets when order is closed
|
# Updating wallets when order is closed
|
||||||
|
@ -568,6 +568,26 @@ class LocalTrade():
|
|||||||
profit_ratio = (close_trade_value / self.open_trade_value) - 1
|
profit_ratio = (close_trade_value / self.open_trade_value) - 1
|
||||||
return float(f"{profit_ratio:.8f}")
|
return float(f"{profit_ratio:.8f}")
|
||||||
|
|
||||||
|
|
||||||
|
def recalc_trade_from_orders(self):
|
||||||
|
total_amount = 0.0
|
||||||
|
total_stake = 0.0
|
||||||
|
for temp_order in self.orders:
|
||||||
|
if temp_order.ft_is_open == False and temp_order.status == "closed" and temp_order.ft_order_side == 'buy':
|
||||||
|
tmp_amount = temp_order.amount
|
||||||
|
if temp_order.filled is not None:
|
||||||
|
tmp_amount = temp_order.filled
|
||||||
|
total_amount += tmp_amount
|
||||||
|
total_stake += temp_order.average * tmp_amount
|
||||||
|
|
||||||
|
if total_amount > 0:
|
||||||
|
self.open_rate = total_stake / total_amount
|
||||||
|
self.stake_amount = total_stake
|
||||||
|
self.amount = total_amount
|
||||||
|
self.fee_open_cost = self.fee_open * self.stake_amount
|
||||||
|
self.recalc_open_trade_value()
|
||||||
|
|
||||||
|
|
||||||
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]:
|
||||||
"""
|
"""
|
||||||
Finds latest order for this orderside and status
|
Finds latest order for this orderside and status
|
||||||
|
@ -383,8 +383,8 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
|
|
||||||
def adjust_trade_position(self, pair: str, trade: Trade,
|
def adjust_trade_position(self, pair: str, trade: Trade,
|
||||||
current_time: datetime, adjust_trade_position: float,
|
current_time: datetime, current_rate: float, current_profit: float,
|
||||||
current_rate: float, current_profit: float, **kwargs) -> float:
|
**kwargs) -> Optional[float]:
|
||||||
"""
|
"""
|
||||||
Custom trade adjustment logic, returning the amount that a trade shold be either increased or decreased.
|
Custom trade adjustment logic, returning the amount that a trade shold be either increased or decreased.
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
:param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
|
||||||
:return float: Amount to adjust your trade (buy more or sell some)
|
:return float: Amount to adjust your trade (buy more or sell some)
|
||||||
"""
|
"""
|
||||||
return 0.0
|
return None
|
||||||
|
|
||||||
def informative_pairs(self) -> ListPairsWithTimeframes:
|
def informative_pairs(self) -> ListPairsWithTimeframes:
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user