Wired up the /autotrade Telegram bot command handler in the autotrade-foundation branch, completing the command dispatch layer for the autotrade config-repo module.

What Was Built

commands.rs — crates/polymarket-fetch/src/pmv2/autotrade/commands.rs

Four new items added:

apply_set(param, value) -> Result<(&'static str, f64)> Pure function. Calls validate_set then param_to_column, returns the resolved (column_name, validated_f64) tuple. Used by the SetParam arm in run_autotrade.

format_status(c: &AutotradeConfig) -> String Formats the full AutotradeConfig row into a human-readable Telegram status string. BigDecimal fields format via their Display impl — no conversion needed.

run_autotrade(pool, args) -> String (async) Top-level dispatcher. Calls parse_autotrade, then dispatches to:

  • config::load → Status arm
  • config::update_numeric → SetParam arm (via apply_set)
  • config::set_text → SetMode / SetExit arms (via set_text_reply)
  • config::set_source_enabled → Filter arm
  • config::set_text → Maker arm (via set_text_reply)

Always returns a reply String (errors are stringified, never propagated — the bot must always send something back).

set_text_reply (private async helper) Shared by SetMode / SetExit / Filter / Maker arms to avoid repetition.

Test added: apply_set_ok_and_errors alongside the existing 2 tests.

Clippy gotcha

This project runs clippy with -D clippy::unwrap_used. Test bodies using .unwrap() must be annotated with #[allow(clippy::unwrap_used)].

bot.rs — crates/polymarket-fetch/src/bot.rs

Added an "autotrade" match arm in run_command (around line 768):

"autotrade" => {
    let reply = crate::pmv2::autotrade::commands::run_autotrade(&state.pool, &parsed.args).await;
    send_with_timeout(&state.telegram, chat_id, &reply, None).await?;
    Ok(())
}

The owner-auth gate (scope_for("autotrade") -> Scope::Owner) was already present at the top of run_command from autotrade-authz-module. No second auth check was added — the single gate at the top of the match covers all arms uniformly.

Key Decisions & Gotchas

DecisionRationale
run_autotrade returns String, never errorsTelegram dispatch must always reply; errors surface as user-readable strings
send_with_timeout(..., None) — no parse_modePlain text; status output uses only printable ASCII
BigDecimal via Display in format_statusAutotradeConfig stores NUMERIC as BigDecimal; {} formatter works out of the box
c.mode / c.exit_mode are String, not enumThe DB column is text; AutotradeConfig stores them as String from sqlx
Auth gate NOT duplicated in the armscope_for("autotrade") in the preamble of run_command already blocks non-owners

#[allow(clippy::unwrap_used)] required on all test fns

The project-wide -D clippy::unwrap_used flag fires on test bodies too (--all-targets includes #[cfg(test)]). Every test function that calls .unwrap() must carry this allow attribute.

Test Results

  • cargo test -p polymarket-fetch autotrade::commands3 passed (existing 2 + new apply_set_ok_and_errors)
  • cargo fmt -p polymarket-fetch → clean
  • bash scripts/gate.shexit 0 (244 tests pass)