convert_profile_to_live
Convert a specified OctoBot profile to live trading type and select it, requiring explicit confirmation before applying the change.
Instructions
Relabel a profile as OctoBot's internal ProfileType.LIVE and select it. Confirm-gated (ADR-0003).
This tool does NOT enable real-money trading -- confirmed against
OctoBot source, not just suspected (resolves requirements doc open
question #5). Whether OctoBot actually places real orders is governed
entirely by a separate config flag, config.trader.enabled, checked by
OctoBot's own is_real_trading(profile) (semantically: returns
trading_util.is_trader_enabled(profile.config), itself
config[CONFIG_TRADER][CONFIG_ENABLED_OPTION] -- both confirmed against
source, not a verbatim one-line quote of the actual multi-statement
function body). models.convert_to_live_profile and models.select_profile
(the two functions this tool's route calls -- see "Spec correction #1"
below) both leave config.trader/config.trader-simulator completely
untouched; only profile.profile_type (a label) changes. The one
HTTP-visible route this project found that DOES set config.trader.enabled
(_save_distribution_user_config, reached via save_prediction_market_configuration
in OctoBot's web-interface source) is registered only when the running
OctoBot instance's distribution is OctoBotDistribution.PREDICTION_MARKET
-- an elif branch mutually exclusive with the DEFAULT distribution
this project assumes throughout (confirmed against
controllers/__init__.py::register()); it has nothing to do with
OctoBot's onboarding wizard (a separate, always-registered
controllers/welcome.py). This route is therefore unreachable on the
ordinary/default OctoBot instance this server targets, not because of
this project's own "onboarding" out-of-scope exclusion (a previous
version of this docstring cited that exclusion; it was the wrong
reason, even though the practical conclusion below still holds).
As of this writing, this server has NO tool that can actually toggle
real-vs-simulated trading on a default OctoBot instance. If you need
that, it currently requires editing config.trader/config.trader-simulator
in the profile's own saved config file directly (outside this server)
and re-importing/re-selecting the profile -- there is no safer,
HTTP-API-driven path this server can offer today.
If confirm is not exactly true, no OctoBot call is made at all --
this returns require_confirmation's structured refusal (a normal
return, not an error) instead.
Once confirmed, maps to GET /profiles_management/use_as_live?profile_id=<id>
(confirmed against source controllers/configuration.py's
"use_as_live" action).
Spec correction #1, verified against source and reproduced live:
this route's one non-raising code path calls
models.convert_to_live_profile(profile_id) then
models.select_profile(profile_id), flashes an HTML-only session
message, and returns flask.redirect(flask.url_for("profile")) -- an
HTTP 302 to /profile with no informative body at all, whether or not
the conversion actually took effect. This is the same
uninformative-GET-response category milestone 5 already found for
duplicate (also a bare success marker) and select_profile (a 200
that can silently mean "nothing changed"). Following the same pattern
select_profile established, this tool never trusts the response
status alone: it re-fetches via the same scrape list_profiles/
get_profile use afterward and raises ProfileConversionFailedError
if profile_id isn't selected there (see spec correction #2 for why
only is_selected, not profile_type, is re-checked this way).
Spec correction #2 -- a genuine terminology collision in OctoBot's own
data model, found live and confirmed against source, NOT just a
misreading of this milestone's own instructions: is_selected/
profile_type were expected to both be independently re-verifiable
post-hoc via the same scrape list_profiles uses (mirroring
select_profile's pattern). is_selected is: the
profile-overview-selected CSS class this scrape already parses
reliably reflects models.select_profile(profile_id)'s effect.
profile_type, however, is NOT reverified here, because the scrape's
profile_type field (list_profiles/get_profile's "LIVE"/
"SIMULATOR"/"UNKNOWN") and the profile.profile_type attribute
models.convert_to_live_profile actually sets are two unrelated
OctoBot concepts that merely share a confusingly similar name:
The scrape's
profile_typecomes from thebadge-infobadge text, which is the return value ofget_enabled_trader(profile)(confirmed against sourceflask_util/context_processor.py):"Real trading"ifftrading_util.is_trader_enabled(profile.config), else"Simulated trading"iffis_trader_simulator_enabled, else no badge at all -- entirely about whether a real (vs. simulated) trader is enabled in that profile's OWN trading config.models.convert_to_live_profile(confirmed against sourcemodels/profiles.py) only doesprofile.profile_type = commons_enums.ProfileType.LIVE; profile.validate_and_save_config().octobot_commons.enums.ProfileType(confirmed against source) has exactly two members,LIVE = "live"andBACKTESTING = "backtesting"-- there is noSIMULATORmember at all, and this attribute is never rendered anywhere in/profiles_selector's HTML (confirmed by readingcomponents/config/profiles.htmlandcontext_processor.py: neither referencesprofile.profile_type).
Concretely (reproduced live against a freshly-duplicated, default
SIMULATOR-trader profile): after a successful convert_profile_to_live
call, the scrape's profile_type for that profile is still
"SIMULATOR" -- convert_to_live_profile never touches the
trader-enabled config the badge reflects. Gating this tool's success on
profile_type == "LIVE" (as this milestone's own instructions
originally called for) would therefore make it report failure on
essentially every real invocation, which is worse than not checking it
at all. There is no OctoBot HTTP-visible signal (Tier A or B) this
server can use to independently confirm profile.profile_type flipped
-- an acknowledged, documented gap (the same category as
update_profile's undeliverable config field from milestone 5), not
a silently-accepted assumption. This tool's "profile_type": "LIVE" in
its own output below is therefore an echo of what was requested (and,
per convert_to_live_profile's unconditional, non-branching
implementation, reliably applied whenever this call doesn't raise --
see spec correction #3), not an independently re-scraped fact.
Spec correction #3, on why a non-5xx response is nonetheless a
reasonably strong signal for the profile_type half specifically:
unlike remove_profile's check-and-return-(result, err) pattern,
convert_to_live_profile's body (confirmed against source) has no
conditional branch that could skip the profile.profile_type = ProfileType.LIVE assignment -- it either runs to completion (assigns,
then saves) or raises (propagating to the uncaught-500 path this tool
already classifies as OctoBotServerError). A non-5xx response
therefore does mean that assignment executed and was saved; the
remaining genuine uncertainty this tool resolves by re-checking is only
whether the immediately-following models.select_profile(profile_id)
call (a separate function with its own historically-silent-failure mode
for unknown ids, per select_profile's docstring) actually took
effect -- which is exactly what the is_selected re-check above
verifies.
An exception raised inside OctoBot's own handling (e.g. an unrecognized
profile_id, which makes the underlying get_profile() raise) is not
caught by this route at all, so it propagates to OctoBot's global error
handler as an HTTP 500 -- classified here as OctoBotServerError, with
the Content-Type: application/json request-header hint (same
established technique as duplicate/select_profile/export_profile)
so that handler's message is readable JSON instead of an HTML page.
Output on success: {"profile_id": str, "profile_type": "LIVE", "selected": true}.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | ||
| profile_id | Yes |