Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Staggered] use dynamic order fees when available #1108

Merged
merged 3 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,8 @@ async def order_filled_callback(self, filled_order):
filled_price = decimal.Decimal(str(filled_order[trading_enums.ExchangeConstantsOrderColumns.PRICE.value]))
filled_volume = decimal.Decimal(str(filled_order[trading_enums.ExchangeConstantsOrderColumns.FILLED.value]))
price = filled_price + price_increment if now_selling else filled_price - price_increment
volume = self._compute_mirror_order_volume(now_selling, filled_price, price, filled_volume)
fee = filled_order[trading_enums.ExchangeConstantsOrderColumns.FEE.value]
volume = self._compute_mirror_order_volume(now_selling, filled_price, price, filled_volume, fee)
new_order = OrderData(new_side, volume, price, self.symbol, False, associated_entry_id)
self.logger.debug(f"Creating mirror order: {new_order} after filled order: {filled_order}")
if self.mirror_order_delay == 0 or trading_api.get_is_backtesting(self.exchange_manager):
Expand All @@ -717,7 +718,7 @@ async def order_filled_callback(self, filled_order):
self._lock_portfolio_and_create_order_when_possible(new_order, filled_price)
))

def _compute_mirror_order_volume(self, now_selling, filled_price, target_price, filled_volume):
def _compute_mirror_order_volume(self, now_selling, filled_price, target_price, filled_volume, paid_fees: dict):
# use target volumes if set
if self.sell_volume_per_order != trading_constants.ZERO and now_selling:
return self.sell_volume_per_order
Expand All @@ -732,9 +733,20 @@ def _compute_mirror_order_volume(self, now_selling, filled_price, target_price,
if self.reinvest_profits:
return new_order_quantity
# remove exchange fees
quantity_change = self.max_fees
quantity = new_order_quantity * (1 - quantity_change)
return quantity
if paid_fees:
base, quote = symbol_util.parse_symbol(self.symbol).base_and_quote()
fees_in_base = trading_personal_data.get_fees_for_currency(paid_fees, base)
fees_in_base += trading_personal_data.get_fees_for_currency(paid_fees, quote) / filled_price
if fees_in_base == trading_constants.ZERO:
self.logger.debug(
f"Zero fees for trade on {self.symbol}"
)
else:
self.logger.debug(
f"No fees given to compute {self.symbol} mirror order size, using default ratio of {self.max_fees}"
)
fees_in_base = new_order_quantity * self.max_fees
return new_order_quantity - fees_in_base

async def _lock_portfolio_and_create_order_when_possible(self, new_order, filled_price):
await asyncio.wait_for(self.allowed_mirror_orders.wait(), timeout=None)
Expand Down Expand Up @@ -1335,7 +1347,9 @@ def _get_quantity_from_recent_trades(self, price, max_quantity, recent_trades, c
if trade is None:
return None
now_selling = trade.side == trading_enums.TradeOrderSide.BUY
return self._compute_mirror_order_volume(now_selling, trade.executed_price, price, trade.executed_quantity)
return self._compute_mirror_order_volume(
now_selling, trade.executed_price, price, trade.executed_quantity, trade.fee
)

def _get_associated_trade(self, price, trades, selling):
increment_window = self.flat_increment / 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1152,31 +1152,70 @@ async def test_compute_mirror_order_volume():
# no fixed volumes
producer.reinvest_profits = producer.use_fixed_volume_for_mirror_orders = False
# 1% max fees
producer.max_fees = 0.01
producer.max_fees = decimal.Decimal("0.01")
# take exchange fees into account
assert producer._compute_mirror_order_volume(True, 100, 120, 2) == 2 * (1 - producer.max_fees)
assert producer._compute_mirror_order_volume(False, 100, 80, 2) == 2 * (100 / 80) * (1 - producer.max_fees)
assert producer._compute_mirror_order_volume(
True, decimal.Decimal("100"), decimal.Decimal("120"), decimal.Decimal("2"), None
) == 2 * (1 - producer.max_fees)
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), {}
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80")) * (1 - producer.max_fees)
# with given fees
fees = {
trading_enums.FeePropertyColumns.COST.value: decimal.Decimal("0.032"),
trading_enums.FeePropertyColumns.CURRENCY.value: "BTC"
}
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), fees
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80")) - decimal.Decimal("0.032")
fees = {
trading_enums.FeePropertyColumns.COST.value: decimal.Decimal("2.3"),
trading_enums.FeePropertyColumns.CURRENCY.value: "USD"
}
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), fees
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80")) - (decimal.Decimal("2.3") / decimal.Decimal("100"))

# with profits reinvesting
producer.reinvest_profits = True
# consider fees already taken, sell everything
assert producer._compute_mirror_order_volume(True, 100, 120, 2) == 2
assert producer._compute_mirror_order_volume(False, 100, 80, 2) == 2 * (100 / 80)
assert producer._compute_mirror_order_volume(
True, decimal.Decimal("100"), decimal.Decimal("120"), decimal.Decimal("2"), None
) == 2
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), {}
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80"))
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), fees
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80"))

# with fixed volumes
producer.reinvest_profits = False
producer.sell_volume_per_order = 3
# consider fees already taken, sell everything
assert producer._compute_mirror_order_volume(True, 100, 120, 2) == 3
assert producer._compute_mirror_order_volume(
True, decimal.Decimal("100"), decimal.Decimal("120"), decimal.Decimal("2"), fees
) == 3
# buy order
assert producer._compute_mirror_order_volume(False, 100, 80, 2) == 2 * (100 / 80) * (1 - producer.max_fees)
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), None
) == 2 * (decimal.Decimal("100") / decimal.Decimal("80")) * (1 - producer.max_fees)
producer.buy_volume_per_order = 5
assert producer._compute_mirror_order_volume(False, 100, 80, 2) == 5
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), {}
) == 5

# with fixed volumes and profits reinvesting
producer.reinvest_profits = True
assert producer._compute_mirror_order_volume(True, 100, 120, 2) == 3
assert producer._compute_mirror_order_volume(False, 100, 80, 2) == 5
assert producer._compute_mirror_order_volume(
True, decimal.Decimal("100"), decimal.Decimal("120"), decimal.Decimal("2"), None
) == 3
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), {}
) == 5
assert producer._compute_mirror_order_volume(
False, decimal.Decimal("100"), decimal.Decimal("80"), decimal.Decimal("2"), fees
) == 5


async def test_create_order():
Expand Down