Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
env:
BINANCE_API_KEY: ${{ secrets.BINANCE_API_KEY }}
BINANCE_API_SECRET: ${{ secrets.BINANCE_API_SECRET }}
STRATEGY_PROFILE: ${{ vars.STRATEGY_PROFILE || 'crypto_leader_rotation' }}
STRATEGY_PROFILE: ${{ vars.STRATEGY_PROFILE || 'crypto_live_pool_rotation' }}
TG_TOKEN: ${{ secrets.TG_TOKEN }}
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
NOTIFY_LANG: ${{ vars.NOTIFY_LANG }}
Expand Down
2 changes: 1 addition & 1 deletion application/cycle_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def run_live_cycle(
platform="binance",
deploy_target=os.getenv("LOG_DEPLOY_TARGET", "vps"),
service_name=os.getenv("SERVICE_NAME", "binance-platform"),
strategy_profile=str(getattr(runtime, "strategy_profile", "") or os.getenv("STRATEGY_PROFILE", "crypto_leader_rotation")),
strategy_profile=str(getattr(runtime, "strategy_profile", "") or os.getenv("STRATEGY_PROFILE", "crypto_live_pool_rotation")),
run_id=str(getattr(runtime, "run_id", "") or ""),
extra_fields={
"dry_run": bool(getattr(runtime, "dry_run", False)),
Expand Down
2 changes: 1 addition & 1 deletion degraded_mode_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def resolve_trend_pool_source(
)
messages.extend(last_good_result.get("errors", []))

configured_path = get_strategy_artifact_env("STRATEGY_ARTIFACT_FILE", "TREND_POOL_FILE")
configured_path = get_strategy_artifact_env("STRATEGY_ARTIFACT_FILE")
file_candidates = build_strategy_artifact_file_candidates(
configured_path=configured_path,
default_candidates=get_default_live_pool_candidates(default_live_pool_legacy_path),
Expand Down
4 changes: 2 additions & 2 deletions docs/binance_platform_rename_checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This checklist records the completed runtime repository rename from `BinanceQuan
- Current runtime model: Oracle Cloud / VPS hosted self-hosted runner
- Current runtime trigger: external scheduler calling GitHub `workflow_dispatch`
- Current strategy domain: `crypto`
- Current live profile: `crypto_leader_rotation`
- Current live profile: `crypto_live_pool_rotation`

## Why this rename is different from Cloud Run repos

Expand Down Expand Up @@ -88,7 +88,7 @@ These can be updated in the repo during the rename change:
- GCP project id `binancequant`
- Firestore database name / location
- strategy domain `crypto`
- current profile `crypto_leader_rotation`
- current profile `crypto_live_pool_rotation`

## Blocking condition

Expand Down
8 changes: 4 additions & 4 deletions docs/operator_runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Degraded mode:

- Source is `last_known_good`, `local_file`, or `static`
- New trend buys are paused by default
- Set `STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` only if you intentionally want degraded-mode entries; `TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` remains a compatibility alias for the current live profile
- Set `STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED=1` only if you intentionally want degraded-mode entries

Interpretation:

Expand All @@ -98,13 +98,13 @@ Interpretation:

## Strategy Artifact Settings

Use the generic `STRATEGY_ARTIFACT_*` names for new crypto strategies. The older `TREND_POOL_*` names are accepted only as compatibility aliases for `crypto_leader_rotation`.
Use the generic `STRATEGY_ARTIFACT_*` names for crypto strategy artifacts.

Primary settings:

- `STRATEGY_PROFILE`: live profile selector; current supported value is `crypto_leader_rotation`
- `STRATEGY_PROFILE`: live profile selector; current supported value is `crypto_live_pool_rotation`
- `STRATEGY_ARTIFACT_FIRESTORE_COLLECTION`: upstream artifact collection, default `strategy`
- `STRATEGY_ARTIFACT_FIRESTORE_DOCUMENT`: upstream artifact document, default `CRYPTO_LEADER_ROTATION_LIVE_POOL`
- `STRATEGY_ARTIFACT_FIRESTORE_DOCUMENT`: upstream artifact document, default `CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL`
- `STRATEGY_ARTIFACT_FILE`: local fallback artifact path
- `STRATEGY_ARTIFACT_MAX_AGE_DAYS`: freshness window for upstream `as_of_date`
- `STRATEGY_ARTIFACT_ACCEPTABLE_MODES`: comma-separated accepted upstream modes
Expand Down
6 changes: 3 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@

DEFAULT_LIVE_POOL_LEGACY_PATH = STRATEGY_RUNTIME.default_local_artifact_path
DEFAULT_TREND_POOL_FIRESTORE_COLLECTION = "strategy"
DEFAULT_TREND_POOL_FIRESTORE_DOCUMENT = "CRYPTO_LEADER_ROTATION_LIVE_POOL"
DEFAULT_TREND_POOL_FIRESTORE_DOCUMENT = "CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL"
RETIRED_TREND_POSITIONS_KEY = "retired_trend_positions"
TREND_POOL_LAST_GOOD_PAYLOAD_KEY = "trend_pool_last_good_payload"
TREND_POOL_ACTION_HISTORY_KEY = "trend_action_history"
Expand Down Expand Up @@ -532,8 +532,8 @@ def maybe_send_periodic_btc_status_report(
notifier_fn=None,
):
resolved_strategy_display_name = strategy_display_name or build_strategy_display_name(t)(
getattr(STRATEGY_RUNTIME, "profile", "crypto_leader_rotation"),
fallback_name="Crypto Leader Rotation",
getattr(STRATEGY_RUNTIME, "profile", "crypto_live_pool_rotation"),
fallback_name="Crypto Live Pool Rotation",
)
return report_maybe_send_periodic_btc_status_report(
state,
Expand Down
4 changes: 2 additions & 2 deletions notify_i18n_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
"usdt_unavailable_for_trend_buy": "USDT unavailable for trend buy",
"usdt_unavailable_for_btc_dca_buy": "USDT unavailable for BTC DCA buy",
"btc_unavailable_for_dca_sell": "BTC unavailable for DCA sell",
"strategy_name_crypto_leader_rotation": "Crypto Leader Rotation",
"strategy_name_crypto_live_pool_rotation": "Crypto Live Pool Rotation",
},
"zh": {
"telegram_prefix": "🤖 加密量化助手",
Expand Down Expand Up @@ -253,7 +253,7 @@
"usdt_unavailable_for_trend_buy": "USDT 不足,无法执行趋势买入",
"usdt_unavailable_for_btc_dca_buy": "USDT 不足,无法执行 BTC 定投买入",
"btc_unavailable_for_dca_sell": "BTC 不足,无法执行定投止盈卖出",
"strategy_name_crypto_leader_rotation": "加密领涨轮动",
"strategy_name_crypto_live_pool_rotation": "加密领涨轮动",
},
}

Expand Down
2 changes: 1 addition & 1 deletion research/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
if str(path) not in sys.path:
sys.path.insert(0, str(path))

from crypto_strategies.strategies.crypto_leader_rotation.core import (
from crypto_strategies.strategies.crypto_live_pool_rotation.core import (
allocate_trend_buy_budget,
build_rotation_pool_ranking,
compute_allocation_budgets,
Expand Down
11 changes: 1 addition & 10 deletions runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

from notify_i18n_support import build_strategy_display_name, build_translator, get_notify_lang
from runtime_support import ExecutionRuntime
from strategy_artifact_support import get_strategy_artifact_env
from strategy_registry import (
BINANCE_PLATFORM,
resolve_strategy_definition,
Expand All @@ -29,13 +28,6 @@ def get_env_bool(name: str, default: bool = False) -> bool:
return str(value).strip().lower() in {"1", "true", "yes", "y", "on"}


def get_env_bool_alias(name: str, legacy_name: str, default: bool = False) -> bool:
value = get_strategy_artifact_env(name, legacy_name)
if not value:
return bool(default)
return str(value).strip().lower() in {"1", "true", "yes", "y", "on"}


def get_env_csv(name: str, default_values: list[str] | tuple[str, ...]) -> list[str]:
raw = os.getenv(name)
if raw is None or not str(raw).strip():
Expand Down Expand Up @@ -69,9 +61,8 @@ def load_cycle_execution_settings() -> CycleExecutionSettings:
)
return CycleExecutionSettings(
btc_status_report_interval_hours=max(1, min(24, get_env_int("BTC_STATUS_REPORT_INTERVAL_HOURS", 24))),
allow_new_trend_entries_on_degraded=get_env_bool_alias(
allow_new_trend_entries_on_degraded=get_env_bool(
"STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED",
"TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED",
False,
),
strategy_profile=strategy_definition.profile,
Expand Down
2 changes: 1 addition & 1 deletion runtime_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def build_execution_report(runtime):
platform="binance",
deploy_target=os.getenv("LOG_DEPLOY_TARGET", "vps"),
service_name=os.getenv("SERVICE_NAME", "binance-platform"),
strategy_profile=str(runtime.strategy_profile or os.getenv("STRATEGY_PROFILE", "crypto_leader_rotation")),
strategy_profile=str(runtime.strategy_profile or os.getenv("STRATEGY_PROFILE", "crypto_live_pool_rotation")),
strategy_domain=str(runtime.strategy_domain or os.getenv("STRATEGY_DOMAIN", "crypto")),
run_id=str(runtime.run_id),
run_source="github_actions" if os.getenv("GITHUB_RUN_ID") or os.getenv("GITHUB_ACTIONS") else "runtime",
Expand Down
11 changes: 2 additions & 9 deletions scripts/print_strategy_switch_env_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,9 @@ def build_switch_plan(profile: str) -> dict[str, object]:
"STRATEGY_ARTIFACT_ACCEPTABLE_MODES",
"STRATEGY_ARTIFACT_EXPECTED_SIZE",
"STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED",
"TREND_POOL_FILE",
"TREND_POOL_FIRESTORE_COLLECTION",
"TREND_POOL_FIRESTORE_DOCUMENT",
"TREND_POOL_MAX_AGE_DAYS",
"TREND_POOL_ACCEPTABLE_MODES",
"TREND_POOL_EXPECTED_SIZE",
"TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED",
]
notes = [
"Binance runtime resolves strategy artifacts through STRATEGY_ARTIFACT_* settings; TREND_POOL_* remains accepted as a compatibility alias for crypto_leader_rotation.",
"Binance runtime resolves strategy artifacts through STRATEGY_ARTIFACT_* settings.",
"Switching is mainly STRATEGY_PROFILE plus the shared strategy artifact settings.",
"Keep exchange credentials and Telegram settings stable across strategy switches.",
]
Expand All @@ -78,7 +71,7 @@ def build_switch_plan(profile: str) -> dict[str, object]:
"remove_if_present": [],
"hints": {
"strategy_artifact_default_firestore_collection": "strategy",
"strategy_artifact_default_firestore_document": "CRYPTO_LEADER_ROTATION_LIVE_POOL",
"strategy_artifact_default_firestore_document": "CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL",
"default_local_artifact": str(ROOT / "artifacts" / "live_pool_legacy.json"),
"default_local_artifact_manifest": str(ROOT / "artifacts" / "artifact_manifest.json"),
},
Expand Down
2 changes: 1 addition & 1 deletion strategy_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
BINANCE_PLATFORM = "binance"


DEFAULT_STRATEGY_PROFILE = "crypto_leader_rotation"
DEFAULT_STRATEGY_PROFILE = "crypto_live_pool_rotation"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Update the pinned strategy package before changing the default

requirements-lock.txt:2 and requirements.txt:2 still install crypto-strategies@v0.4.7; I checked that tag's catalog/runtime adapter and it only registers crypto_leader_rotation. When the GitHub workflow or any live run relies on the default profile, resolve_strategy_definition(None) now selects crypto_live_pool_rotation, which the installed package cannot resolve, so the runtime will fail before the cycle starts. Please bump the dependency to a version that contains crypto_live_pool_rotation before making it the default.

Useful? React with 👍 / 👎.

ROLLBACK_STRATEGY_PROFILE = DEFAULT_STRATEGY_PROFILE

STRATEGY_CATALOG = get_strategy_catalog()
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/cycle_replay/pool_input_snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
"LTCUSDT": {"base_asset": "LTC"},
"BCHUSDT": {"base_asset": "BCH"}
},
"source_project": "crypto-leader-rotation"
"source_project": "crypto-live-pool-pipelines"
}
16 changes: 8 additions & 8 deletions tests/test_cycle_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def fake_execute_cycle(runtime):
with patch.dict(
os.environ,
{
"STRATEGY_PROFILE": "crypto_leader_rotation",
"STRATEGY_PROFILE": "crypto_live_pool_rotation",
"SERVICE_NAME": "binance-quant",
},
clear=False,
Expand Down Expand Up @@ -65,7 +65,7 @@ def test_run_live_cycle_emits_structured_runtime_events(self):
with patch.dict(
os.environ,
{
"STRATEGY_PROFILE": "crypto_leader_rotation",
"STRATEGY_PROFILE": "crypto_live_pool_rotation",
"SERVICE_NAME": "binance-quant",
"LOG_DEPLOY_TARGET": "vps",
},
Expand All @@ -75,8 +75,8 @@ def test_run_live_cycle_emits_structured_runtime_events(self):
runtime_builder=lambda: SimpleNamespace(
run_id="run-001",
dry_run=True,
strategy_profile="crypto_leader_rotation",
strategy_display_name="Crypto Leader Rotation",
strategy_profile="crypto_live_pool_rotation",
strategy_display_name="Crypto Live Pool Rotation",
strategy_display_name_localized="加密领涨轮动",
),
execute_cycle=lambda _runtime: {
Expand All @@ -101,8 +101,8 @@ def test_run_live_cycle_emits_structured_runtime_events(self):
start_log = json.loads(observed["printed"][0])
end_log = json.loads(observed["printed"][2])
self.assertEqual(start_log["event"], "strategy_cycle_started")
self.assertEqual(start_log["strategy_profile"], "crypto_leader_rotation")
self.assertEqual(start_log["strategy_display_name"], "Crypto Leader Rotation")
self.assertEqual(start_log["strategy_profile"], "crypto_live_pool_rotation")
self.assertEqual(start_log["strategy_display_name"], "Crypto Live Pool Rotation")
self.assertEqual(start_log["strategy_display_name_localized"], "加密领涨轮动")
self.assertEqual(start_log["run_id"], "run-001")
self.assertEqual(end_log["event"], "strategy_cycle_completed")
Expand All @@ -116,7 +116,7 @@ def test_run_live_cycle_uses_shared_runtime_report_archive(self):
with patch.dict(
os.environ,
{
"STRATEGY_PROFILE": "crypto_leader_rotation",
"STRATEGY_PROFILE": "crypto_live_pool_rotation",
"SERVICE_NAME": "binance-quant",
"EXECUTION_REPORT_GCS_URI": "gs://demo-bucket/runtime-reports",
"GCP_PROJECT_ID": "demo-project",
Expand All @@ -133,7 +133,7 @@ def test_run_live_cycle_uses_shared_runtime_report_archive(self):
)
or SimpleNamespace(
local_path=kwargs.get("output_path"),
gcs_uri="gs://demo-bucket/runtime-reports/binance/crypto_leader_rotation/2026-04/run-001.json",
gcs_uri="gs://demo-bucket/runtime-reports/binance/crypto_live_pool_rotation/2026-04/run-001.json",
),
):
report, persisted_path = run_live_cycle(
Expand Down
6 changes: 3 additions & 3 deletions tests/test_main_runtime_error_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ class BinanceAPIException(Exception):
strategy_registry_module = types.ModuleType("strategy_registry")
strategy_registry_module.BINANCE_PLATFORM = "binance"
strategy_registry_module.resolve_strategy_definition = lambda *_args, **_kwargs: types.SimpleNamespace(
profile="crypto_leader_rotation",
profile="crypto_live_pool_rotation",
domain="crypto",
)
strategy_registry_module.resolve_strategy_metadata = lambda *_args, **_kwargs: types.SimpleNamespace(
display_name="Crypto Leader Rotation",
display_name="Crypto Live Pool Rotation",
)
sys.modules["strategy_registry"] = strategy_registry_module

Expand Down Expand Up @@ -112,7 +112,7 @@ def fake_send_tg_msg(token, chat_id, text):
{
"TG_TOKEN": "token-1",
"GLOBAL_TELEGRAM_CHAT_ID": "chat-1",
"STRATEGY_PROFILE": "crypto_leader_rotation",
"STRATEGY_PROFILE": "crypto_live_pool_rotation",
},
clear=False,
):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_notify_i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_trend_pool_source_logs_use_chinese_when_notify_lang_is_zh(self):
"mode": "core_major",
"version": "2026-03-10-core_major",
"as_of_date": "2026-03-10",
"source_project": "crypto-leader-rotation",
"source_project": "crypto-live-pool-pipelines",
"messages": ["payload stale", "using cached pool"],
"degraded": True,
},
Expand Down
4 changes: 2 additions & 2 deletions tests/test_rotation_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
if str(path) not in sys.path:
sys.path.insert(0, str(path))

from crypto_strategies.strategies.crypto_leader_rotation.rotation import ( # noqa: E402
from crypto_strategies.strategies.crypto_live_pool_rotation.rotation import ( # noqa: E402
get_trend_sell_reason,
plan_trend_buys,
refresh_rotation_pool,
)
from crypto_strategies.strategies.crypto_leader_rotation.core import allocate_trend_buy_budget # noqa: E402
from crypto_strategies.strategies.crypto_live_pool_rotation.core import allocate_trend_buy_budget # noqa: E402


class RotationStrategyTests(unittest.TestCase):
Expand Down
25 changes: 12 additions & 13 deletions tests/test_runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_load_cycle_execution_settings_clamps_interval_and_reads_degraded_flag(s
os.environ,
{
"BTC_STATUS_REPORT_INTERVAL_HOURS": "48",
"TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1",
"STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1",
},
clear=False,
):
Expand All @@ -41,22 +41,21 @@ def test_load_cycle_execution_settings_clamps_interval_and_reads_degraded_flag(s
self.assertEqual(settings.btc_status_report_interval_hours, 24)
self.assertTrue(settings.allow_new_trend_entries_on_degraded)
self.assertEqual(settings.strategy_profile, DEFAULT_STRATEGY_PROFILE)
self.assertEqual(settings.strategy_display_name, "Crypto Leader Rotation")
self.assertEqual(settings.strategy_display_name_localized, "Crypto Leader Rotation")
self.assertEqual(settings.strategy_display_name, "Crypto Live Pool Rotation")
self.assertEqual(settings.strategy_display_name_localized, "Crypto Live Pool Rotation")
self.assertEqual(settings.strategy_domain, CRYPTO_DOMAIN)

def test_load_cycle_execution_settings_accepts_strategy_artifact_degraded_alias(self):
def test_load_cycle_execution_settings_ignores_legacy_trend_pool_degraded_alias(self):
with patch.dict(
os.environ,
{
"STRATEGY_ARTIFACT_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1",
"TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "0",
"TREND_POOL_ALLOW_NEW_ENTRIES_ON_DEGRADED": "1",
},
clear=False,
clear=True,
):
settings = load_cycle_execution_settings()

self.assertTrue(settings.allow_new_trend_entries_on_degraded)
self.assertFalse(settings.allow_new_trend_entries_on_degraded)

def test_load_cycle_execution_settings_rejects_unknown_strategy_profile(self):
with patch.dict(os.environ, {"STRATEGY_PROFILE": "global_etf_rotation"}, clear=False):
Expand All @@ -77,7 +76,7 @@ def test_platform_profile_status_matrix_marks_default_profile_eligible_and_enabl
{
"platform": BINANCE_PLATFORM,
"canonical_profile": DEFAULT_STRATEGY_PROFILE,
"display_name": "Crypto Leader Rotation",
"display_name": "Crypto Live Pool Rotation",
"eligible": True,
"enabled": True,
"is_default": True,
Expand Down Expand Up @@ -115,7 +114,7 @@ def test_build_live_runtime_reads_env_and_preserves_injected_hooks(self):
self.assertEqual(runtime.tg_token, "tg-token")
self.assertEqual(runtime.tg_chat_id, "chat-id")
self.assertEqual(runtime.strategy_profile, DEFAULT_STRATEGY_PROFILE)
self.assertEqual(runtime.strategy_display_name, "Crypto Leader Rotation")
self.assertEqual(runtime.strategy_display_name, "Crypto Live Pool Rotation")
self.assertIs(runtime.state_loader, state_loader)
self.assertIs(runtime.state_writer, state_writer)
self.assertIs(runtime.notifier, notifier)
Expand Down Expand Up @@ -179,10 +178,10 @@ def test_switch_env_plan_script_json_matches_binance_runtime_shape(self):
self.assertIn("TG_TOKEN", plan["keep_env"])
self.assertIn("STRATEGY_ARTIFACT_FILE", plan["optional_env"])
self.assertIn("STRATEGY_ARTIFACT_MANIFEST_FILE", plan["optional_env"])
self.assertIn("TREND_POOL_FILE", plan["optional_env"])
self.assertNotIn("TREND_POOL_FILE", plan["optional_env"])
self.assertEqual(
plan["hints"]["strategy_artifact_default_firestore_document"],
"CRYPTO_LEADER_ROTATION_LIVE_POOL",
"CRYPTO_LIVE_POOL_ROTATION_LIVE_POOL",
)
self.assertEqual(plan["remove_if_present"], [])

Expand All @@ -197,7 +196,7 @@ def test_switch_env_plan_script_table_contains_expected_sections(self):
)

self.assertIn("platform: binance", result.stdout)
self.assertIn("profile: crypto_leader_rotation", result.stdout)
self.assertIn("profile: crypto_live_pool_rotation", result.stdout)
self.assertIn("set_env:", result.stdout)
self.assertIn("keep_env:", result.stdout)
self.assertIn("optional_env:", result.stdout)
Expand Down
Loading