Skip to content

Commit 4434cfe

Browse files
authored
Send crisis plugin alerts by email
Use shared QuantPlatformKit strategy-plugin alert helpers and send independent SMTP email alerts for escalated crisis plugin signals.
1 parent ab1484f commit 4434cfe

12 files changed

Lines changed: 347 additions & 20 deletions

.github/workflows/sync-cloud-run-env.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,22 @@ jobs:
3434
IBKR_MIN_RESERVED_CASH_USD: ${{ vars.IBKR_MIN_RESERVED_CASH_USD }}
3535
IBKR_RESERVED_CASH_RATIO: ${{ vars.IBKR_RESERVED_CASH_RATIO }}
3636
IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD: ${{ vars.IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD }}
37+
CRISIS_ALERT_EMAIL_TO: ${{ vars.CRISIS_ALERT_EMAIL_TO }}
38+
CRISIS_ALERT_EMAIL_FROM: ${{ vars.CRISIS_ALERT_EMAIL_FROM }}
39+
CRISIS_ALERT_SMTP_HOST: ${{ vars.CRISIS_ALERT_SMTP_HOST }}
40+
CRISIS_ALERT_SMTP_PORT: ${{ vars.CRISIS_ALERT_SMTP_PORT }}
41+
CRISIS_ALERT_SMTP_USERNAME: ${{ vars.CRISIS_ALERT_SMTP_USERNAME }}
42+
CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME: ${{ vars.CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME }}
43+
CRISIS_ALERT_SMTP_STARTTLS: ${{ vars.CRISIS_ALERT_SMTP_STARTTLS }}
44+
CRISIS_ALERT_SMTP_SSL: ${{ vars.CRISIS_ALERT_SMTP_SSL }}
3745
# Strategy-owned defaults continue to come from UsEquityStrategies; this workflow only syncs platform/runtime inputs.
3846
EXECUTION_REPORT_GCS_URI: ${{ vars.EXECUTION_REPORT_GCS_URI }}
3947
IB_GATEWAY_ZONE: ${{ vars.IB_GATEWAY_ZONE }}
4048
IB_GATEWAY_IP_MODE: ${{ vars.IB_GATEWAY_IP_MODE }}
4149
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
4250
NOTIFY_LANG: ${{ vars.NOTIFY_LANG }}
4351
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
52+
CRISIS_ALERT_SMTP_PASSWORD: ${{ secrets.CRISIS_ALERT_SMTP_PASSWORD }}
4453
steps:
4554
- name: Check whether env sync is enabled
4655
id: config
@@ -273,6 +282,16 @@ jobs:
273282
remove_secret_vars+=("TELEGRAM_TOKEN")
274283
fi
275284
285+
if [ -n "${CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME:-}" ]; then
286+
secret_pairs+=("CRISIS_ALERT_SMTP_PASSWORD=${CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME}:latest")
287+
elif [ -n "${CRISIS_ALERT_SMTP_PASSWORD:-}" ]; then
288+
env_pairs+=("CRISIS_ALERT_SMTP_PASSWORD=${CRISIS_ALERT_SMTP_PASSWORD}")
289+
remove_secret_vars+=("CRISIS_ALERT_SMTP_PASSWORD")
290+
else
291+
remove_env_vars+=("CRISIS_ALERT_SMTP_PASSWORD")
292+
remove_secret_vars+=("CRISIS_ALERT_SMTP_PASSWORD")
293+
fi
294+
276295
if [ -n "${IB_GATEWAY_ZONE:-}" ]; then
277296
env_pairs+=("IB_GATEWAY_ZONE=${IB_GATEWAY_ZONE}")
278297
else
@@ -345,6 +364,48 @@ jobs:
345364
remove_env_vars+=("IBKR_RESERVED_CASH_RATIO")
346365
fi
347366
367+
if [ -n "${CRISIS_ALERT_EMAIL_TO:-}" ]; then
368+
env_pairs+=("CRISIS_ALERT_EMAIL_TO=${CRISIS_ALERT_EMAIL_TO}")
369+
else
370+
remove_env_vars+=("CRISIS_ALERT_EMAIL_TO")
371+
fi
372+
373+
if [ -n "${CRISIS_ALERT_EMAIL_FROM:-}" ]; then
374+
env_pairs+=("CRISIS_ALERT_EMAIL_FROM=${CRISIS_ALERT_EMAIL_FROM}")
375+
else
376+
remove_env_vars+=("CRISIS_ALERT_EMAIL_FROM")
377+
fi
378+
379+
if [ -n "${CRISIS_ALERT_SMTP_HOST:-}" ]; then
380+
env_pairs+=("CRISIS_ALERT_SMTP_HOST=${CRISIS_ALERT_SMTP_HOST}")
381+
else
382+
remove_env_vars+=("CRISIS_ALERT_SMTP_HOST")
383+
fi
384+
385+
if [ -n "${CRISIS_ALERT_SMTP_PORT:-}" ]; then
386+
env_pairs+=("CRISIS_ALERT_SMTP_PORT=${CRISIS_ALERT_SMTP_PORT}")
387+
else
388+
remove_env_vars+=("CRISIS_ALERT_SMTP_PORT")
389+
fi
390+
391+
if [ -n "${CRISIS_ALERT_SMTP_USERNAME:-}" ]; then
392+
env_pairs+=("CRISIS_ALERT_SMTP_USERNAME=${CRISIS_ALERT_SMTP_USERNAME}")
393+
else
394+
remove_env_vars+=("CRISIS_ALERT_SMTP_USERNAME")
395+
fi
396+
397+
if [ -n "${CRISIS_ALERT_SMTP_STARTTLS:-}" ]; then
398+
env_pairs+=("CRISIS_ALERT_SMTP_STARTTLS=${CRISIS_ALERT_SMTP_STARTTLS}")
399+
else
400+
remove_env_vars+=("CRISIS_ALERT_SMTP_STARTTLS")
401+
fi
402+
403+
if [ -n "${CRISIS_ALERT_SMTP_SSL:-}" ]; then
404+
env_pairs+=("CRISIS_ALERT_SMTP_SSL=${CRISIS_ALERT_SMTP_SSL}")
405+
else
406+
remove_env_vars+=("CRISIS_ALERT_SMTP_SSL")
407+
fi
408+
348409
if [ -n "${EXECUTION_REPORT_GCS_URI:-}" ]; then
349410
env_pairs+=("EXECUTION_REPORT_GCS_URI=${EXECUTION_REPORT_GCS_URI}")
350411
else

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,14 @@ For IBKR, keep `paper` as a single account-group entry. If you later add live ac
112112
| `TELEGRAM_TOKEN` | Yes | Telegram bot token. For Cloud Run, prefer a Secret Manager reference instead of a literal env var. |
113113
| `GLOBAL_TELEGRAM_CHAT_ID` | Yes | Telegram chat ID used by this service. |
114114
| `NOTIFY_LANG` | No | `en` (default) or `zh` |
115+
| `CRISIS_ALERT_EMAIL_TO` | No | Comma/semicolon/newline-separated recipients for escalated crisis-plugin email alerts. Email is skipped when unset. |
116+
| `CRISIS_ALERT_EMAIL_FROM` | No | Sender address for crisis-plugin email alerts. |
117+
| `CRISIS_ALERT_SMTP_HOST` | No | SMTP host for crisis-plugin email alerts. |
118+
| `CRISIS_ALERT_SMTP_PORT` | No | SMTP port; defaults to `587`. |
119+
| `CRISIS_ALERT_SMTP_USERNAME` | No | Optional SMTP username. |
120+
| `CRISIS_ALERT_SMTP_PASSWORD` | No | Optional SMTP password. For Cloud Run, prefer `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME` in env sync. |
121+
| `CRISIS_ALERT_SMTP_STARTTLS` | No | Whether to use STARTTLS; defaults to `true`. |
122+
| `CRISIS_ALERT_SMTP_SSL` | No | Whether to use implicit SMTP SSL; defaults to `false`. |
115123

116124
The selected account-group entry must provide at least:
117125

@@ -207,6 +215,8 @@ Current behavior is fail-fast:
207215
- missing account-group config source → startup error
208216
- missing key fields in the selected group (`ib_gateway_instance_name`, `ib_gateway_mode`, `ib_client_id`) → startup error
209217

218+
When `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal strategy-cycle Telegram message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends an independent crisis email when the `CRISIS_ALERT_*` SMTP settings are complete.
219+
210220
### GitHub-managed Cloud Run env sync
211221

212222
If code deployment still uses Google Cloud Trigger, but you want GitHub to be the single source of truth for runtime env vars, this repo now includes `.github/workflows/sync-cloud-run-env.yml`. The workflow now also emits `RUNTIME_TARGET_JSON`, so the control plane carries a structured runtime target alongside the legacy `STRATEGY_PROFILE` selector.
@@ -222,10 +232,12 @@ Recommended setup:
222232
- `ACCOUNT_GROUP` (recommended: `paper`)
223233
- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`
224234
- Optional: `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON`, `IBKR_MIN_RESERVED_CASH_USD`, `IBKR_RESERVED_CASH_RATIO`, `IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD`
235+
- Optional crisis email alerts: `CRISIS_ALERT_EMAIL_TO`, `CRISIS_ALERT_EMAIL_FROM`, `CRISIS_ALERT_SMTP_HOST`, `CRISIS_ALERT_SMTP_PORT`, `CRISIS_ALERT_SMTP_USERNAME`, `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`, `CRISIS_ALERT_SMTP_STARTTLS`, `CRISIS_ALERT_SMTP_SSL`
225236
- `GLOBAL_TELEGRAM_CHAT_ID`
226237
- `NOTIFY_LANG`
227238
- **Repository Secrets**
228239
- `TELEGRAM_TOKEN` (fallback only when `TELEGRAM_TOKEN_SECRET_NAME` is not set)
240+
- `CRISIS_ALERT_SMTP_PASSWORD` (fallback only when `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME` is not set)
229241
- **Optional transition Variables**
230242
- `IB_GATEWAY_ZONE`
231243
- `IB_GATEWAY_IP_MODE`
@@ -351,6 +363,14 @@ IBKR 账户
351363
| `TELEGRAM_TOKEN` || Telegram 机器人 Token。Cloud Run 上更推荐走 Secret Manager 引用,不要直接写成明文 env。 |
352364
| `GLOBAL_TELEGRAM_CHAT_ID` || 这个服务使用的 Telegram Chat ID。 |
353365
| `NOTIFY_LANG` || `en`(默认)或 `zh` |
366+
| `CRISIS_ALERT_EMAIL_TO` || 危机插件升级邮件收件人,支持逗号、分号或换行分隔。不配置则跳过邮件。 |
367+
| `CRISIS_ALERT_EMAIL_FROM` || 危机插件升级邮件发件人。 |
368+
| `CRISIS_ALERT_SMTP_HOST` || 危机插件升级邮件的 SMTP host。 |
369+
| `CRISIS_ALERT_SMTP_PORT` || SMTP 端口,默认 `587`|
370+
| `CRISIS_ALERT_SMTP_USERNAME` || 可选 SMTP 用户名。 |
371+
| `CRISIS_ALERT_SMTP_PASSWORD` || 可选 SMTP 密码。Cloud Run env sync 建议配置 `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`|
372+
| `CRISIS_ALERT_SMTP_STARTTLS` || 是否使用 STARTTLS,默认 `true`|
373+
| `CRISIS_ALERT_SMTP_SSL` || 是否使用隐式 SMTP SSL,默认 `false`|
354374

355375
选中的账号组配置里,至少要有:
356376

@@ -412,6 +432,8 @@ IB_GATEWAY_IP_MODE=internal
412432
- 没有账号组配置来源 → 启动直接报错
413433
- 选中的账号组缺少关键字段(`ib_gateway_instance_name``ib_gateway_mode``ib_client_id`)→ 启动直接报错
414434

435+
如果 `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis``suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,只要 `CRISIS_ALERT_*` SMTP 配置完整,服务还会额外发一封独立危机邮件。
436+
415437
### GitHub 统一管理 Cloud Run 环境变量
416438

417439
如果代码部署继续走 Google Cloud Trigger,但你想把运行时环境变量统一放在 GitHub 管理,这个仓库现在提供了 `.github/workflows/sync-cloud-run-env.yml`
@@ -427,10 +449,12 @@ IB_GATEWAY_IP_MODE=internal
427449
- `ACCOUNT_GROUP`(建议设为 `paper`
428450
- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`
429451
- 可选:`IBKR_STRATEGY_PLUGIN_MOUNTS_JSON``IBKR_MIN_RESERVED_CASH_USD``IBKR_RESERVED_CASH_RATIO``IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD`
452+
- 可选危机插件邮件告警:`CRISIS_ALERT_EMAIL_TO``CRISIS_ALERT_EMAIL_FROM``CRISIS_ALERT_SMTP_HOST``CRISIS_ALERT_SMTP_PORT``CRISIS_ALERT_SMTP_USERNAME``CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME``CRISIS_ALERT_SMTP_STARTTLS``CRISIS_ALERT_SMTP_SSL`
430453
- `GLOBAL_TELEGRAM_CHAT_ID`
431454
- `NOTIFY_LANG`
432455
- **仓库级 Secrets**
433456
- `TELEGRAM_TOKEN`(仅在没设置 `TELEGRAM_TOKEN_SECRET_NAME` 时作为 fallback)
457+
- `CRISIS_ALERT_SMTP_PASSWORD`(仅在没设置 `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME` 时作为 fallback)
434458
- **可选过渡 Variables**
435459
- `IB_GATEWAY_ZONE`
436460
- `IB_GATEWAY_IP_MODE`

application/runtime_strategy_adapters.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
from typing import Any
77

88
import pandas as pd
9+
from quant_platform_kit.common.strategy_plugins import (
10+
build_strategy_plugin_alert_messages,
11+
build_strategy_plugin_notification_lines,
12+
should_alert_strategy_plugin_signal,
13+
translate_strategy_plugin_value,
14+
)
915

1016

1117
@dataclass(frozen=True)
@@ -46,26 +52,20 @@ def attach_strategy_plugin_report(self, report, *, signals, error: str | None =
4652
report.setdefault("diagnostics", {})["strategy_plugin_error"] = error
4753

4854
def translate_strategy_plugin_value(self, category: str, raw_value: str | None) -> str:
49-
value = str(raw_value or "").strip() or "unknown"
50-
key = f"strategy_plugin_{category}_{value}"
51-
translated = self.translator(key)
52-
return translated if translated != key else value
55+
return translate_strategy_plugin_value(category, raw_value, translator=self.translator)
5356

5457
def build_strategy_plugin_notification_lines(self, signals) -> tuple[str, ...]:
55-
lines = []
56-
for signal in signals:
57-
route = signal.canonical_route or "unknown_route"
58-
action = signal.suggested_action or "unknown_action"
59-
lines.append(
60-
self.translator(
61-
"strategy_plugin_line",
62-
plugin=self.translate_strategy_plugin_value("name", signal.plugin),
63-
mode=self.translate_strategy_plugin_value("mode", signal.effective_mode),
64-
route=self.translate_strategy_plugin_value("route", route),
65-
action=self.translate_strategy_plugin_value("action", action),
66-
)
67-
)
68-
return tuple(lines)
58+
return build_strategy_plugin_notification_lines(signals, translator=self.translator)
59+
60+
def should_alert_strategy_plugin_signal(self, signal) -> bool:
61+
return should_alert_strategy_plugin_signal(signal)
62+
63+
def build_strategy_plugin_alert_messages(self, signals):
64+
return build_strategy_plugin_alert_messages(
65+
signals,
66+
translator=self.translator,
67+
strategy_label=self.strategy_profile,
68+
)
6969

7070
def get_historical_close(self, ib, symbol, duration="2 Y", bar_size="1 day"):
7171
series = self.fetch_historical_price_series_fn(

main.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from decision_mapper import map_strategy_decision
2626
from entrypoints.cloud_run import is_market_open_today
2727
from notifications.telegram import build_strategy_display_name, build_translator, send_telegram_message
28+
from quant_platform_kit.notifications.email import send_smtp_email
2829
from quant_platform_kit.common.runtime_assembly import build_runtime_assembly
2930
from quant_platform_kit.common.runtime_reports import (
3031
append_runtime_report_error,
@@ -232,6 +233,14 @@ def _env_flag(name: str) -> bool:
232233
TG_TOKEN = RUNTIME_SETTINGS.tg_token
233234
TG_CHAT_ID = RUNTIME_SETTINGS.tg_chat_id
234235
NOTIFY_LANG = RUNTIME_SETTINGS.notify_lang
236+
CRISIS_ALERT_EMAIL_TO = getattr(RUNTIME_SETTINGS, "crisis_alert_email_to", ())
237+
CRISIS_ALERT_EMAIL_FROM = getattr(RUNTIME_SETTINGS, "crisis_alert_email_from", None)
238+
CRISIS_ALERT_SMTP_HOST = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_host", None)
239+
CRISIS_ALERT_SMTP_PORT = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_port", 587)
240+
CRISIS_ALERT_SMTP_USERNAME = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_username", None)
241+
CRISIS_ALERT_SMTP_PASSWORD = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_password", None)
242+
CRISIS_ALERT_SMTP_STARTTLS = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_starttls", True)
243+
CRISIS_ALERT_SMTP_SSL = getattr(RUNTIME_SETTINGS, "crisis_alert_smtp_ssl", False)
235244

236245
CASH_RESERVE_RATIO = STRATEGY_RUNTIME.cash_reserve_ratio
237246
CASH_RESERVE_FLOOR_USD = getattr(STRATEGY_RUNTIME, "cash_reserve_floor_usd", 0.0)
@@ -468,6 +477,35 @@ def build_strategy_plugin_notification_lines(signals) -> tuple[str, ...]:
468477
return build_strategy_adapters().build_strategy_plugin_notification_lines(signals)
469478

470479

480+
def build_strategy_plugin_alert_messages(signals):
481+
return build_strategy_adapters().build_strategy_plugin_alert_messages(signals)
482+
483+
484+
def send_crisis_alert_email(alert_message) -> bool:
485+
return send_smtp_email(
486+
subject=alert_message.subject,
487+
body=alert_message.body,
488+
smtp_host=CRISIS_ALERT_SMTP_HOST,
489+
smtp_port=CRISIS_ALERT_SMTP_PORT,
490+
sender=CRISIS_ALERT_EMAIL_FROM,
491+
recipients=CRISIS_ALERT_EMAIL_TO,
492+
username=CRISIS_ALERT_SMTP_USERNAME,
493+
password=CRISIS_ALERT_SMTP_PASSWORD,
494+
use_starttls=CRISIS_ALERT_SMTP_STARTTLS,
495+
use_ssl=CRISIS_ALERT_SMTP_SSL,
496+
)
497+
498+
499+
def publish_strategy_plugin_alerts(signals) -> int:
500+
sent_count = 0
501+
for alert_message in build_strategy_plugin_alert_messages(signals):
502+
if send_crisis_alert_email(alert_message):
503+
sent_count += 1
504+
if sent_count:
505+
print(f"strategy_plugin_alert_email_sent count={sent_count}", flush=True)
506+
return sent_count
507+
508+
471509
def build_account_notification_lines() -> tuple[str, ...]:
472510
account_ids = tuple(str(account_id).strip() for account_id in ACCOUNT_IDS if str(account_id).strip())
473511
if not account_ids:
@@ -601,6 +639,8 @@ def _handle_request(*, dry_run_only_override: bool | None = None, response_body:
601639
message="Starting strategy precheck" if dry_run_only_override else "Starting strategy execution",
602640
execution_window="precheck" if dry_run_only_override else "execution",
603641
)
642+
if dry_run_only_override is None:
643+
publish_strategy_plugin_alerts(strategy_plugin_signals)
604644
cycle_result = coerce_strategy_cycle_result(
605645
run_strategy_core(
606646
strategy_plugin_signals=strategy_plugin_signals,

notifications/telegram.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@
7777
"blend_gate_reason_bollinger_cap": "突破布林上轨",
7878
"blend_gate_reason_volatility_delever": "{symbol} {window} 日年化波动率 {volatility} 高于 {threshold},SOXL 转向 {redirect_symbol}",
7979
"strategy_plugin_line": "🧩 插件:{plugin} | 状态:{route} | 提醒:{action}",
80+
"strategy_plugin_alert_subject": "🚨 危机插件告警:{plugin} | {route}",
81+
"strategy_plugin_alert_title": "🚨 【危机插件告警】",
82+
"strategy_plugin_alert_strategy": "策略:{strategy}",
83+
"strategy_plugin_alert_as_of": "信号时间:{as_of}",
84+
"strategy_plugin_alert_would_trade": "若启用交易会操作:{value}",
85+
"strategy_plugin_alert_source": "来源:{source}",
8086
"strategy_plugin_name_crisis_response_shadow": "危机观察通知",
8187
"strategy_plugin_mode_shadow": "影子观察",
8288
"strategy_plugin_route_no_action": "未触发危机",
@@ -192,6 +198,12 @@
192198
"blend_gate_reason_bollinger_cap": "price above upper band",
193199
"blend_gate_reason_volatility_delever": "{symbol} {window}d annualized volatility {volatility} is above {threshold}; redirect SOXL to {redirect_symbol}",
194200
"strategy_plugin_line": "🧩 Plugin: {plugin} | status: {route} | notice: {action}",
201+
"strategy_plugin_alert_subject": "🚨 Crisis plugin alert: {plugin} | {route}",
202+
"strategy_plugin_alert_title": "🚨 【Crisis Plugin Alert】",
203+
"strategy_plugin_alert_strategy": "Strategy: {strategy}",
204+
"strategy_plugin_alert_as_of": "Signal as-of: {as_of}",
205+
"strategy_plugin_alert_would_trade": "Would trade if enabled: {value}",
206+
"strategy_plugin_alert_source": "Source: {source}",
195207
"strategy_plugin_name_crisis_response_shadow": "Crisis Watch Notice",
196208
"strategy_plugin_mode_shadow": "shadow",
197209
"strategy_plugin_route_no_action": "no crisis detected",

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@9c4ea7878a08fb2f518c74c99bda68d8ef8fd0bb
4-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@ce7887482eeab7f519484610ee8b20cb7bc886a0
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@1b6febbba7df81179ad7579f430c26a811c0e1a8
4+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@1636271a3e0c17fc0c5da363f67eabe114eeff48
55
pandas
66
numpy
77
requests

0 commit comments

Comments
 (0)