diff --git a/.env.example b/.env.example index 1d14616..5626b32 100644 --- a/.env.example +++ b/.env.example @@ -20,16 +20,22 @@ FIRSTRADE_RUNTIME_EXECUTION_WINDOW_TRADING_DAYS= ACCOUNT_PREFIX=FIRSTRADE ACCOUNT_REGION=US NOTIFY_LANG=en +QSL_NOTIFY_LANG= TELEGRAM_TOKEN= GLOBAL_TELEGRAM_CHAT_ID= +QSL_GLOBAL_TELEGRAM_CHAT_ID= FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON= +QSL_STRATEGY_PLUGIN_MOUNTS_JSON= DCA_MODE= +QSL_DCA_MODE= DCA_BASE_INVESTMENT_USD= +QSL_DCA_BASE_INVESTMENT_USD= FIRSTRADE_MARKET_SIGNAL_HANDOFF_INDEX_URI= FIRSTRADE_MARKET_SIGNAL_REQUIRED= FIRSTRADE_MARKET_SIGNAL_FALLBACK_MODE=last_valid FIRSTRADE_MARKET_SIGNAL_MAX_STALE_DAYS=3 EXECUTION_REPORT_GCS_URI= +QSL_EXECUTION_REPORT_GCS_URI= IBIT_ZSCORE_EXIT_ENABLED= IBIT_ZSCORE_EXIT_MODE= IBIT_ZSCORE_EXIT_PARKING_SYMBOL=BOXX diff --git a/.github/workflows/sync-cloud-run-env.yml b/.github/workflows/sync-cloud-run-env.yml index af5b6e1..c760318 100644 --- a/.github/workflows/sync-cloud-run-env.yml +++ b/.github/workflows/sync-cloud-run-env.yml @@ -41,6 +41,7 @@ jobs: ENABLE_GITHUB_CLOUD_RUN_DEPLOY: ${{ vars.ENABLE_GITHUB_CLOUD_RUN_DEPLOY }} ENABLE_GITHUB_ENV_SYNC: ${{ vars.ENABLE_GITHUB_ENV_SYNC }} ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }} + QSL_ENABLE_CLOUD_RUN_AUTOMATION: ${{ vars.QSL_ENABLE_CLOUD_RUN_AUTOMATION }} CLOUD_RUN_REGION: ${{ vars.CLOUD_RUN_REGION }} CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }} CLOUD_SCHEDULER_LOCATION: ${{ vars.CLOUD_SCHEDULER_LOCATION }} @@ -163,6 +164,8 @@ jobs: run: | set -euo pipefail + # QSL_ENABLE_CLOUD_RUN_AUTOMATION overrides ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION + ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION="${QSL_ENABLE_CLOUD_RUN_AUTOMATION:-$ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION}" if [ "${GITHUB_EVENT_NAME:-}" = "push" ] && [ "${ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION:-}" != "true" ]; then echo "enabled=false" >> "$GITHUB_OUTPUT" echo "Skipping Cloud Run deploy on push because ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION is not true." >&2 diff --git a/main.py b/main.py index e943525..2fe2e48 100644 --- a/main.py +++ b/main.py @@ -69,7 +69,7 @@ def _get_telegram_token() -> str: def _telegram_notification_targets() -> tuple[tuple[str, str], ...]: targets: list[tuple[str, str]] = [] main_token = _get_telegram_token() - main_chat_id = os.getenv("GLOBAL_TELEGRAM_CHAT_ID") + main_chat_id = os.getenv("QSL_GLOBAL_TELEGRAM_CHAT_ID") or os.getenv("GLOBAL_TELEGRAM_CHAT_ID") if main_token and main_chat_id: targets.append((main_token, main_chat_id)) @@ -88,7 +88,7 @@ def _runtime_error_notification_message(exc: Exception) -> str: if len(error_text) > 1200: error_text = error_text[:1197] + "..." is_health_check = request.path == "/probe" - if str(os.getenv("NOTIFY_LANG") or "").strip().lower().startswith("zh"): + if str(os.getenv("QSL_NOTIFY_LANG") or os.getenv("NOTIFY_LANG") or "").strip().lower().startswith("zh"): return "\n".join( ( "Firstrade 健康检查失败" if is_health_check else "Firstrade 策略运行失败", @@ -260,7 +260,7 @@ def _strategy_result_http_status(result: dict[str, Any]) -> int: def _persist_runtime_report(report: dict[str, Any]) -> str | None: persisted = persist_runtime_report( report, - cloud_prefix_uri=os.getenv("EXECUTION_REPORT_GCS_URI"), + cloud_prefix_uri=os.getenv("QSL_EXECUTION_REPORT_GCS_URI") or os.getenv("EXECUTION_REPORT_GCS_URI"), project_id=get_project_id(), ) if isinstance(persisted, str): diff --git a/runtime_config_support.py b/runtime_config_support.py index c77c72b..71b81ac 100644 --- a/runtime_config_support.py +++ b/runtime_config_support.py @@ -185,9 +185,9 @@ def load_platform_runtime_settings( strategy_display_name=runtime_paths.strategy_display_name, strategy_domain=runtime_paths.strategy_domain, strategy_metadata=strategy_metadata, - notify_lang=os.getenv("NOTIFY_LANG", "en"), + notify_lang=os.getenv("QSL_NOTIFY_LANG") or os.getenv("NOTIFY_LANG", "en"), tg_token=_get_credential("firstrade-telegram-token", "TELEGRAM_TOKEN"), - tg_chat_id=os.getenv("GLOBAL_TELEGRAM_CHAT_ID"), + tg_chat_id=os.getenv("QSL_GLOBAL_TELEGRAM_CHAT_ID") or os.getenv("GLOBAL_TELEGRAM_CHAT_ID"), dry_run_only=dry_run_only, runtime_target_enabled=_runtime_target_enabled_env(), cash_only_execution=resolve_cash_only_execution_env( @@ -287,6 +287,7 @@ def load_platform_runtime_settings( strategy_config_source=runtime_paths.strategy_config_source, strategy_plugin_mounts_json=( os.getenv("FIRSTRADE_STRATEGY_PLUGIN_MOUNTS_JSON") + or os.getenv("QSL_STRATEGY_PLUGIN_MOUNTS_JSON") or os.getenv("STRATEGY_PLUGIN_MOUNTS_JSON") ), strategy_plugin_alert_channels=resolve_split_env_list("STRATEGY_PLUGIN_ALERT_CHANNELS"), @@ -327,7 +328,8 @@ def load_platform_runtime_settings( "STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS" ), strategy_plugin_alert_telegram_bot_token=_first_non_empty( - os.getenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN") + os.getenv("QSL_STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN") + or os.getenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN") ), strategy_plugin_alert_telegram_api_base_url=_first_non_empty( os.getenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_API_BASE_URL") @@ -346,7 +348,7 @@ def load_platform_runtime_settings( def _resolve_runtime_target(*, dry_run_only: bool) -> RuntimeTarget: - if os.getenv("RUNTIME_TARGET_JSON"): + if os.getenv("QSL_RUNTIME_TARGET_JSON") or os.getenv("RUNTIME_TARGET_JSON"): return resolve_runtime_target_from_env( env=os.environ, expected_platform_id=FIRSTRADE_PLATFORM, @@ -436,7 +438,7 @@ def _resolve_market_signal_handoff_index_uri() -> str | None: if explicit: return explicit return _default_market_signal_handoff_index_uri_from_report_bucket( - os.getenv("EXECUTION_REPORT_GCS_URI"), + os.getenv("QSL_EXECUTION_REPORT_GCS_URI") or os.getenv("EXECUTION_REPORT_GCS_URI"), )