def portfolio_metrics() -> List[ Union[Union[List[Union[str, Any]], List[str], List[Union[Union[str, float], Any]]], Any]]: data = stats.trades(store.completed_trades.trades, store.app.daily_balance) metrics = [ ['Total Closed Trades', data['total']], ['Total Net Profit', '{} ({})'.format(jh.format_currency(round(data['net_profit'], 4)), str(round(data['net_profit_percentage'], 2)) + '%')], ['Starting => Finishing Balance', '{} => {}'.format(jh.format_currency(round(data['starting_balance'], 2)), jh.format_currency(round(data['finishing_balance'], 2)))], ['Total Open Trades', data['total_open_trades']], ['Open PL', jh.format_currency(round(data['open_pl'], 2))], ['Total Paid Fees', jh.format_currency(round(data['fee'], 2))], ['Max Drawdown', '{}%'.format(round(data['max_drawdown'], 2))], ['Annual Return', '{}%'.format(round(data['annual_return'], 2))], ['Expectancy', '{} ({})'.format(jh.format_currency(round(data['expectancy'], 2)), str(round(data['expectancy_percentage'], 2)) + '%')], ['Avg Win | Avg Loss', '{} | {}'.format(jh.format_currency(round(data['average_win'], 2)), jh.format_currency(round(data['average_loss'], 2)))], ['Ratio Avg Win / Avg Loss', round(data['ratio_avg_win_loss'], 2)], ['Percent Profitable', str(round(data['win_rate'] * 100)) + '%'], ['Longs | Shorts', '{}% | {}%'.format(round(data['longs_percentage']), round(data['short_percentage']))], ['Avg Holding Time', jh.readable_duration(data['average_holding_period'], 3)], ['Winning Trades Avg Holding Time', np.nan if np.isnan(data['average_winning_holding_period']) else jh.readable_duration( data['average_winning_holding_period'], 3)], ['Losing Trades Avg Holding Time', np.nan if np.isnan(data['average_losing_holding_period']) else jh.readable_duration( data['average_losing_holding_period'], 3)] ] if jh.get_config('env.metrics.sharpe_ratio', True): metrics.append(['Sharpe Ratio', round(data['sharpe_ratio'], 2)]) if jh.get_config('env.metrics.calmar_ratio', False): metrics.append(['Calmar Ratio', round(data['calmar_ratio'], 2)]) if jh.get_config('env.metrics.sortino_ratio', False): metrics.append(['Sortino Ratio', round(data['sortino_ratio'], 2)]) if jh.get_config('env.metrics.omega_ratio', False): metrics.append(['Omega Ratio', round(data['omega_ratio'], 2)]) if jh.get_config('env.metrics.winning_streak', False): metrics.append(['Winning Streak', data['winning_streak']]) if jh.get_config('env.metrics.losing_streak', False): metrics.append(['Losing Streak', data['losing_streak']]) if jh.get_config('env.metrics.largest_winning_trade', False): metrics.append(['Largest Winning Trade', jh.format_currency(round(data['largest_winning_trade'], 2))]) if jh.get_config('env.metrics.largest_losing_trade', False): metrics.append(['Largest Losing Trade', jh.format_currency(round(data['largest_losing_trade'], 2))]) if jh.get_config('env.metrics.total_winning_trades', False): metrics.append(['Total Winning Trades', data['total_winning_trades']]) if jh.get_config('env.metrics.total_losing_trades', False): metrics.append(['Total Losing Trades', data['total_losing_trades']]) return metrics
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 async_save(): DailyBalance.insert(**daily_balance).execute() if jh.is_debugging(): logger.info('Stored daily portfolio balance record into the database: {} => {}'.format( daily_balance['asset'], jh.format_currency(round(daily_balance['balance'], 2)) ))
def livetrade(): # sum up balance of all trading exchanges starting_balance = 0 current_balance = 0 for e in store.exchanges.storage: starting_balance += store.exchanges.storage[e].starting_assets[ jh.app_currency()] current_balance += store.exchanges.storage[e].assets[jh.app_currency()] starting_balance = round(starting_balance, 2) current_balance = round(current_balance, 2) arr = [[ 'started at', jh.timestamp_to_arrow(store.app.starting_time).humanize() ], ['current time', jh.timestamp_to_time(jh.now_to_timestamp())[:19]], [ 'errors/info', '{}/{}'.format(len(store.logs.errors), len(store.logs.info)) ], ['active orders', store.orders.count_all_active_orders()], ['open positions', store.positions.count_open_positions()]] # TODO: for now, we assume that we trade on one exchange only. Later, we need to support for more than one exchange at a time first_exchange = selectors.get_exchange(router.routes[0].exchange) if first_exchange.type == 'futures': arr.append([ 'started/current balance', '{}/{}'.format(starting_balance, current_balance) ]) else: # loop all trading exchanges for exchange in selectors.get_all_exchanges(): # loop all assets for asset_name, asset_balance in exchange.assets.items(): if asset_name == jh.base_asset(router.routes[0].symbol): current_price = selectors.get_current_price( router.routes[0].exchange, router.routes[0].symbol) arr.append([ '{}'.format(asset_name), '{}/{} ({} {})'.format( round(exchange.available_assets[asset_name], 5), round(asset_balance, 5), jh.format_currency( round(asset_balance * current_price, 2)), jh.quote_asset(router.routes[0].symbol)) ]) else: arr.append([ '{}'.format(asset_name), '{}/{}'.format( round(exchange.available_assets[asset_name], 5), round(asset_balance, 5), ) ]) # short trades summary if len(store.completed_trades.trades): df = pd.DataFrame.from_records( [t.to_dict() for t in store.completed_trades.trades]) total = len(df) winning_trades = df.loc[df['PNL'] > 0] losing_trades = df.loc[df['PNL'] < 0] pnl = round(df['PNL'].sum(), 2) pnl_percentage = round((pnl / starting_balance) * 100, 2) arr.append([ 'total/winning/losing trades', '{}/{}/{}'.format(total, len(winning_trades), len(losing_trades)) ]) arr.append(['PNL (%)', '${} ({}%)'.format(pnl, pnl_percentage)]) if config['app']['debug_mode']: arr.append(['debug mode', config['app']['debug_mode']]) if config['app']['is_test_driving']: arr.append(['Test Drive', config['app']['is_test_driving']]) return arr