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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ Telegram notifications include structured execution and heartbeat messages, with
| `ACCOUNT_REGION` | No | Account region marker for platform-style deployment (e.g. `HK`, `SG`; defaults to `ACCOUNT_PREFIX` / `DEFAULT`) |
| `LONGBRIDGE_DRY_RUN_ONLY` | No | Set to `true` to keep the selected deployment in dry-run mode. |
| `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED` | No | Defaults to `true`; set `false` to force whole-share sizing. |
| `LONGBRIDGE_ORDER_QUANTITY_STEP` | No | Explicit order quantity step override; e.g. `1` for whole shares or `0.000001` for fractional sizing. |
| `LONGBRIDGE_ORDER_QUANTITY_STEP` | No | Explicit order quantity step override; e.g. `1` for whole shares or `0.0001` for fractional sizing. |
| `LONGBRIDGE_MIN_ORDER_NOTIONAL_USD` | No | Minimum buy notional for fractional sizing; defaults to `1.0`. |
| `LONGBRIDGE_DEBUG_POSITION_SNAPSHOT` | No | Set to `true` to log raw LongBridge position quantity and available quantity for troubleshooting. |
| `INCOME_THRESHOLD_USD` | No | Optional override for the `tqqq_growth_income` income-layer threshold. Leave unset to use the strategy package default. |
| `QQQI_INCOME_RATIO` | No | Optional override for QQQI's share of the `tqqq_growth_income` income layer, 0–1. |
| `NOTIFY_LANG` | No | Notification language: `en` (English, default) or `zh` (Chinese) |
| `GOOGLE_CLOUD_PROJECT` | No | GCP project ID (defaults to ADC project when unset) |

Quantity sizing is resolved at runtime: `LONGBRIDGE_ORDER_QUANTITY_STEP` wins when set; otherwise `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED=true` uses a `0.000001` step and `false` uses whole shares. When a target value is zero, sell sizing uses the sellable position quantity instead of re-deriving shares from current price, so liquidation targets do not leave a residual share because of quote drift.
Quantity sizing is resolved at runtime: `LONGBRIDGE_ORDER_QUANTITY_STEP` wins when set; otherwise `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED=true` uses a `0.0001` step and `false` uses whole shares. When a target value is zero, sell sizing uses the sellable position quantity instead of re-deriving shares from current price, so liquidation targets do not leave a residual share because of quote drift.

Secret Manager must contain the secret named by `LONGPORT_SECRET_NAME` (default: `longport_token_hk`), where the **latest version = active access token**. The app refreshes it when expiry is within 30 days.

Expand Down Expand Up @@ -217,15 +217,15 @@ Telegram 通知包含结构化的调仓和心跳消息,支持中英文切换
| `ACCOUNT_REGION` | 否 | 平台化部署时的账户区域标记(如 `HK`、`SG`;默认按 `ACCOUNT_PREFIX` / `DEFAULT` 推断) |
| `LONGBRIDGE_DRY_RUN_ONLY` | 否 | 设为 `true` 时,该部署保持 dry-run。 |
| `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED` | 否 | 默认 `true`;设为 `false` 时强制按整数股计算。 |
| `LONGBRIDGE_ORDER_QUANTITY_STEP` | 否 | 显式覆盖下单数量步进;如 `1` 表示整数股,`0.000001` 表示碎股数量步进。 |
| `LONGBRIDGE_ORDER_QUANTITY_STEP` | 否 | 显式覆盖下单数量步进;如 `1` 表示整数股,`0.0001` 表示碎股数量步进。 |
| `LONGBRIDGE_MIN_ORDER_NOTIONAL_USD` | 否 | 碎股买入的最小名义金额;默认 `1.0`。 |
| `LONGBRIDGE_DEBUG_POSITION_SNAPSHOT` | 否 | 设为 `true` 时输出 LongBridge 原始持仓数量和可卖数量,便于排查。 |
| `INCOME_THRESHOLD_USD` | 否 | 可选的 `tqqq_growth_income` 收入层启动阈值覆盖。不填时使用策略包默认值。 |
| `QQQI_INCOME_RATIO` | 否 | 可选的 QQQI 收入层占比覆盖,0–1。 |
| `NOTIFY_LANG` | 否 | 通知语言: `en`(英文,默认)或 `zh`(中文) |
| `GOOGLE_CLOUD_PROJECT` | 否 | GCP 项目 ID(未设置时使用 ADC 默认项目) |

下单数量在运行时解析:显式设置 `LONGBRIDGE_ORDER_QUANTITY_STEP` 时优先使用该步进;否则 `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED=true` 使用 `0.000001` 步进,`false` 使用整数股。目标市值为 0 时,卖出数量直接按可卖持仓计算,不再用当前报价反推股数,避免因报价漂移留下 1 股残仓。
下单数量在运行时解析:显式设置 `LONGBRIDGE_ORDER_QUANTITY_STEP` 时优先使用该步进;否则 `LONGBRIDGE_FRACTIONAL_SHARES_ENABLED=true` 使用 `0.0001` 步进,`false` 使用整数股。目标市值为 0 时,卖出数量直接按可卖持仓计算,不再用当前报价反推股数,避免因报价漂移留下 1 股残仓。

Secret Manager 中需存在 `LONGPORT_SECRET_NAME` 指定的密钥(默认: `longport_token_hk`),**最新版本 = 当前有效的 access token**。Token 到期前 30 天会自动刷新。

Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
flask
gunicorn
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@08ed04ae9796f54a2218ffb700f97e0e33bf312f
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@c9ec484c9a12cdffedf7d87c8906b93b21f50b1c
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@268171a74a1b522f03940af399f71c37e9a32c70
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@47685bce6187b5e7d3f93bab72b72a7fc0d119d7
pandas
requests
pytz
Expand Down
1 change: 1 addition & 0 deletions runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def load_platform_runtime_settings(
step_env="LONGBRIDGE_ORDER_QUANTITY_STEP",
fractional_env="LONGBRIDGE_FRACTIONAL_SHARES_ENABLED",
fractional_default=True,
fractional_step=0.0001,
),
min_order_notional=resolve_float_env(
os.environ,
Expand Down
8 changes: 4 additions & 4 deletions tests/test_rebalance_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,12 +573,12 @@ def test_fractional_quantity_step_allows_small_soxx_target_buy(self):
plan,
prices={"SOXX.US": 504.60, "SOXL.US": 162.93, "BOXX.US": 116.59},
estimate_max_purchase_quantity_value=10,
quantity_step=0.000001,
quantity_step=0.0001,
min_order_notional=1.0,
)

self.assertEqual(len(sent_messages), 1)
self.assertIn("限价买入] SOXX: 0.321699股", sent_messages[0])
self.assertIn("限价买入] SOXX: 0.3216股", sent_messages[0])
self.assertNotIn("不足买入 1 股", sent_messages[0])

def test_zero_target_sell_uses_sellable_quantity_not_price_derived_floor(self):
Expand Down Expand Up @@ -636,12 +636,12 @@ def test_fractional_buy_uses_budget_when_broker_estimate_is_whole_share_zero(sel
plan,
prices={"SOXX.US": 504.60},
estimate_max_purchase_quantity_value=0,
quantity_step=0.000001,
quantity_step=0.0001,
min_order_notional=1.0,
)

self.assertEqual(len(sent_messages), 1)
self.assertIn("限价买入] SOXX: 0.321699股", sent_messages[0])
self.assertIn("限价买入] SOXX: 0.3216股", sent_messages[0])
self.assertNotIn("券商估算可买数量为 0", sent_messages[0])

def test_zero_investable_cash_reports_buying_power_without_trade_note(self):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_runtime_composer.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def fake_bootstrap_builder(**kwargs):
limit_buy_premium=1.005,
order_poll_interval_sec=1,
order_poll_max_attempts=8,
quantity_step=0.000001,
quantity_step=0.0001,
min_order_notional=1.0,
dry_run_only=True,
broker_adapters=SimpleNamespace(
Expand Down Expand Up @@ -113,7 +113,7 @@ def fake_bootstrap_builder(**kwargs):
assert runtime.post_submit_order == "post-submit-order"
assert config.limit_sell_discount == 0.995
assert config.limit_buy_premium == 1.005
assert config.quantity_step == 0.000001
assert config.quantity_step == 0.0001
assert config.min_order_notional == 1.0
assert config.strategy_display_name == "SOXL/SOXX 半导体趋势收益"
assert config.dry_run_only is True
2 changes: 1 addition & 1 deletion tests/test_runtime_config_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_load_platform_runtime_settings_uses_defaults_with_explicit_strategy_pro
self.assertIsNone(settings.tg_token)
self.assertIsNone(settings.tg_chat_id)
self.assertFalse(settings.dry_run_only)
self.assertEqual(settings.quantity_step, 0.000001)
self.assertEqual(settings.quantity_step, 0.0001)
self.assertEqual(settings.min_order_notional, 1.0)
self.assertFalse(settings.debug_position_snapshot)
self.assertIsNone(settings.income_threshold_usd)
Expand Down