def _reduce(self, qty, price): if self.is_open is False: raise EmptyPosition('The position is closed.') # just to prevent confusion qty = abs(qty) estimated_profit = jh.estimate_PNL(qty, self.entry_price, price, self.type) if self.exchange: self.exchange.increase_balance( self, qty * self.entry_price + estimated_profit) if self.type == trade_types.LONG: self.qty -= qty elif self.type == trade_types.SHORT: self.qty += qty info_text = 'REDUCED position: {}, {}, {}, {}, ${}'.format( self.exchange_name, self.symbol, self.type, self.qty, round(self.entry_price, 2)) if jh.is_debuggable('position_reduced'): logger.info(info_text) if jh.is_live( ) and config['env']['notifications']['events']['updated_position']: notifier.notify(info_text)
def _close(self, close_price): if self.is_open is False: raise EmptyPosition('The position is already closed.') # just to prevent confusion close_qty = abs(self.qty) estimated_profit = jh.estimate_PNL(close_qty, self.entry_price, close_price, self.type) entry = self.entry_price trade_type = self.type self.exit_price = close_price if self.exchange: self.exchange.increase_balance( self, close_qty * self.entry_price + estimated_profit) self.qty = 0 self.entry_price = None self.closed_at = jh.now() info_text = 'CLOSED {} position: {}, {}. PNL: ${}, entry: {}, exit: {}'.format( trade_type, self.exchange_name, self.symbol, round(estimated_profit, 2), entry, close_price) if jh.is_debuggable('position_closed'): logger.info(info_text) if jh.is_live( ) and config['env']['notifications']['events']['updated_position']: notifier.notify(info_text)
def _reduce(self, qty: float, price: float) -> None: if self.is_open is False: raise EmptyPosition('The position is closed.') # just to prevent confusion qty = abs(qty) estimated_profit = jh.estimate_PNL(qty, self.entry_price, price, self.type) if self.exchange: # self.exchange.increase_futures_balance(qty * self.entry_price + estimated_profit) self.exchange.add_realized_pnl(estimated_profit) self.exchange.temp_reduced_amount[jh.base_asset( self.symbol)] += abs(qty * price) if self.type == trade_types.LONG: self.qty = subtract_floats(self.qty, qty) elif self.type == trade_types.SHORT: self.qty = sum_floats(self.qty, qty) info_text = 'REDUCED position: {}, {}, {}, {}, ${}'.format( self.exchange_name, self.symbol, self.type, self.qty, round(self.entry_price, 2)) if jh.is_debuggable('position_reduced'): logger.info(info_text) if jh.is_live( ) and config['env']['notifications']['events']['updated_position']: notifier.notify(info_text)
def _close(self, close_price: float) -> None: if self.is_open is False: raise EmptyPosition('The position is already closed.') # just to prevent confusion close_qty = abs(self.qty) estimated_profit = jh.estimate_PNL(close_qty, self.entry_price, close_price, self.type) entry = self.entry_price trade_type = self.type self.exit_price = close_price if self.exchange: self.exchange.add_realized_pnl(estimated_profit) self.exchange.temp_reduced_amount[jh.base_asset( self.symbol)] += abs(close_qty * close_price) self.qty = 0 self.entry_price = None self.closed_at = jh.now_to_timestamp() if not jh.is_unit_testing(): info_text = 'CLOSED {} position: {}, {}, {}. PNL: ${}, Balance: ${}, entry: {}, exit: {}'.format( trade_type, self.exchange_name, self.symbol, self.strategy.name, round(estimated_profit, 2), jh.format_currency( round(self.exchange.wallet_balance(self.symbol), 2)), entry, close_price) if jh.is_debuggable('position_closed'): logger.info(info_text) if jh.is_live( ) and config['env']['notifications']['events']['updated_position']: notifier.notify(info_text)
def pnl(self): """PNL""" fee = config['env']['exchanges'][self.exchange]['fee'] return jh.estimate_PNL( self.qty, self.entry_price, self.exit_price, self.type, fee )
def test_estimate_PNL(): # profit assert jh.estimate_PNL(2, 50, 60, 'long') == 20 assert jh.estimate_PNL(2, 60, 50, 'short') == 20 # loss assert jh.estimate_PNL(2, 50, 60, 'short') == -20 assert jh.estimate_PNL(2, 60, 50, 'long') == -20 # profit with fee assert jh.estimate_PNL(1, 10, 20, 'long', 0.002) == 9.94 # loss with fee assert jh.estimate_PNL(1, 10, 20, 'short', 0.002) == -10.06 with pytest.raises(TypeError): jh.estimate_PNL(1, 200, 220, 1) jh.estimate_PNL(1, 200, 'invalid_input', 'short') jh.estimate_PNL(1, 'invalid_input', 220, 'short') jh.estimate_PNL('invalid_input', 200, 220, 'short')