@ -744,116 +744,109 @@ class Telegram(RPCHandler):
else :
await self . _status_msg ( update , context )
async def _status_msg ( self , update : Update , context : CallbackContext ) - > None :
"""
handler for ` / status ` and ` / status < id > ` .
"""
# Check if there's at least one numerical ID provided.
# If so, try to get only these trades.
trade_ids = [ ]
if context . args and len ( context . args ) > 0 :
trade_ids = [ int ( i ) for i in context . args if i . isnumeric ( ) ]
results = self . _rpc . _rpc_trade_status ( trade_ids = trade_ids )
position_adjust = self . _config . get ( " position_adjustment_enable " , False )
max_entries = self . _config . get ( " max_entry_position_adjustment " , - 1 )
for r in results :
r [ " open_date_hum " ] = dt_humanize_delta ( r [ " open_date " ] )
def _get_status_msg_lines (
self , r : dict [ str , Any ] , position_adjust : bool , max_entries : int
) - > list [ str ] :
r [ " open_date_hum " ] = dt_humanize_delta ( r [ " open_date " ] )
r [ " stake_amount_r " ] = fmt_coin ( r [ " stake_amount " ] , r [ " quote_currency " ] )
r [ " max_stake_amount_r " ] = fmt_coin (
r [ " max_stake_amount " ] or r [ " stake_amount " ] , r [ " quote_currency " ]
)
r [ " profit_abs_r " ] = fmt_coin ( r [ " profit_abs " ] , r [ " quote_currency " ] )
r [ " realized_profit_r " ] = fmt_coin ( r [ " realized_profit " ] , r [ " quote_currency " ] )
r [ " total_profit_abs_r " ] = fmt_coin ( r [ " total_profit_abs " ] , r [ " quote_currency " ] )
profit_emoji = " 🟢 " if r [ " profit_ratio " ] > = 0 else " 🔴 "
lines = [
f " * { r [ ' pair ' ] } * (# { r [ ' trade_id ' ] } ) " ,
f " { ' 🔴 `Short` ' if r . get ( ' is_short ' ) else ' 🟢 `Long` ' } "
+ ( f " ` ( { r [ ' leverage ' ] } x)` " if r . get ( " leverage " ) else " " )
+ f " { profit_emoji } ` { format_pct ( r [ ' profit_ratio ' ] ) } ` `( { r [ ' profit_abs_r ' ] } )` " ,
f " *Amount:* ` { r [ ' amount ' ] } ( { r [ ' stake_amount_r ' ] } )` "
+ ( f " / ` { r [ ' max_stake_amount_r ' ] } ` " if position_adjust else " " ) ,
f " *Open:* ` { round_value ( r [ ' open_rate ' ] , 8 ) } ` " ,
f " *Current:* ` { round_value ( r [ ' current_rate ' ] , 8 ) } ` "
if r [ " is_open " ]
else f " *Close:* ` { round_value ( r [ ' close_rate ' ] , 8 ) } ` " ,
]
r [ " stake_amount_r " ] = fmt_coin ( r [ " stake_amount " ] , r [ " quote_currency " ] )
r [ " max_stake_amount_r " ] = fmt_coin (
r [ " max_stake_amount " ] or r [ " stake_amount " ] , r [ " quote_currency " ]
)
r [ " profit_abs_r " ] = fmt_coin ( r [ " profit_abs " ] , r [ " quote_currency " ] )
r [ " realized_profit_r " ] = fmt_coin ( r [ " realized_profit " ] , r [ " quote_currency " ] )
r [ " total_profit_abs_r " ] = fmt_coin ( r [ " total_profit_abs " ] , r [ " quote_currency " ] )
lines = [
f " *Trade ID:* ` { r [ ' trade_id ' ] } ` "
+ ( f " `(since { r [ ' open_date_hum ' ] } )` " if r [ " is_open " ] else " " ) ,
f " *Current Pair:* { r [ ' pair ' ] } " ,
(
f " *Direction:* { ' 🔴 `Short` ' if r . get ( ' is_short ' ) else ' 🟢 `Long` ' } "
+ ( f " ` ( { r [ ' leverage ' ] } x)` " if r . get ( " leverage " ) else " " )
) ,
f " *Amount:* ` { r [ ' amount ' ] } ( { r [ ' stake_amount_r ' ] } )` " ,
f " *Total invested:* ` { r [ ' max_stake_amount_r ' ] } ` " if position_adjust else " " ,
f " *Enter Tag:* ` { r [ ' enter_tag ' ] } ` " if r [ " enter_tag " ] else " " ,
f " *Exit Reason:* ` { r [ ' exit_reason ' ] } ` " if r . get ( " exit_reason " ) else " " ,
]
if r [ " is_open " ] :
lines . append ( f " *Age:* ` { r [ ' open_date_hum ' ] } ` " )
if position_adjust :
max_buy_str = f " / { max_entries + 1 } " if ( max_entries > 0 ) else " "
lines . extend (
[
f " *Number of Entries:* ` { r [ ' nr_of_successful_entries ' ] } { max_buy_str } ` " ,
f " *Number of Exits:* ` { r [ ' nr_of_successful_exits ' ] } ` " ,
]
)
if r [ " enter_tag " ] :
lines . append ( f " *Tag:* ` { r [ ' enter_tag ' ] } ` " )
if r . get ( " exit_reason " ) :
lines . append ( f " *Exit:* ` { r [ ' exit_reason ' ] } ` " )
profit_emoji = " 🟢 " if r [ " profit_ratio " ] > = 0 else " 🔴 "
if position_adjust :
max_buy_str = f " / { max_entries + 1 } " if ( max_entries > 0 ) else " "
lines . extend (
[
f " *Open Rate:* ` { round_value ( r [ ' open_rate ' ] , 8 ) } ` " ,
f " *Close Rate:* ` { round_value ( r [ ' close_rate ' ] , 8 ) } ` " if r [ " close_rate " ] else " " ,
f " *Open Date:* ` { r [ ' open_date ' ] } ` " ,
f " *Close Date:* ` { r [ ' close_date ' ] } ` " if r [ " close_date " ] else " " ,
(
f " \n *Current Rate:* ` { round_value ( r [ ' current_rate ' ] , 8 ) } ` "
if r [ " is_open " ]
else " "
) ,
( " *Unrealized Profit:* " if r [ " is_open " ] else " *Close Profit: * " )
+ f " { profit_emoji } ` { format_pct ( r [ ' profit_ratio ' ] ) } ` `( { r [ ' profit_abs_r ' ] } )` " ,
f " *Entries:* ` { r [ ' nr_of_successful_entries ' ] } { max_buy_str } ` " ,
f " *Exits:* ` { r [ ' nr_of_successful_exits ' ] } ` " ,
]
)
if r [ " is_open " ] :
if (
r . get ( " realized_profit " ) is not None
and r . get ( " realized_profit_ratio " ) is not None
) :
lines . append (
f " *Realized Profit:* ` { format_pct ( r [ ' realized_profit_ratio ' ] ) } "
f " ( { r [ ' realized_profit_r ' ] } )` "
)
if r . get ( " total_profit_ratio " ) is not None :
lines . append (
f " *Total Profit:* ` { format_pct ( r [ ' total_profit_ratio ' ] ) } "
f " ( { r [ ' total_profit_abs_r ' ] } )` "
)
if r [ " is_open " ] :
if r . get ( " realized_profit " ) is not None and r . get ( " realized_profit_ratio " ) is not None :
lines . append (
f " *Realized Profit:* ` { format_pct ( r [ ' realized_profit_ratio ' ] ) } "
f " ( { r [ ' realized_profit_r ' ] } )` "
)
if r . get ( " total_profit_ratio " ) is not None :
lines . append (
f " *Total Profit:* ` { format_pct ( r [ ' total_profit_ratio ' ] ) } "
f " ( { r [ ' total_profit_abs_r ' ] } )` "
)
# Append empty line to improve readability
lines . append ( " " )
# Adding liquidation only if it is not None
if liquidation := r . get ( " liquidation_price " ) :
lines . append ( f " *Liquidation:* ` { round_value ( liquidation , 8 ) } ` " )
if (
r [ " stop_loss_abs " ] != r [ " initial_stop_loss_abs " ]
and r [ " initial_stop_loss_ratio " ] is not None
) :
# Adding initial stoploss only if it is different from stoploss
lines . append (
f " *Initial Stoploss:* ` { r [ ' initial_stop_loss_abs ' ] : .8f } ` "
f " `( { format_pct ( r [ ' initial_stop_loss_ratio ' ] ) } )` "
)
# Append empty line to improve readability
lines . append ( " " )
# Adding liquidation only if it is not None
if liquidation := r . get ( " liquidation_price " ) :
lines . append ( f " *Liquidation:* ` { round_value ( liquidation , 8 ) } ` " )
# Adding stoploss and stoploss percentage only if it is not None
if (
r [ " stop_loss_abs " ] != r [ " initial_stop_loss_abs " ]
and r [ " initial_stop_loss_ratio " ] is not None
) :
# Adding initial stoploss only if it is different from stoploss
lines . append (
f " *Stoploss:* ` { round_value ( r [ ' stop_loss_abs ' ] , 8 ) } ` "
+ ( f " `( { format_pct ( r [ ' stop_loss_ratio ' ] ) } )` " if r [ " stop_loss_ratio " ] else " " )
f " *Initial Stoploss:* ` { r [ ' initial_stop_loss_abs ' ] : .8f } ` "
f " `( { format_pct ( r [ ' initial_stop_loss_ratio ' ] ) } )` "
)
# Adding stoploss and stoploss percentage only if it is not None
lines . append (
f " *Stoploss:* ` { round_value ( r [ ' stop_loss_abs ' ] , 8 ) } ` "
+ ( f " `( { format_pct ( r [ ' stop_loss_ratio ' ] ) } )` " if r [ " stop_loss_ratio " ] else " " )
)
lines . append (
f " *Stoploss distance:* ` { round_value ( r [ ' stoploss_current_dist ' ] , 8 ) } ` "
f " `( { format_pct ( r [ ' stoploss_current_dist_ratio ' ] ) } )` "
)
if open_orders := r . get ( " open_orders " ) :
lines . append (
f " *Stoploss distance:* ` { round_value ( r [ ' stoploss_current_dist ' ] , 8 ) } ` "
f " `( { format_pct ( r [ ' stoploss_current_dist_ratio ' ] ) } )` "
f " * Open Order:* `{ open_orders } ` "
+ ( f " - ` { r [ ' exit_order_status ' ] } ` " if r [ " exit_order_status " ] else " " )
)
if open_orders := r . get ( " open_orders " ) :
lines . append (
f " *Open Order:* ` { open_orders } ` "
+ ( f " - ` { r [ ' exit_order_status ' ] } ` " if r [ " exit_order_status " ] else " " )
)
return lines
async def _status_msg ( self , update : Update , context : CallbackContext ) - > None :
"""
handler for ` / status ` and ` / status < id > ` .
"""
# Check if there's at least one numerical ID provided.
# If so, try to get only these trades.
trade_ids = [ ]
if context . args and len ( context . args ) > 0 :
trade_ids = [ int ( i ) for i in context . args if i . isnumeric ( ) ]
results = self . _rpc . _rpc_trade_status ( trade_ids = trade_ids )
position_adjust = self . _config . get ( " position_adjustment_enable " , False )
max_entries = self . _config . get ( " max_entry_position_adjustment " , - 1 )
for r in results :
lines = self . _get_status_msg_lines ( r , position_adjust , max_entries )
await self . __send_status_msg ( lines , r )
async def __send_status_msg ( self , lines : list [ str ] , r : dict [ str , Any ] ) - > None :
@ -1914,61 +1907,61 @@ class Telegram(RPCHandler):
: return : None
"""
force_enter_text = (
" */forcelong <pair> [<rate>]:* ` Instantly buys the given pair. "
" /forcelong <pair> [<rate>] - Instantly buys the given pair. "
" Optionally takes a rate at which to buy "
" (only applies to limit orders). ` \n "
" (only applies to limit orders). \n "
)
if self . _rpc . _freqtrade . trading_mode != TradingMode . SPOT :
force_enter_text + = (
" */forceshort <pair> [<rate>]:* ` Instantly shorts the given pair. "
" /forceshort <pair> [<rate>] - Instantly shorts the given pair. "
" Optionally takes a rate at which to sell "
" (only applies to limit orders). ` \n "
" (only applies to limit orders). \n "
)
message = (
" 🤖 *Bot Control* \n "
" */start:* `Starts the trader` \n "
" */stop:* `Stops the trader` \n "
" */pause:* `Pause new entries (keeps open trades)` \n "
" */stopentry:* `Alias for /pause` \n "
" */forceexit <id>|all:* `Instantly exits trade(s)` \n "
" */fx <id>|all:* `Alias for /forceexit` \n "
" /start - Starts the trader \n "
" /stop - Stops the trader \n "
" /pause - Pause new entries (keeps open trades) \n "
" /stopentry - Alias for /pause \n "
" /forceexit <id>|all - Instantly exits trade(s) \n "
" /fx <id>|all - Alias for /forceexit \n "
f " { force_enter_text if self . _config . get ( ' force_entry_enable ' , False ) else ' ' } "
" */delete <id>:* `Delete trade from DB (no exchange action)` \n "
" */reload_trade <id>:* `Reload trade from exchange` \n "
" */cancel_open_order <id>:* `Cancel open orders` \n "
" /delete <id> - Delete trade from DB (no exchange action) \n "
" /reload_trade <id> - Reload trade from exchange \n "
" /cancel_open_order <id> - Cancel open orders \n "
" \n "
" 📊 *Statistics* \n "
" */status <id>|[table]:* `List open trades` \n "
" */profit [<n>]:* `Cumulative profit (last n days)` \n "
" */profit_long [<n>]:* `Long profit` \n "
" */profit_short [<n>]:* `Short profit` \n "
" */daily <n>:* `Daily profit` \n "
" */weekly <n>:* `Weekly profit` \n "
" */monthly <n>:* `Monthly profit` \n "
" */trades [limit]:* `Recent closed trades` \n "
" */performance:* `Performance by pair` \n "
" */stats:* `Win/Loss stats and durations` \n "
" */count:* `Active trade count` \n "
" */entries <pair>:* `Entry tag performance` \n "
" */exits <pair>:* `Exit reason performance` \n "
" */mix_tags <pair>:* `Combined tag performance` \n "
" /status <id>|[table] - List open trades \n "
" /profit [<n>] - Cumulative profit (last n days) \n "
" /profit_long [<n>] - Long profit \n "
" /profit_short [<n>] - Short profit \n "
" /daily <n> - Daily profit \n "
" /weekly <n> - Weekly profit \n "
" /monthly <n> - Monthly profit \n "
" /trades [limit] - Recent closed trades \n "
" /performance - Performance by pair \n "
" /stats - Win/Loss stats and durations \n "
" /count - Active trade count \n "
" /entries <pair> - Entry tag performance \n "
" /exits <pair> - Exit reason performance \n "
" /mix_tags <pair> - Combined tag performance \n "
" \n "
" ⚙️ *Configuration* \n "
" */show_config:* `Show running config` \n "
" */reload_config:* `Reload config file` \n "
" */whitelist [sorted|baseonly]:* `Show whitelist` \n "
" */blacklist [pair]:* `Show/add to blacklist` \n "
" */bl_delete [pair]:* `Remove from blacklist` \n "
" */marketdir [dir]:* `Set market direction` \n "
" /show_config - Show running config \n "
" /reload_config - Reload config file \n "
" /whitelist [sorted|baseonly] - Show whitelist \n "
" /blacklist [pair] - Show/add to blacklist \n "
" /bl_delete [pair] - Remove from blacklist \n "
" /marketdir [dir] - Set market direction \n "
" \n "
" ℹ ️ *Info*\n " # noqa: RUF001
" */balance:* `Show balances` \n "
" */locks:* `Show active locks` \n "
" */unlock <pair|id>:* `Unlock pair/id` \n "
" */logs [limit]:* `Show recent logs` \n "
" */health:* `Health check` \n "
" */version:* `Show version` \n "
" */help:* `Show this help` \n "
" /balance - Show balances \n "
" /locks - Show active locks \n "
" /unlock <pair|id> - Unlock pair/id \n "
" /logs [limit] - Show recent logs \n "
" /health - Health check \n "
" /version - Show version \n "
" /help - Show this help \n "
)
await self . _send_msg ( message , parse_mode = ParseMode . MARKDOWN )