3434from freqtrade .exchange .exchange_utils import price_to_precision
3535from freqtrade .ft_types import AnnotationType
3636from freqtrade .loggers import bufferHandler
37- from freqtrade .persistence import CustomDataWrapper , KeyValueStore , PairLocks , Trade
37+ from freqtrade .persistence import CustomDataWrapper , KeyValueStore , Order , PairLocks , Trade
3838from freqtrade .persistence .models import PairLock , custom_data_rpc_wrapper
3939from freqtrade .plugins .pairlist .pairlist_helpers import expand_pairlist
4040from freqtrade .rpc .fiat_convert import CryptoToFiatConverter
@@ -502,20 +502,13 @@ def trade_win_loss(trade):
502502 durations = {"wins" : wins_dur , "draws" : draws_dur , "losses" : losses_dur }
503503 return {"exit_reasons" : exit_reasons , "durations" : durations }
504504
505- def _rpc_trade_statistics (
506- self , stake_currency : str , fiat_display_currency : str , start_date : datetime | None = None
505+ def _collect_trade_statistics_data (
506+ self ,
507+ trades : Sequence ["Trade" ],
508+ stake_currency : str ,
509+ fiat_display_currency : str ,
507510 ) -> dict [str , Any ]:
508- """Returns cumulative profit statistics"""
509-
510- start_date = datetime .fromtimestamp (0 ) if start_date is None else start_date
511-
512- trade_filter = (
513- Trade .is_open .is_ (False ) & (Trade .close_date >= start_date )
514- ) | Trade .is_open .is_ (True )
515- trades : Sequence [Trade ] = Trade .session .scalars (
516- Trade .get_trades_query (trade_filter , include_orders = False ).order_by (Trade .id )
517- ).all ()
518-
511+ """Iterate trades, calculate various statistics, and return intermediate results."""
519512 profit_all_coin = []
520513 profit_all_ratio = []
521514 profit_closed_coin = []
@@ -544,7 +537,7 @@ def _rpc_trade_statistics(
544537 losing_trades += 1
545538 losing_profit += profit_abs
546539 else :
547- # Get current rate
540+ # Get current rate for open trades
548541 if len (trade .select_filled_orders (trade .entry_side )) == 0 :
549542 # Skip trades with no filled orders
550543 continue
@@ -558,17 +551,74 @@ def _rpc_trade_statistics(
558551 profit_abs = nan
559552 else :
560553 _profit = trade .calculate_profit (trade .close_rate or current_rate )
561-
562554 profit_ratio = _profit .profit_ratio
563555 profit_abs = _profit .total_profit
564556
565557 profit_all_coin .append (profit_abs )
566558 profit_all_ratio .append (profit_ratio )
567559
560+ return {
561+ "profit_all_coin" : profit_all_coin ,
562+ "profit_all_ratio" : profit_all_ratio ,
563+ "profit_closed_coin" : profit_closed_coin ,
564+ "profit_closed_ratio" : profit_closed_ratio ,
565+ "durations" : durations ,
566+ "winning_trades" : winning_trades ,
567+ "losing_trades" : losing_trades ,
568+ "winning_profit" : winning_profit ,
569+ "losing_profit" : losing_profit ,
570+ }
571+
572+ def _rpc_trade_statistics (
573+ self ,
574+ stake_currency : str ,
575+ fiat_display_currency : str ,
576+ start_date : datetime | None = None ,
577+ direction : str | None = None ,
578+ ) -> dict [str , Any ]:
579+ """
580+ Returns cumulative profit statistics, with optional direction filter (long/short)
581+ """
582+ start_date = datetime .fromtimestamp (0 ) if start_date is None else start_date
583+
584+ trade_filter = (
585+ Trade .is_open .is_ (False ) & (Trade .close_date >= start_date )
586+ ) | Trade .is_open .is_ (True )
587+
588+ if direction == "long" :
589+ dir_filter = Trade .is_short .is_ (False )
590+ trade_filter = trade_filter & dir_filter
591+ elif direction == "short" :
592+ dir_filter = Trade .is_short .is_ (True )
593+ trade_filter = trade_filter & dir_filter
594+
595+ trades : Sequence [Trade ] = Trade .session .scalars (
596+ Trade .get_trades_query (trade_filter , include_orders = False ).order_by (Trade .id )
597+ ).all ()
598+
599+ stats = self ._collect_trade_statistics_data (trades , stake_currency , fiat_display_currency )
600+
601+ profit_all_coin = stats ["profit_all_coin" ]
602+ profit_all_ratio = stats ["profit_all_ratio" ]
603+ profit_closed_coin = stats ["profit_closed_coin" ]
604+ profit_closed_ratio = stats ["profit_closed_ratio" ]
605+ durations = stats ["durations" ]
606+ winning_trades = stats ["winning_trades" ]
607+ losing_trades = stats ["losing_trades" ]
608+ winning_profit = stats ["winning_profit" ]
609+ losing_profit = stats ["losing_profit" ]
610+
568611 closed_trade_count = len ([t for t in trades if not t .is_open ])
569612
570- best_pair = Trade .get_best_pair (start_date )
571- trading_volume = Trade .get_trading_volume (start_date )
613+ best_pair_filters = [Trade .close_date > start_date ]
614+ trading_volume_filters = [Order .order_filled_date >= start_date ]
615+
616+ if direction :
617+ best_pair_filters .append (dir_filter )
618+ trading_volume_filters .append (dir_filter )
619+
620+ best_pair = Trade .get_best_pair (best_pair_filters )
621+ trading_volume = Trade .get_trading_volume (trading_volume_filters )
572622
573623 # Prepare data to display
574624 profit_closed_coin_sum = round (sum (profit_closed_coin ), 8 )
0 commit comments