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
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@
__pycache__
*.py[cod]
.env
build
dist
*.egg-info
.venv-*
.ruff_cache
21 changes: 7 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,12 @@ jobs:
- name: Install dependencies
run: |
set -euo pipefail
python -m pip install --upgrade pip
# Install non-git deps via pyproject.toml (moved from requirements.txt)
grep -vE "^\s*(#|$)|git\+" requirements.txt | xargs -r python -m pip install -c https://raw.githubusercontent.com/QuantStrategyLab/QuantPlatformKit/main/constraints.txt
# Install git-based packages individually with --no-deps (editable installs below)
grep 'git+' requirements.txt | while IFS= read -r pkg; do
[ -n "$pkg" ] && python -m pip install --no-deps "$pkg"
done
python -m pip install pytest pytest-cov ruff

python -m pip install --upgrade pip uv
uv sync --frozen --extra test --no-install-project
- name: Smoke import pinned shared packages
run: |
set -euo pipefail
python - <<'PY'
uv run --no-sync python - <<'PY'
from quant_platform_kit.common.port_adapters import CallableNotificationPort, CallablePortfolioPort
from hk_equity_strategies import resolve_canonical_profile as resolve_hk_canonical_profile
from us_equity_strategies import resolve_canonical_profile
Expand All @@ -110,17 +103,17 @@ jobs:
- name: Install editable shared repositories
run: |
set -euo pipefail
python -m pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies -e external/UsEquitySnapshotPipelines
uv pip install --no-deps -e external/QuantPlatformKit -e external/UsEquityStrategies -e external/UsEquitySnapshotPipelines

- name: Run ruff
run: |
set -euo pipefail
ruff check --exclude external .
uv run --no-sync ruff check --exclude external .

- name: Check QPK pin consistency
run: python scripts/check_qpk_pin_consistency.py
run: uv run --no-sync python scripts/check_qpk_pin_consistency.py

- name: Run unit tests
run: |
set -euo pipefail
PYTHONPATH=. PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python -m pytest -q tests --ignore=tests/test_request_handling.py --ignore=tests/test_event_loop.py --ignore=tests/test_monitor_dispatcher.py --ignore=tests/test_notifications.py --ignore=tests/test_connect_timeout_alert.py || true
PYTHONPATH=. PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 uv run --no-sync python -m pytest -q tests --ignore=tests/test_request_handling.py --ignore=tests/test_event_loop.py --ignore=tests/test_monitor_dispatcher.py --ignore=tests/test_notifications.py --ignore=tests/test_connect_timeout_alert.py || true
7 changes: 3 additions & 4 deletions .github/workflows/sync-cloud-run-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,14 @@ jobs:
if: steps.config.outputs.env_sync_enabled == 'true'
run: |
set -euo pipefail
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

python -m pip install --upgrade pip uv
uv sync --frozen --no-dev --no-install-project
- name: Resolve Cloud Run sync targets
id: strategy_requirements
if: steps.config.outputs.env_sync_enabled == 'true'
run: |
set -euo pipefail
sync_plan_json="$(python scripts/build_cloud_run_env_sync_plan.py --json)"
sync_plan_json="$(uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json)"
{
echo "sync_plan_json<<__SYNC_PLAN_JSON__"
printf '%s\n' "${sync_plan_json}"
Expand Down
15 changes: 8 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ FROM python:3.12-slim-bookworm

ENV PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
UV_COMPILE_BYTECODE=1 \
UV_NO_CACHE=1 \
PATH="/app/.venv/bin:${PATH}" \
PORT=8080

WORKDIR /app
Expand All @@ -10,18 +13,16 @@ RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*

COPY requirements.txt ./
COPY constraints.txt ./
RUN python -m pip install --upgrade pip \
&& python -m pip install -r requirements.txt -c constraints.txt \
COPY pyproject.toml uv.lock ./
RUN python -m pip install --upgrade pip uv \
&& uv sync --frozen --no-dev --no-install-project \
&& apt-get purge -y git \
&& apt-get autoremove -y --purge \
&& rm -rf /var/lib/apt/lists/*

COPY . .
RUN useradd --create-home --uid 1000 appuser

RUN useradd --create-home --uid 1000 appuser \
&& chown -R appuser:appuser /app
COPY --chown=appuser:appuser . .
USER appuser

CMD ["gunicorn", "--bind", ":8080", "--workers", "1", "--threads", "1", "--timeout", "300", "main:app"]
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ Direct runtime profiles can usually run from market history or portfolio state.
## Quick start

```bash
python -m pip install -r requirements.txt
python -m pytest -q
uv sync --frozen --extra test --no-install-project
uv run --no-sync ruff check --exclude external .
uv run --no-sync python scripts/check_qpk_pin_consistency.py
```

## Useful docs
Expand Down
5 changes: 3 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ InteractiveBrokersPlatform 是 QuantStrategyLab 的Interactive Brokers 多市场
## 快速开始

```bash
python -m pip install -r requirements.txt
python -m pytest -q
uv sync --frozen --extra test --no-install-project
uv run --no-sync ruff check --exclude external .
uv run --no-sync python scripts/check_qpk_pin_consistency.py
```

## 延伸文档
Expand Down
25 changes: 25 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ name = "interactive-brokers-platform"
version = "0.1.0"
description = "QuantStrategyLab platform layer for Interactive Brokers."
requires-python = ">=3.11"
dependencies = [
"flask",
"gunicorn",
"pandas",
"numpy",
"requests",
"pytz",
"pandas-market-calendars",
"ib-insync",
"google-auth",
"google-cloud-compute",
"google-cloud-secret-manager",
"google-cloud-storage",
"yfinance",
"quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@0063af3b4a974650ea58a7d3f26dd1b94f65d3e8",
"us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@46887bc3f5454d5b59623b1f5efb7c65912c6b8b",
"hk-equity-strategies @ git+https://github.com/QuantStrategyLab/HkEquityStrategies.git@61993bf261aeccf64b5a75428b9405f4e1d1d682",
]

[project.optional-dependencies]
test = [
"pytest",
"pytest-cov",
"ruff",
]

[tool.coverage.run]
branch = true
Expand Down
2 changes: 1 addition & 1 deletion tests/test_dependency_pin_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,5 @@ def test_dependency_pin_guard_is_blocking_in_ci() -> None:
next_step = workflow.find("\n - name:", step_start + 1)
step = workflow[step_start : next_step if next_step != -1 else len(workflow)]

assert "python scripts/check_qpk_pin_consistency.py" in step
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in step
assert "continue-on-error" not in step
3 changes: 2 additions & 1 deletion tests/test_sync_cloud_run_env_workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ grep -Fq 'workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}'
grep -Fq 'service_account: ${{ env.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}' "$workflow_file"
grep -Fq 'uses: actions/checkout@v6' "$workflow_file"
grep -Fq 'uses: actions/setup-python@v6' "$workflow_file"
grep -Fq 'python -m pip install -r requirements.txt' "$workflow_file"
grep -Fq 'uv sync --frozen --no-dev --no-install-project' "$workflow_file"

grep -Fq 'ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION: ${{ vars.ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION }}' "$workflow_file"
grep -Fq 'target:' "$workflow_file"
Expand Down Expand Up @@ -69,6 +69,7 @@ grep -Fq 'STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN: ${{ secrets.STRATEGY_PLUGIN_

grep -Fq 'Skipping Cloud Run automation because ENABLE_GITHUB_CLOUD_RUN_DEPLOY and ENABLE_GITHUB_ENV_SYNC are not true.' "$workflow_file"
grep -Fq 'Skipping Cloud Run automation on push because ENABLE_MAIN_PUSH_CLOUD_RUN_AUTOMATION is not true.' "$workflow_file"
grep -Fq 'uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json' "$workflow_file"
grep -Fq 'scripts/build_cloud_run_env_sync_plan.py --json' "$workflow_file"
grep -Fq 'sync_plan_json<<__SYNC_PLAN_JSON__' "$workflow_file"
grep -Fq 'SYNC_PLAN_JSON: ${{ steps.strategy_requirements.outputs.sync_plan_json }}' "$workflow_file"
Expand Down
29 changes: 29 additions & 0 deletions tests/test_uv_dependency_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pathlib import Path


def test_pyproject_declares_runtime_and_test_dependencies() -> None:
pyproject = Path("pyproject.toml").read_text(encoding="utf-8")

assert "dependencies = [" in pyproject
assert "quant-platform-kit @ git+https://github.com/QuantStrategyLab/" in pyproject
assert "us-equity-strategies @ git+https://github.com/QuantStrategyLab/" in pyproject
assert "hk-equity-strategies @ git+https://github.com/QuantStrategyLab/" in pyproject
assert "[project.optional-dependencies]" in pyproject
assert "test = [" in pyproject


def test_ci_docker_and_env_sync_use_uv_lock() -> None:
ci = Path(".github/workflows/ci.yml").read_text(encoding="utf-8")
dockerfile = Path("Dockerfile").read_text(encoding="utf-8")
env_sync = Path(".github/workflows/sync-cloud-run-env.yml").read_text(encoding="utf-8")
lockfile = Path("uv.lock").read_text(encoding="utf-8")

assert lockfile.startswith("version = ")
assert "uv sync --frozen --extra test --no-install-project" in ci
assert "uv run --no-sync ruff check --exclude external ." in ci
assert "uv run --no-sync python scripts/check_qpk_pin_consistency.py" in ci
assert "uv sync --frozen --no-dev --no-install-project" in env_sync
assert "uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json" in env_sync
assert "COPY pyproject.toml uv.lock ./" in dockerfile
assert "uv sync --frozen --no-dev --no-install-project" in dockerfile
assert "python -m pip install -r requirements.txt" not in dockerfile
Loading
Loading