Skip to content
Draft
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
31 changes: 31 additions & 0 deletions .agents/skills/source-command-goal/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
name: "source-command-goal"
description: "Migrated source command `goal`"
---

# source-command-goal

Use this skill when the user asks to run the migrated source command `goal`.

## Command Template

# QuantRadar 商业冲刺 · 一轮执行

继续 QuantRadar 商业冲刺(Desk→Revenue)。

SSOT:先完整读 `~/quantradar/GROK_GOAL_COMMERCIAL.md`(QD 轨道 + 编排 SOP + 红线);
边界与铁律从 `~/quantradar/GROK_GOAL.md` v3(路径 C、单写者、Skill 采掘环)。

本轮任务:
- 若本条消息末尾指定了 QD-ID,执行该任务;
- 否则按文档 §五推荐序取下一个 ⬜ QD-ID。

强制守规:
1. `git -C ~/quantradar pull --rebase` 后开工;TodoWrite 建轮内清单
2. explore 子代理 ≤2 个且只读;全仓唯一写者是主 agent
3. 验证闸门:`python3 -m unittest discover tests` 全绿;UI 改动出 375/1440 截图存 `docs/audit/{date}/`;计费一律先 Stripe test mode;密钥零入库零打印
4. 契约 / 计费 / auth 改动完成后必跑 verifier(bugbot)复审
5. 卡住走 L1 本机 skill → L2 仓内 → L3 X 双源比对,记 `docs/SKILL_INHERIT.md`
6. 收尾:commit `qr(QD-ID): …` → push → `PROGRESS.md` 一行 → 结束语含「可 Manus pull 发布」→ 回写 GROK_GOAL_COMMERCIAL.md 状态(覆盖 QR-ID 时同步 GROK_GOAL.md)

红线(违反即停):假社会证明 / 假稀缺 / cancel 迷宫 / 伪造 Greeks 或新鲜度 / QD1-0 裁决前把 live 吹成已可用 / 第三方 tracker。
11 changes: 9 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copy to .env (gitignored) or set in Render/Fly dashboard.
# Copy to .env (gitignored); production environment location: docs/CORRECT_OPS.md.
# python3 scripts/bootstrap_env.py

PUBLIC_BASE_URL=https://quantradar.one
Expand All @@ -20,7 +20,7 @@ SESSION_SECRET=replace-with-long-random-string
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

# Magic link email (optional — without SMTP, links go to console + data/last_magic_link.txt)
# Magic link email (optional; production requires SMTP, no public debug delivery)
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
Expand All @@ -36,7 +36,14 @@ STRIPE_PRICE_ID=
STRIPE_PRICE_ID_MONTHLY=
STRIPE_PRICE_ID_YEARLY=
STRIPE_PRICE_ID_PORTFOLIO_PRO_MONTHLY=
STRIPE_PRICE_ID_REPORT=
STRIPE_PRICE_ID_BUMP=
STRIPE_WEBHOOK_SECRET=
STRIPE_PORTAL_CONFIGURATION_ID=
# Durable billing ledger (default: data/billing.sqlite3). Preserve across releases.
# QUANTRADAR_BILLING_DB=/absolute/persistent/path/billing.sqlite3
# Test checkout audit only; requires TEST keys/prices, never the production .env:
# QR_STRIPE_TEST_ACCOUNT=acct_your_sandbox
# NEVER set on production hosts — skips webhook signature verify (unit tests only):
# QUANTRADAR_STRIPE_WEBHOOK_TEST=1

Expand Down
10 changes: 3 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
with:
python-version: "3.12"
- name: Unit tests
run: python -m unittest discover -s tests -v
run: python scripts/test_isolated.py
- name: P0 smoke (in-process)
run: python scripts/p0_smoke.py
- name: Contract sample
Expand All @@ -24,14 +24,13 @@ jobs:

ios-unit:
runs-on: macos-14
if: github.event_name == 'pull_request' || contains(github.event.head_commit.message, 'ios')
steps:
- uses: actions/checkout@v4
- name: Install xcodegen
run: brew install xcodegen
- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer
- name: Generate + AppAccess unit tests
- name: Generate and run all offline iOS unit tests
run: |
cd ios
xcodegen generate
Expand All @@ -44,10 +43,7 @@ jobs:
-project QuantRadar.xcodeproj \
-scheme QuantRadar \
-destination "platform=iOS Simulator,id=$DEST" \
-only-testing:QuantRadarTests/AppAccessTests \
-only-testing:QuantRadarTests/OrganicFunnelTests \
-only-testing:QuantRadarTests/BarsCacheTests \
-only-testing:QuantRadarTests/DepthEngineTests \
-only-testing:QuantRadarTests \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ ios/DerivedData/
ios/build/
.tools/
**/xcuserdata/
.webhook_secret_pending
free_engine/.cache/
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ COPY app ./app
COPY static ./static
COPY fixtures ./fixtures
COPY schemas ./schemas
COPY free_engine ./free_engine
COPY scripts ./scripts

# Non-root
Expand Down
2 changes: 2 additions & 0 deletions GROK_GOAL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **2026-09-06 状态更正**:下文为历史计划,旧 Manus/Render、支持者权益和已上架推断不作为当前事实。当前运行于 Nube VPS,待发布成果与剩余闸门见 [验收记录](docs/audit/2026-09-06/README.md) 和 [CORRECT_OPS](docs/CORRECT_OPS.md)。244 项测试及真实 Stripe sandbox 生命周期已通过,正式 Portal 已准备;数据许可、生产切换和 Apple 审核尚未完成,未宣称全量上线或盈利。

# GROK /goal — QuantRadar 极致产品优化 v3

> **唯一主任务源**(产品仓 SSOT):本文件 `Alexaliao001/quantradar/GROK_GOAL.md`
Expand Down
2 changes: 2 additions & 0 deletions GROK_GOAL_COMMERCIAL.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **2026-09-06 状态更正**:下文为历史计划,旧 Manus/Render、支持者权益和已上架推断不作为当前事实。当前运行于 Nube VPS,待发布成果与剩余闸门见 [验收记录](docs/audit/2026-09-06/README.md) 和 [CORRECT_OPS](docs/CORRECT_OPS.md)。244 项测试及真实 Stripe sandbox 生命周期已通过,正式 Portal 已准备;数据许可、生产切换和 Apple 审核尚未完成,未宣称全量上线或盈利。

# GROK /goal — QuantRadar 商业冲刺 v1(Desk → Revenue)

> **本文件**:`~/quantradar/GROK_GOAL_COMMERCIAL.md` — `GROK_GOAL.md` v3 的**商业子战役**执行层
Expand Down
6 changes: 6 additions & 0 deletions PROGRESS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
2026-09-06 | **STRIPE-SANDBOX** | JJ 账号真实 test mode 完成美元/英镑报告支付、签名回调、90 日交付/隔离、退款撤券、Pro→Portfolio、期末取消、时钟续费/失败/补缴;修复退款后旧优惠 Checkout 卡住,244 测试与独立复核通过;正式独立 Portal 已准备;Marketstack 询问已发送,工单 307916 | 待数据书面授权后配套切换 Nube/正式计费,Apple 仍 build 7 排队;无全量上线或盈利结论

2026-09-06 | **BUILD8-UPLOAD** | App Store Connect build 8(1.2.0)上传成功且 VALID;原 build 7 审核仍 WAITING_FOR_REVIEW;已备份审核/IAP资料并准备双项目替换和地区价格文案;补充 Marketstack $9.99 商用候选及未发送询问 | 仍待数据授权、Stripe test 重认证/真实购买验收;未全量上线

2026-09-06 | **LAUNCH-HARDENING** | 完成交易日/独立数据源、持久化单股付费交付/退款/防重复订阅、每日观察列表与真实下载;Python 238 通过,iOS 46(4 外部接口检查跳过)通过,375/1440 截图;build 8 本地归档;两条遗留 Stripe 付款链接已停用;生产热备份及异机校验完成 | 尚未全量上线:待数据授权、Stripe sandbox 生命周期、正式 Portal/配置和 Apple 审核;见 docs/audit/2026-09-06/README.md

# PROGRESS — QuantRadar

2026-08-02 | **AUDIT-FIX** | 安全/诚实审计落地:bootstrap 默认关+公网禁 demo;SESSION_SECRET 公网失败关闭;plan 仅 users store;缺/非法分数 withheld;market/sector 不再假 pass;magic 不落盘/日志脱敏;login+magic 限流;Stripe customer_id 降级回退;health storage_note;72+ 回归测 | push 后需手动 Trigger Render
Expand Down
68 changes: 59 additions & 9 deletions app/charts_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
from __future__ import annotations

import json
import copy
import os
import re
import subprocess
import sys
import threading
import time
from concurrent.futures import Future
from collections import OrderedDict
from pathlib import Path
from typing import Any

Expand All @@ -22,20 +27,24 @@
r"^[A-Za-z0-9][A-Za-z0-9._-]{0,180}\.(png|jpg|jpeg|webp|svg)$",
re.I,
)
_SCAN_LOCK = threading.Lock()
_SCAN_SLOTS = threading.BoundedSemaphore(2)
_SCAN_FLIGHTS: dict[tuple, Future] = {}
_SCAN_CACHE: OrderedDict = OrderedDict()


def charts_dir() -> Path:
env = os.environ.get("CHARTS_DIR", "").strip()
if env:
return Path(env).expanduser().resolve()
return (Path.home() / "charts").resolve()
return (REPO_ROOT / "free_engine").resolve()


def resolve_mode(explicit: str | None = None) -> str:
if explicit in {"live", "artifact"}:
return explicit
env = os.environ.get("QUANTRADAR_MODE", "artifact").strip().lower()
return env if env in {"live", "artifact"} else "artifact"
env = os.environ.get("QUANTRADAR_MODE", "live").strip().lower()
return env if env in {"live", "artifact"} else "live"


def resolve_chart_asset(basename: str) -> Path | None:
Expand Down Expand Up @@ -101,7 +110,42 @@ def load_charts_json(path: Path) -> dict[str, Any]:
return data


def run_fetch_all(ticker: str, sector: str | None = None, timeout: int = 300) -> dict[str, Any]:
def run_fetch_all(ticker: str, sector: str | None = None, timeout: int = 75) -> dict[str, Any]:
"""Share identical scans, bound engine processes, and cache one completed-session result."""
from free_engine.market_calendar import completed_session
key = (str(charts_dir()), ticker, sector, completed_session())
with _SCAN_LOCK:
cached = _SCAN_CACHE.get(key)
if cached and time.monotonic() - cached[0] < 60:
return copy.deepcopy(cached[1])
flight = _SCAN_FLIGHTS.get(key)
owner = flight is None
if owner:
if not _SCAN_SLOTS.acquire(blocking=False):
raise RuntimeError("Scan capacity is busy. Please retry shortly.")
flight = Future()
_SCAN_FLIGHTS[key] = flight
if not owner:
return copy.deepcopy(flight.result(timeout=timeout + 5))
try:
payload = _run_fetch_all(ticker, sector, timeout)
with _SCAN_LOCK:
_SCAN_CACHE[key] = (time.monotonic(), payload)
_SCAN_CACHE.move_to_end(key)
while len(_SCAN_CACHE) > 64:
_SCAN_CACHE.popitem(last=False)
flight.set_result(payload)
return copy.deepcopy(payload)
except Exception as exc:
flight.set_exception(exc)
raise
finally:
with _SCAN_LOCK:
_SCAN_FLIGHTS.pop(key, None)
_SCAN_SLOTS.release()


def _run_fetch_all(ticker: str, sector: str | None = None, timeout: int = 75) -> dict[str, Any]:
"""Subprocess charts fetch_all.py; parse stdout JSON."""
cdir = charts_dir()
script = cdir / "fetch_all.py"
Expand Down Expand Up @@ -160,6 +204,12 @@ def _map_or_fail(
sources=sources,
quality=quality,
)
if mode == "live" and payload.get("daily_bars"):
from free_engine.replay import build_replay
try:
mapped["_replay"] = build_replay(payload)
except ValueError:
mapped["warnings"].append("Historical replay is unavailable for this coverage window.")
return mapped


Expand Down Expand Up @@ -235,11 +285,11 @@ def analyze(
return fail_response(
ticker=t,
contract_version=req["contract_version"],
error="artifact_not_found",
error_detail=(
f"no charts artifact for {t}; place fixtures/charts_sample/{t}_analysis.json "
"or set CHARTS_DIR with reports/**/assets"
),
error="artifact_not_found",
error_detail=(
f"No frozen sample for {t} — frozen demos cover INTC and AAPL only. "
"Switch Mode → live for a free multi-source scan of any ticker."
),
mode="artifact",
sources=[{"name": "charts.artifact", "role": "engine", "status": "missing"}],
warnings=[f"no charts artifact for {t}"],
Expand Down
2 changes: 2 additions & 0 deletions app/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ def map_charts_payload(
"engine": "charts",
"mode": mode,
"fetch_time": fetch_time,
"market_as_of": (payload.get("data_quality") or {}).get("market_as_of"),
"market_close": payload["daily_bars"][-1][1] if payload.get("daily_bars") else None,
"generated_at": (indicator_data or {}).get("generated_at")
if isinstance(indicator_data, dict)
else None,
Expand Down
Loading
Loading