X-Trader auto_strategy: migration, timed sell, sl_protection, phased_sl, anti_pvp
auto_strategy is the required object inside config that holds exit rules that are not plain TP/SL stages: selling on migration, a single delayed full sell, clearing dust or stalled positions, profit protection, a delayed stop-loss (sl_protection), a two-phase stop-loss (phased_sl), a temporary block on targets that sell quickly (anti_pvp) and a per-config sell fee override. Only migration_clear, timed_sell and sell_delay are required; every other rule is optional.
Primary source: X-Trader API reference. This page is an independent explanation of X-Trader Public API v1.0.0; it is not affiliated with X-Trader.
Where auto_strategy lives
Section titled “Where auto_strategy lives”auto_strategy is a required key of config in the sniper, copy-trade and transfer-snipe Create and Patch schemas and in both template schemas. The object schema is identical in all of them. It works next to auto_sell and timed_strategy. The object and every nested object are strict: unknown keys return validation_error. All numeric values are decimal strings, max 32 characters (see write rules).
Field overview
Section titled “Field overview”| Field | Type | Required | Purpose |
|---|---|---|---|
migration_clear |
boolean | yes | Sell the whole position when migration is detected. |
migration_sell |
object | no | Sell a percentage on migration. |
timed_sell |
boolean | yes | Enable one delayed full-position sell. |
sell_delay |
string | yes | Delay for timed_sell, seconds "0" to "31536000". |
remaining_position_clear |
object | no | Clear a small remaining position (SOL value). |
tp1_timeout_clear |
object | no | Clear if TP1 is not reached in time. |
price_stagnation_clear |
object | no | Clear if price stays unchanged. |
profit_protection |
object | no | Reduce the position while profit holds above a threshold. |
sl_protection |
object | no | Delayed one-shot stop-loss. |
phased_sl |
object | no | Two-phase live stop-loss. |
anti_pvp |
object | no | Temporarily block a target that sells soon after buying. |
sell_fee_override |
object | no | Per-config sell Priority Fee and Tip. |
Every optional object has its own enabled flag, and all of its keys are required whenever the object is present (even with enabled: false).
Migration rules
Section titled “Migration rules”| Field | Type | Values | Meaning |
|---|---|---|---|
migration_clear |
boolean | On detected migration, sell 100%. | |
migration_sell.enabled |
boolean | Turns the percentage rule on. | |
migration_sell.percent |
string | greater than 0, at most 100 | Share of the position to sell on migration. |
When both migration_clear and migration_sell.enabled are true, migration_clear takes priority. The spec does not define which events count as migration.
Timed sell
Section titled “Timed sell”| Field | Type | Values | Meaning |
|---|---|---|---|
timed_sell |
boolean | Enables a single delayed sell of the full position. | |
sell_delay |
string | "0" to "31536000" seconds (365 days) |
Delay before that sell. |
sell_delay is required even when timed_sell is false; send "0" in that case. For several partial sells at different times use timed_strategy instead.
Clear rules
Section titled “Clear rules”| Object | value meaning |
Limits stated in the spec |
|---|---|---|
remaining_position_clear |
Decimal SOL amount | minimum "0.01" |
tp1_timeout_clear |
Seconds | not stated |
price_stagnation_clear |
Seconds | not stated |
Each object is { "enabled": <bool>, "value": "<string>" }.
remaining_position_clearclears a small leftover position. The spec gives only the SOL unit and minimum; the practical reading is that a remainder worth less thanvalueSOL is sold out.tp1_timeout_clearclears the position when the first take-profit stage ofauto_sell.tp_ordershas not been reached withinvalueseconds.price_stagnation_clearclears the position when the price stays unchanged forvalueseconds.
profit_protection
Section titled “profit_protection”| Field | Type | Values | Meaning |
|---|---|---|---|
enabled |
boolean | ||
threshold |
string | profit percent | Profit level that must hold. |
price_checks |
string | "1" to "10000" |
Consecutive price checks above threshold required. |
reduce_ratio |
string | "0" to "100" |
Percentage by which the position is reduced. |
When profit stays above threshold for price_checks consecutive checks, the position is reduced by reduce_ratio percent. The PnL used here honours auto_sell.ignore_fee_for_auto_sell. The spec does not say how often a price check happens.
sl_protection (delayed stop-loss)
Section titled “sl_protection (delayed stop-loss)”| Field | Type | Values | Meaning |
|---|---|---|---|
enabled |
boolean | ||
seconds |
string | "0" to "86400", 0.1 s precision |
Grace window after which the rule is evaluated once. |
trigger |
string | below 0, no lower than "-100" |
PnL threshold, mark-to-entry. |
Behaviour:
- During the first
secondsthe rule never sells. - At expiry, if mark-to-entry PnL is at or below
trigger, 100% of the position is sold. - If PnL is above
triggerat expiry, the rule ends; later drops are ignored by this rule.
auto_sell.sl_orders keep firing immediately during and after the window.
phased_sl (two-phase stop-loss)
Section titled “phased_sl (two-phase stop-loss)”| Field | Type | Values | Meaning |
|---|---|---|---|
enabled |
boolean | Omit the object or set false to keep it off. |
|
seconds |
string | "0.1" to "31536000" |
Length of the early window. |
early_trigger |
string | "-100" to "-1" |
Stop used before seconds elapse. |
late_trigger |
string | "-100" to "-1" |
Stop used after the early window. |
On every price tick the active stop is compared with PnL; a hit sells 100%. The rule does not sell by itself at the moment the window ends, it only switches stops. early_trigger and late_trigger are independent, so either can be the tighter one: for example a loose -50 early (to survive launch volatility) and a tight -20 later, or the reverse. sl_orders and sl_protection continue to follow their own rules.
sl_protection vs phased_sl
Section titled “sl_protection vs phased_sl”sl_protection |
phased_sl |
|
|---|---|---|
| Sells before the deadline | never | yes, on early_trigger |
| At the deadline | one check, sells 100% if PnL at or below trigger |
switches to late_trigger, no sell by itself |
| After the deadline | inactive | live on late_trigger |
| Sell size | 100% | 100% |
anti_pvp
Section titled “anti_pvp”| Field | Type | Meaning |
|---|---|---|
enabled |
boolean | |
holding_time |
string, seconds | Observation window after the buy. |
ban_duration |
string, seconds | How long the target is blocked. |
If the target sells within holding_time seconds of buying, it is temporarily blocked for ban_duration seconds. The spec does not give numeric limits for either value, and does not describe what “blocked” means for a sniper target as opposed to a copy-trade target; the description refers to the target wallet in both cases.
sell_fee_override
Section titled “sell_fee_override”| Field | Type | Values |
|---|---|---|
enabled |
boolean | |
priority_fee |
string | SOL, "0" to "1000" |
tip_amount |
string | SOL, "0" to "1000"; at least "0.0001" when enabled |
When enabled, these sell fees take precedence over the account’s global defaults for this configuration.
Example inside a create body
Section titled “Example inside a create body”{ "target_wallet": "<solana-address>", "config": { "auto_strategy": { "migration_clear": false, "migration_sell": { "enabled": true, "percent": "50" }, "timed_sell": true, "sell_delay": "3600", "remaining_position_clear": { "enabled": true, "value": "0.01" }, "tp1_timeout_clear": { "enabled": true, "value": "900" }, "price_stagnation_clear": { "enabled": false, "value": "300" }, "profit_protection": { "enabled": true, "threshold": "40", "price_checks": "5", "reduce_ratio": "50" }, "sl_protection": { "enabled": true, "seconds": "30", "trigger": "-40" }, "phased_sl": { "enabled": true, "seconds": "60", "early_trigger": "-50", "late_trigger": "-20" }, "anti_pvp": { "enabled": true, "holding_time": "10", "ban_duration": "86400" }, "sell_fee_override": { "enabled": true, "priority_fee": "0.001", "tip_amount": "0.001" } }, "auto_sell": { "enabled": true, "expiry_time": "0", "tp_orders": [{ "trigger": "100", "sell": "50" }], "sl_orders": [], "follow_sell": false, "proportional_sell": false }, "timed_strategy": { "enabled": false, "orders": [] } }}The other required config keys are omitted; see sniper config and copy-trade config.
Updating auto_strategy on an existing config
Section titled “Updating auto_strategy on an existing config”A PATCH with config replaces the whole nested config and is validated against the full schema, so read the current config, change auto_strategy, and send the complete config with the ETag as If-Match. Changing config requires a paused configuration (config_must_be_paused otherwise); a stale ETag returns version_conflict. Snapshots returned by GET may include legacy fields the strict schema rejects; error.fields lists the offending paths.
ID='<config-uuid>'BASE='https://x-trader.ai/api/v1'
curl -sD headers.txt "$BASE/sniper/configs/$ID" \ --header "Authorization: Bearer $X_TRADER_API_TOKEN" \ | jq '.data.config' > config.jsonETAG=$(grep -i '^etag:' headers.txt | cut -d' ' -f2 | tr -d '\r')
jq '.auto_strategy.phased_sl = {"enabled":true,"seconds":"120","early_trigger":"-60","late_trigger":"-25"} | .auto_strategy.anti_pvp = {"enabled":true,"holding_time":"15","ban_duration":"43200"}' \ config.json | jq '{config: .}' > patch.json
curl --request PATCH "$BASE/sniper/configs/$ID" \ --header "Authorization: Bearer $X_TRADER_API_TOKEN" \ --header 'Content-Type: application/json' \ --header "If-Match: $ETAG" \ --data @patch.jsonPython
Section titled “Python”import osimport requests
BASE = "https://x-trader.ai/api/v1"CONFIG_ID = "<config-uuid>"session = requests.Session()session.headers["Authorization"] = f"Bearer {os.environ['X_TRADER_API_TOKEN']}"
url = f"{BASE}/sniper/configs/{CONFIG_ID}" # or /copy-trade/configs/{id}resp = session.get(url)resp.raise_for_status()etag = resp.headers["ETag"]resource = resp.json()["data"]if resource["is_active"]: raise SystemExit("Pause the config first (POST .../pause with If-Match)")
config = resource["config"]strategy = config["auto_strategy"]strategy["sl_protection"] = {"enabled": True, "seconds": "20", "trigger": "-35"}strategy["sell_fee_override"] = { "enabled": True, "priority_fee": "0.0005", "tip_amount": "0.0002",}strategy["timed_sell"] = Falsestrategy["sell_delay"] = "0"
patch = session.patch(url, json={"config": config}, headers={"If-Match": etag})if patch.status_code >= 400: print(patch.json()["error"])patch.raise_for_status()print("new version:", patch.headers["ETag"])Which auto_strategy fields are required?
Section titled “Which auto_strategy fields are required?”Only migration_clear (boolean), timed_sell (boolean) and sell_delay (decimal string of seconds). Every other rule is an optional object that can be omitted.
What happens if migration_clear and migration_sell are both enabled?
Section titled “What happens if migration_clear and migration_sell are both enabled?”migration_clear wins: the whole position is sold on migration and the migration_sell percentage is not used.
How is sl_protection different from phased_sl?
Section titled “How is sl_protection different from phased_sl?”sl_protection waits a fixed number of seconds and then sells 100% once, only if PnL is still at or below its trigger. phased_sl is a live stop that uses early_trigger before the deadline and late_trigger after it, selling 100% on any price tick that hits the active stop.
Do sl_protection and phased_sl replace auto_sell.sl_orders?
Section titled “Do sl_protection and phased_sl replace auto_sell.sl_orders?”No. Ordinary sl_orders still fire immediately, and each rule follows its own logic independently.
Is auto_strategy different for sniper and copy-trade?
Section titled “Is auto_strategy different for sniper and copy-trade?”No. The schema is identical in sniper, copy-trade, transfer-snipe and template configs.