partial sell using avg price
Co-Authored-By: மனோஜ்குமார் பழனிச்சாமி <smartmanoj42857@gmail.com>
This commit is contained in:
parent
f9e983e7a3
commit
1e9cbd579f
@ -489,7 +489,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
if amount > trade.amount:
|
||||
logger.info(f"Amount is higher than available. {amount} > {trade.amount}")
|
||||
return
|
||||
self.execute_trade_exit(trade, current_rate, sell_reason=SellCheckTuple(
|
||||
self.execute_trade_exit(trade, current_exit_rate, sell_reason=SellCheckTuple(
|
||||
sell_type=SellType.CUSTOM_SELL), sub_trade_amt=amount)
|
||||
|
||||
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
|
||||
@ -1409,9 +1409,6 @@ class FreqtradeBot(LoggingMixin):
|
||||
self.handle_order_fee(trade, order_obj, order)
|
||||
|
||||
trade.update_trade(order_obj)
|
||||
# TODO: is the below necessary? it's already done in update_trade for filled buys
|
||||
trade.recalc_trade_from_orders()
|
||||
Trade.commit()
|
||||
|
||||
if order['status'] in constants.NON_OPEN_EXCHANGE_STATES:
|
||||
# If a buy order was closed, force update on stoploss on exchange
|
||||
|
@ -180,7 +180,6 @@ class Order(_DECL_BASE):
|
||||
self.amount = order.get('amount', self.amount)
|
||||
self.filled = order.get('filled', self.filled)
|
||||
self.average = order.get('average', self.average)
|
||||
self.initial_average = order.get('average', self.initial_average)
|
||||
self.remaining = order.get('remaining', self.remaining)
|
||||
self.cost = order.get('cost', self.cost)
|
||||
if 'timestamp' in order and order['timestamp'] is not None:
|
||||
@ -516,46 +515,24 @@ class LocalTrade():
|
||||
|
||||
def process_sell_sub_trade(self, order: Order, is_closed: bool = True,
|
||||
is_non_bt: bool = True) -> None:
|
||||
orders = (self.select_filled_orders('buy'))
|
||||
sell_amount = order.safe_filled
|
||||
sell_amount = order.safe_amount_after_fee
|
||||
sell_rate = order.safe_price
|
||||
sell_stake_amount = sell_rate * sell_amount * (1 - self.fee_close)
|
||||
if is_closed:
|
||||
if sell_amount == self.amount:
|
||||
if sell_amount == self.amount:
|
||||
if is_closed:
|
||||
self.close(sell_rate)
|
||||
if is_non_bt:
|
||||
Trade.commit()
|
||||
return
|
||||
realized_profit = 0.0
|
||||
profit = 0.0
|
||||
idx = -1
|
||||
while sell_amount:
|
||||
b_order = orders[idx]
|
||||
buy_amount = b_order.safe_amount_after_fee
|
||||
avg_rate = b_order.safe_price
|
||||
buy_rate = b_order.initial_average or b_order.price
|
||||
if sell_amount < buy_amount:
|
||||
amount = sell_amount
|
||||
else:
|
||||
idx -= 1
|
||||
amount = buy_amount
|
||||
if is_closed:
|
||||
b_order.filled -= amount
|
||||
b_order.order_update_date = datetime.now(timezone.utc)
|
||||
sell_amount -= amount
|
||||
profit += self.calc_profit2(avg_rate, sell_rate, amount)
|
||||
realized_profit += self.calc_profit2(buy_rate, sell_rate, amount)
|
||||
profit = self.calc_profit2(self.open_rate, sell_rate, sell_amount)
|
||||
if is_closed:
|
||||
b_order2 = orders[idx]
|
||||
amount2 = b_order2.safe_amount_after_fee
|
||||
b_order2.average = (b_order2.average * amount2 - profit / (1 + self.fee_open)) / amount2
|
||||
if is_non_bt:
|
||||
Order.query.session.commit()
|
||||
self.recalc_trade_from_orders()
|
||||
self.realized_profit += realized_profit
|
||||
self.amount -= sell_amount
|
||||
self.stake_amount = self.open_rate * self.amount
|
||||
self.realized_profit += profit
|
||||
|
||||
self.close_profit_abs = profit
|
||||
self.close_profit = sell_stake_amount / (sell_stake_amount - profit) - 1
|
||||
self.recalc_open_trade_value()
|
||||
if is_non_bt:
|
||||
Trade.commit()
|
||||
|
||||
@ -578,7 +555,7 @@ class LocalTrade():
|
||||
"""
|
||||
self.close_rate = rate
|
||||
self.close_profit = self.calc_profit_ratio()
|
||||
self.close_profit_abs = self.calc_profit()
|
||||
self.close_profit_abs = self.calc_profit() + self.realized_profit
|
||||
self.close_date = self.close_date or datetime.utcnow()
|
||||
self.is_open = False
|
||||
self.sell_order_status = 'closed'
|
||||
@ -699,18 +676,21 @@ class LocalTrade():
|
||||
def recalc_trade_from_orders(self):
|
||||
total_amount = 0.0
|
||||
total_stake = 0.0
|
||||
avg_price = None
|
||||
|
||||
for o in self.orders:
|
||||
if (o.ft_is_open or
|
||||
(o.ft_order_side != 'buy') or
|
||||
not o.filled or
|
||||
(o.status not in NON_OPEN_EXCHANGE_STATES)):
|
||||
if o.ft_is_open or not o.filled:
|
||||
continue
|
||||
|
||||
tmp_amount = o.safe_amount_after_fee
|
||||
tmp_price = o.safe_price
|
||||
is_sell = o.ft_order_side != 'buy'
|
||||
side = [1, -1][is_sell]
|
||||
if tmp_amount > 0.0 and tmp_price is not None:
|
||||
total_amount += tmp_amount
|
||||
total_stake += tmp_price * tmp_amount
|
||||
total_amount += tmp_amount * side
|
||||
total_stake += [tmp_price, avg_price][is_sell] * tmp_amount * side
|
||||
if total_amount > 0:
|
||||
avg_price = total_stake / total_amount
|
||||
|
||||
if total_amount > 0:
|
||||
self.open_rate = total_stake / total_amount
|
||||
@ -915,6 +895,7 @@ class Trade(_DECL_BASE, LocalTrade):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.realized_profit = 0
|
||||
self.recalc_open_trade_value()
|
||||
|
||||
def delete(self) -> None:
|
||||
|
@ -294,11 +294,11 @@ class Telegram(RPCHandler):
|
||||
if self._rpc._fiat_converter:
|
||||
cp_fiat = self._rpc._fiat_converter.convert_amount(
|
||||
msg['cumulative_profit'], msg['stake_currency'], msg['fiat_currency'])
|
||||
cp_extra = f" / {cp_fiat:.3f} {msg['fiat_currency']})"
|
||||
cp_extra = f" / {cp_fiat:.3f} {msg['fiat_currency']}"
|
||||
else:
|
||||
cp_extra = ''
|
||||
cp_extra = f"*Cumulative Profit:* (`{msg['cumulative_profit']:.8f}" \
|
||||
f"{msg['stake_currency']}{cp_extra}`)\n"
|
||||
f" {msg['stake_currency']}{cp_extra}`)\n"
|
||||
else:
|
||||
cp_extra = ''
|
||||
message = (
|
||||
|
@ -4588,7 +4588,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
||||
assert trade.open_order_id is None
|
||||
assert pytest.approx(trade.open_rate) == 9.90909090909
|
||||
assert trade.amount == 22
|
||||
assert trade.stake_amount == 218
|
||||
assert pytest.approx(trade.stake_amount) == 218
|
||||
|
||||
orders = Order.query.all()
|
||||
assert orders
|
||||
@ -4672,8 +4672,8 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
|
||||
assert trade.open_order_id is None
|
||||
assert trade.is_open
|
||||
assert trade.amount == 22
|
||||
assert trade.stake_amount == 203.59850374064837
|
||||
assert pytest.approx(trade.open_rate) == 9.254477442756745
|
||||
assert trade.stake_amount == 192.05405405405406
|
||||
assert pytest.approx(trade.open_rate) == 8.729729729729
|
||||
|
||||
orders = Order.query.all()
|
||||
assert orders
|
||||
@ -4804,6 +4804,11 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
assert trade.open_order_id is None
|
||||
assert trade.amount == 50
|
||||
assert trade.open_rate == 11
|
||||
assert trade.stake_amount == 550
|
||||
assert pytest.approx(trade.realized_profit) == -152.375
|
||||
assert pytest.approx(trade.close_profit_abs) == -152.375
|
||||
|
||||
orders = Order.query.all()
|
||||
assert orders
|
||||
@ -4843,8 +4848,11 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
assert trade.open_order_id is None
|
||||
assert trade.close_profit_abs == 94.25
|
||||
|
||||
assert trade.amount == 50
|
||||
assert trade.open_rate == 11
|
||||
assert trade.stake_amount == 550
|
||||
assert pytest.approx(trade.realized_profit) == -152.375
|
||||
assert pytest.approx(trade.close_profit_abs) == 94.25
|
||||
orders = Order.query.all()
|
||||
assert orders
|
||||
assert len(orders) == 3
|
||||
@ -4866,12 +4874,12 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
('sell', 50, 5),
|
||||
),
|
||||
(
|
||||
# amount, open_rate, stake_amount, realized_profit, unrealized_profit
|
||||
(100.0, 10.0, 1000.0, 0.0, -5.0),
|
||||
(200.0, 12.5, 2500.0, 0.0, 486.25),
|
||||
(150.0, 12.686616791354945, 1902.9925187032418, -153.375, -112.25),
|
||||
(50.0, -1.7406483790523748, -87.03241895261874, 588.5, 1084.75),
|
||||
(50.0, -1.7406483790523748, -87.03241895261874, 588.5, 336.625),
|
||||
# amount, open_rate, stake_amount, cumulative_profit, realized_profit
|
||||
(100.0, 10.0, 1000.0, 0.0, None,),
|
||||
(200.0, 12.5, 2500.0, 0.0, None,),
|
||||
(150.0, 12.5, 1875.0, -28.0625, -28.0625,),
|
||||
(50.0, 12.5, 625.0, 713.8125, 741.875,),
|
||||
(50.0, 12.5, 625.0, 713.8125, 336.625,),
|
||||
)
|
||||
),
|
||||
(
|
||||
@ -4884,17 +4892,16 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
|
||||
('sell', 150, 23),
|
||||
),
|
||||
(
|
||||
(100.0, 3.0, 300.0, 0.0, -1.5),
|
||||
(200.0, 5.0, 1000.0, 0.0, 394.0),
|
||||
(100.0, -0.9451371571072332, -94.51371571072332, 395.5, 1192.0),
|
||||
(250.0, 8.621945137157107, 2155.4862842892767, 395.5, 1579.75),
|
||||
(150.0, 1.766417290108061, 264.96259351620915, 787.0, 2577.25),
|
||||
(150.0, 1.766417290108061, 264.96259351620915, 787.0, 3175.75),
|
||||
(100.0, 3.0, 300.0, 0.0, None,),
|
||||
(200.0, 5.0, 1000.0, 0.0, None,),
|
||||
(100.0, 5.0, 500.0, 596.0, 596.0,),
|
||||
(250.0, 11.0, 2750.0, 596.0, 596.0,),
|
||||
(150.0, 11.0, 1650.0, 1388.5, 792.5,),
|
||||
(150.0, 11.0, 1650.0, 1388.5, 3175.75,),
|
||||
)
|
||||
),
|
||||
])
|
||||
def test_position_adjust3(mocker, default_conf_usdt, fee, orders, res) -> None:
|
||||
|
||||
default_conf_usdt.update({
|
||||
"position_adjustment_enable": True,
|
||||
"dry_run": False,
|
||||
@ -4965,7 +4972,7 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, orders, res) -> None:
|
||||
assert trade.open_rate == res[idx][1]
|
||||
assert trade.stake_amount == res[idx][2]
|
||||
assert pytest.approx(trade.realized_profit) == res[idx][3]
|
||||
assert trade.calc_profit(order[2]) == res[idx][4]
|
||||
assert pytest.approx(trade.close_profit_abs) == res[idx][4]
|
||||
|
||||
order_obj = trade.select_order(order[0], False)
|
||||
assert order_obj.order_id == f'60{idx}'
|
||||
@ -4973,7 +4980,6 @@ def test_position_adjust3(mocker, default_conf_usdt, fee, orders, res) -> None:
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
assert trade.open_order_id is None
|
||||
assert trade.close_profit_abs == res[idx][4]
|
||||
assert trade.is_open is False
|
||||
|
||||
|
||||
|
@ -1484,7 +1484,8 @@ def test_recalc_trade_from_orders(fee):
|
||||
assert pytest.approx(trade.fee_open_cost) == o1_fee_cost + o2_fee_cost + o3_fee_cost
|
||||
assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
|
||||
|
||||
# Just to make sure sell orders are ignored, let's calculate one more time.
|
||||
# Just to make sure non partial sell orders are ignored, let's calculate one more time.
|
||||
|
||||
sell1 = Order(
|
||||
ft_order_side='sell',
|
||||
ft_pair=trade.pair,
|
||||
@ -1641,32 +1642,6 @@ def test_recalc_trade_from_orders_ignores_bad_orders(fee):
|
||||
assert trade.open_trade_value == 2 * o1_trade_val
|
||||
assert trade.nr_of_successful_buys == 2
|
||||
|
||||
# Just to make sure sell orders are ignored, let's calculate one more time.
|
||||
sell1 = Order(
|
||||
ft_order_side='sell',
|
||||
ft_pair=trade.pair,
|
||||
ft_is_open=False,
|
||||
status="closed",
|
||||
symbol=trade.pair,
|
||||
order_type="market",
|
||||
side="sell",
|
||||
price=4,
|
||||
average=3,
|
||||
filled=2,
|
||||
remaining=1,
|
||||
cost=5,
|
||||
order_date=trade.open_date,
|
||||
order_filled_date=trade.open_date,
|
||||
)
|
||||
trade.orders.append(sell1)
|
||||
trade.recalc_trade_from_orders()
|
||||
|
||||
assert trade.amount == 2 * o1_amount
|
||||
assert trade.stake_amount == 2 * o1_amount
|
||||
assert trade.open_rate == o1_rate
|
||||
assert trade.fee_open_cost == 2 * o1_fee_cost
|
||||
assert trade.open_trade_value == 2 * o1_trade_val
|
||||
assert trade.nr_of_successful_buys == 2
|
||||
# Check with 1 order
|
||||
order_noavg = Order(
|
||||
ft_order_side='buy',
|
||||
|
Loading…
Reference in New Issue
Block a user