🔐
Before you upload anything: here's how we store your portfolio data — encrypted, with no plaintext in the database.
Try free Read more
Privora 泊睿

Privora 泊睿 User Guide

Your cloud-native data workstation and quantitative monitoring powerhouse. Build automated data pipelines or run quantitative strategies with built-in A/H stock data — no tedious low-level coding required.

Getting Started

  • Registration & Survey: After your first registration and login, the system will guide you through a Welcome Survey (role, purpose, etc.). Once completed, a Product Tour will automatically play, walking you through each sidebar module to help you quickly familiarize with the layout.
  • Re-trigger Tour: If you skipped the tour, you can restart it anytime by clicking "Feature Tour" at the bottom of the left navigation bar.
  • Language Switch: Click "Language" in the sidebar to switch the system language in real-time.

AI Assistant

A conversational assistant docked in the sidebar — skip the menu hierarchy and get things done in plain language.

1. Entry Point

  • Once logged in, an "AI Assistant" entry appears near the bottom of the sidebar (next to the language switcher); click it to open the chat drawer. It is text-first today — no microphone needed. This entry only appears once an admin has enabled the feature for your environment.

2. What It Can Do

  • Ask it in plain language how to use the platform, or to look up data on a dashboard/asset; you can also ask "what can you do" and it will list the operations currently available to you.

3. Voice-Dictated Bookkeeping

  • You can dictate a real-money position trade (buy/sell) covering stocks, gold, or off-exchange funds (say the 6-digit fund code); key details like the account and amount are echoed back on a confirmation card first, and the trade is only recorded after you confirm.

4. Portfolio Summary Card

  • Ask something like "how's my portfolio doing" and it returns a summary card with total market value and floating P&L at a glance.
Sub-capabilities such as voice-dictated bookkeeping and the portfolio summary card are enabled independently by admins via feature flags — actual availability depends on what's turned on for your team.

Home Overview

The first screen after login, adapting its statistics, to-dos, and guidance to your usage stage.

1. Six Stat Cards

  • Total portfolio value, today's P&L, subscription count, token count, API calls in the last 7 days, and scheduled-job total/active counts — any card with no data shows a matching call-to-action (e.g. "Create a Token") instead of a bare zero.

2. New-User Guidance

  • A newly registered account with no positions or subscriptions yet sees 2 scenario cards pointing to common entry points, plus a 7-step checklist: create a token → subscribe to a data asset → complete your first API call → configure an alert rule → connect a webhook (Feishu/WeChat) → run a backtest → connect an agent. Progress shows up in the welcome line and progress bar.

3. Active-User Operations Panel

  • Once the checklist is fully complete, this area switches to Quick Actions plus a 3-column band (job health over the last 24 hours, today's alerts) for daily monitoring — the new-user checklist no longer shows.

Insight Studio (Dashboards)

Turn raw data into real-time dashboards and metric alerts — your boss will love it.

1. Build Dashboards & Global Variables

Insight Studio -> Dashboard Builder -> [Create Panel]
  • Data Binding: Bindtime-series charts and tables directly to registered assets from the Asset Catalog, or select "Custom SQL Query" to write raw SQL.
  • Linked Filtering: Add global dropdown variables (e.g. stock_code), reference them in chart SQL via ${stock_code}. Switch the dropdown and all charts refresh instantly.

2. Metric Alerts

Sidebar -> Insight Studio -> Alerts (standalone /alerts page, decoupled from dashboards)
  • Click "New Alert" and pick a scenario (search a built-in instrument for A-shares / HK / Funds / Gold, or choose "Custom metric" to bind any subscribed data asset + column filters) to define what's being watched.
  • Set the trigger condition (e.g. "when the latest value of Close Price is less than 1500, notify me"), then pick a configured Webhook datasource as the channel under "How to notify" — add a WEBHOOK-type datasource under Asset Studio -> Data Source Connections first if you don't have one yet.
  • Under "Advanced", set the silence period (minutes, default 60) and the daily fire cap to keep one rule from repeatedly flooding you with notifications.

Asset Studio (Data Foundation)

Standardize messy databases and APIs into platform-consumable "Data Assets".

1. Configure Data Source Connections

Asset Studio -> Data Source Connections -> [Add Data Source]
  • Category: Select system type — Database, API, Webhook (for alert notifications like Feishu/DingTalk bots), etc.
  • Authentication: Fill in credentials for databases; configure Token or Sign Secret for API/Webhook.
  • Server Type: Choose Production, Development, or Test to achieve physical multi-environment isolation.
  • Hologres (Alibaba Cloud's real-time data warehouse) was added as a data source type.

2. Register & Manage Data Assets

Asset Studio -> Asset Catalog -> [Edit / Add Data Asset]
  • Sensitivity Control: Public — visible to other teams. Internal — private to your team.
Internal assets must include a permission_field tag (e.g. permission_field:tenant_id) to be published externally. The system will auto-apply Row-Level Security (RLS).
  • Data Profile: In the detail page, click "Data Profile" to auto-generate null rates, min/max values, and distribution charts for each column.

3. New Data Asset Families

  • The marketplace catalog gained two new directly-subscribable datasets: off-exchange/on-exchange fund daily prices and NAV (fund_day, ~15 years of history) and fund dividends (fund_dividend); usage matches the existing equity-price assets — search and subscribe from the Marketplace.

Process Studio (Data Factory)

No need to write lengthy scripts — drag, drop, and SQL your way to data cleansing and transformation pipelines.

1. Process Diagram Canvas

  • Drag & Configure Nodes: Drag Database, API, Transform, Filter nodes from the left panel onto the grid. Double-click to configure. For SQL nodes, write logic and click "Format SQL" to beautify. Supports Retry Times on failure.
  • Connection Control: Drag from a node's right port to the next node. Double-click a line to set trigger conditions (On Success for normal flow, On Failure for fallback compensation).
  • Version Snapshots: The system auto-saves snapshots of every modification. Supports version diff "Compare" and one-click "Restore" rollback.
  • Agent / API Updates: Agents and scripts can update an existing pipeline via PUT /api/ingestions/{id} (scope process.pipeline.update). Omit nodes to rename only; send nodes=[...] to fully replace steps. Every PUT writes a version snapshot — any mistake can be rolled back from the Versions tab. Legacy rows with an empty team are rejected with 403 until backfilled.

2. Built-in Components

The left panel of the diagram editor provides various components. Drag them onto the canvas and double-click to configure. Each component has a "Guide" tab with detailed usage instructions.

Python Script (python_script)

Run custom Python code with the built-in lg_utils library (no installation required):

  • get_context() — View your team's available assets, datasources, dashboards, and processes
  • get_asset_data("name", filter_column=..., filter_value=[...], filter_operator="eq") — Fetch asset data. Pass a list to filter_value + filter_operator="eq" to do a single IN query across many symbols (e.g. stock_num IN (601985,600050,002085))
  • get_portfolio_positions(stock_num=None) — Read your team's current holdings; each row carries the latest Process recommendation (Action / Add1,2 / Reduce1,2)
  • get_trading_records(account_id=None, market=None, stock_num=None, trade_type=None, page=1, size=50) — Read your team's trading records, paginated. Filter optionally by accountId, market, stockNum, tradeType. Use for swing-monitor anchors (latest BUY/SELL) or transaction-record-driven backtests inside Process.
  • write_recommendations([{...}]) — Append per-stock recommendation rows (history is preserved). The holdings page exposes the history via a per-row "Rec history" button — paginated, newest first.
  • get_connection("ds_name") — Connect to team datasources (auto-resolves config; supports PostgreSQL/MySQL/Oracle/SQL Server)
  • get_variable("key") — Read scheduling context variables (job name, batch number, etc.)
  • put_variable("key", value) — Write a variable back into the pipeline context so downstream steps can reference it via ${key} (good for log summaries, counts, small JSON values; ≤ 64 KB per value)
  • log.info() / warn() / error() — Structured log output, displayed in real-time in execution logs
  • backtest(..., persist=True, persist_name="...") — Run a historical backtest and persist the result to My Backtests in one call; multiple runs with different persist_name values can be compared side-by-side on Sharpe, max drawdown, and total return.
  • result.persist(name="...") — Manually persist an existing backtest result to My Backtests — useful for recording a result outside of a backtest() call, or backfilling.

SQL Execution (sql)

Execute SQL statements on a specified datasource. Supports multiple statements separated by semicolons, variable substitution ${variable}, and row_count tracking.

Fetch Asset Data (fetchAssetData)

Pull paginated rows for a registered asset into a pipeline variable — downstream Python / SQL / Loop nodes read via ${varName.data}. Supports single- and multi-value filters (e.g. stock_num="601985,600050" → IN query). Team permissions are enforced automatically; no credentials are exposed to the step.

Fetch Team Holdings (fetchPortfolioPositions)

Load your team's current portfolio into a pipeline variable, each row enriched with the latest Process recommendation. Typical pattern: fetchPortfolioPositions → pythonScript that scores each holding → write_recommendations() to push the result back to the holdings page.

Conditional Branch (if)

Route the flow to different branches based on condition expressions. Supports comparison operators (>, <, ==), logical operators (&&, ||), and an else default branch.

Loop (for)

Loop over child steps. Three modes: counter loop (i=0;i<10;i++), SQL cursor loop (row in SELECT ...#datasource), file line loop (line open path).

Variable Assignment (var)

Set or compute variable values. Supports strings, numbers, JSON, SQL query results, and math expressions. Enable setGlobal to write variables to the global context.

HTTP Call (callService)

Call external REST APIs. Supports GET/POST/PUT/DELETE with custom headers. Response available via ${logInfo} in subsequent steps.

Send Email (sendMail)

Send email notifications via SMTP. Supports HTML/plain text, multiple recipients/CC, and variable substitution. Ideal for completion notifications and alerts.

Tip: Double-click any component, then switch to the "Guide" tab for complete parameter reference and code examples.

3. Team-Shared Python Methods

Profile Settings → Team Python Methods
  • Turn functions/classes your team reuses often into a shared module, then call it from any python_script step with from team_lib.<module_name> import .... Saving runs a security scan that blocks high-risk calls like os.system/subprocess/eval; inside the module you can use get_variable/put_variable directly, sharing the same process-context variables as python_script steps.

3.5 Process Runtime — Backtest API

Use historical market data to simulate and measure a strategy's performance, entirely inside a python_script step — no extra infrastructure required.

1. Quick Start (3 steps)

  1. In the Process List, create a new process.
  2. Drag in a python_script node and write your strategy (see the example below).
  3. Run the process; results are saved to My Backtests.

2. Minimal Single-Stock Example (stock_day)

Uses the stock_day asset (built-in A/H share daily bars). Column mapping: date_column="day_id", price_columns={open: "OPEN_PRICE", close: "CLOSE_PRICE"}, filter_column="STOCK_NUM".

from lg_utils import get_variable
from lg_utils.backtest_examples.stock_day import run_stock_day_backtest

def my_strategy(bar, ctx):
    if len(ctx.history) < 20:
        return
    ma20 = sum(b.close for b in ctx.history[-20:]) / 20
    if bar.close > ma20 and ctx.position == 0:
        ctx.buy(size="all")
    elif bar.close < ma20 and ctx.position > 0:
        ctx.sell(size="all")

result = run_stock_day_backtest(
    strategy=my_strategy,
    stock_num=get_variable("stock_num", "000001"),
    start=get_variable("start_date"),   # 'YYYYMMDD' or 'YYYY-MM-DD'
    end=get_variable("end_date"),
    initial_cash=1_000_000,
    commission_bps=3,
    slippage_bps=1,
)
print(result.summary())
result.export_to_context("run1")   # snapshot to run log
result.persist(name="run1")        # save to My Backtests

3. Full backtest() Signature (24 parameters)

All parameters with defaults — pass only what differs from the defaults:

  • strategy — Callable fn(bar, ctx), or an object with on_bar(bar, ctx). Optional hooks: on_start(ctx) / on_end(ctx).
  • asset — Asset ID (int) or asset name (str) — passed to get_asset_data.
  • start, end — Date strings (closed interval) used to slice bars. None = no clipping. Both 'YYYYMMDD' and 'YYYY-MM-DD' are auto-normalized.
  • initial_cash — Starting cash. Default: 1_000_000.0.
  • commission_bps — Commission in basis points (1 bp = 1/10 000). Default: 0.0.
  • slippage_bps — Slippage in basis points. Default: 0.0.
  • fill"next_open" (default) — fills at the open of the next bar. "this_close" — fills at the current bar's close.
  • date_column — Column name that holds the bar date. Default: "trade_date". For stock_day use "day_id".
  • price_columns — Dict mapping logical names to actual column names, e.g. {"open": "OPEN_PRICE", "close": "CLOSE_PRICE"}. Defaults: open/high/low/close/volume.
  • filter_column, filter_value — Server-side filter pushed to get_asset_data. Use for multi-symbol tables (e.g. filter_column="STOCK_NUM", filter_value="000001").
  • warmup_bars — First N bars are fed to ctx.history but the strategy callback is not called. Default: 0.
  • max_bars — Hard cap on bars loaded to avoid runaway fetches. Default: 1_000_000.
  • max_history — Max length of ctx.history. None = unlimited.
  • on_trade — Callback fn(trade_dict) fired on each completed round-trip.
  • benchmark_asset — Optional asset name/ID for benchmark comparison. Produces benchmark_return, alpha, beta in metrics.
  • benchmark_price_column — Benchmark close column. Defaults to the same as price_columns["close"].
  • benchmark_filter_column, benchmark_filter_value — Server-side filter for the benchmark asset (same semantics as filter_column / filter_value).
  • persist — If True, calls result.persist(name=persist_name) automatically at the end. Default: False.
  • persist_name — Label stored when persist=True; same as the name arg of BacktestResult.persist().
  • bars — Bypass get_asset_data and supply bars directly as a list of dicts (useful for unit tests or custom data sources).

4. Runtime Objects: Bar / Context / BacktestResult

Bar — Named-tuple passed to the strategy callback each tick.

  • bar.dt — Bar date string (same value as the raw date_column field).
  • bar.open, bar.high, bar.low, bar.close, bar.volume — Resolved numeric prices and volume. None if the column is absent in the asset.
  • bar.raw — Original raw row dict from the data source — useful for accessing non-price columns.

Context — Strategy runtime context — holds account state and order submission methods.

  • ctx.position — Current number of shares held (integer, long-only).
  • ctx.cash — Available cash.
  • ctx.equity — Total portfolio value: cash + position × current bar close.
  • ctx.nav — Net asset value relative to initial cash (equity / initial_cash).
  • ctx.history — List of all Bar objects seen so far (capped at max_history).
  • ctx.buy(size="all", limit_price=None)size: "all" (use all available cash), float ∈ (0,1] (fraction of cash), or positive int (share count). limit_price acts as a cap — order skipped if fill price exceeds it.
  • ctx.sell(size="all", limit_price=None)size: "all" (sell full position), float ∈ (0,1] (fraction of position), or positive int. limit_price acts as a floor.
  • ctx.close_all() — Convenience: sell full position if any is held.
  • ctx.order_target_pct(pct) — Adjust holding to pct × equity worth of shares. pct ∈ [0, 1].

BacktestResult — Returned by backtest(). Contains all outcome data.

  • result.metrics — Dict with total_return, cagr, sharpe, sortino, max_drawdown, win_rate, profit_factor, num_trades, exposure, and (if benchmark provided) benchmark_return, alpha, beta.
  • result.trades — List of round-trip trade dicts: entry_dt, exit_dt, qty, entry_px, exit_px, pnl, return_bps.
  • result.equity_curve — List of per-bar account snapshots (see §7 for schema).
  • result.summary() — Returns a formatted multi-line string of all key metrics — useful for print() in the run log.
  • result.export_to_context(name) — Writes a sentinel line to stdout so PythonScriptStep captures it in the job log.
  • result.persist(name=...) — Persists the result to the process_backtest_result table (append-only, team-isolated). Requires running inside PythonScriptStep.

5. Portfolio Backtest (backtest_portfolio)

Runs multiple assets against a shared cash pool. Use run_stock_day_portfolio_backtest for the built-in stock_day asset. result.metrics["per_asset"] contains per-symbol return, max_drawdown, num_trades, and contribution. The assets / stock_nums list order determines which strategy gets to fill first when multiple size='all' orders land on the same bar.

from lg_utils.backtest_examples.stock_day import run_stock_day_portfolio_backtest

def make_ma_strategy(fast, slow):
    def strategy(bar, ctx):
        if len(ctx.history) < slow:
            return
        ma_fast = sum(b.close for b in ctx.history[-fast:]) / fast
        ma_slow = sum(b.close for b in ctx.history[-slow:]) / slow
        if ma_fast > ma_slow and ctx.position == 0:
            ctx.buy(size=0.5)   # use 50% of available cash
        elif ma_fast < ma_slow and ctx.position > 0:
            ctx.sell(size="all")
    return strategy

result = run_stock_day_portfolio_backtest(
    strategies={
        "000001": make_ma_strategy(5, 20),
        "600519": make_ma_strategy(10, 30),
    },
    stock_nums=["000001", "600519"],  # settlement order for size='all'
    start="20240101", end="20241231",
    initial_cash=1_000_000,
    commission_bps=3,
)
print(result.summary())
# result.metrics["per_asset"] has per-stock contribution / max_dd
result.persist(name="portfolio-v1")

6. Transaction-Record-Driven Backtest

Use get_trading_records() to read real BUY/SELL anchors from your trading history, then replay those entry dates in the backtest engine to measure what the outcome would have been.

import datetime
from lg_utils import get_trading_records
from lg_utils.backtest_examples.stock_day import run_stock_day_backtest

# Load actual BUY anchors from trading history
# Response shape: {"success": True, "data": [...], "totalElements": N, ...}
# Field names use Jackson camelCase: tradeDate, stockNum, price, tradeType
records = get_trading_records(stock_num="000001", trade_type="BUY", size=1)
if records.get("totalElements", 0) == 0:
    print("No BUY records found.")
else:
    latest_buy = records["data"][0]
    last_buy_date = str(latest_buy["tradeDate"])[:10]  # e.g. '2024-01-15'
    buy_price = float(latest_buy["price"])
    upper = buy_price * 1.10
    lower = buy_price * 0.90

    def swing_strategy(bar, ctx):
        if bar.close is None:
            return
        if ctx.position == 0 and bar.close <= lower:
            ctx.buy(size="all")
        elif ctx.position > 0 and bar.close >= upper:
            ctx.sell(size="all")

    end_date = datetime.date.today().strftime("%Y-%m-%d")
    result = run_stock_day_backtest(
        strategy=swing_strategy,
        stock_num="000001",
        start=last_buy_date,
        end=end_date,
        initial_cash=500_000,
    )
    result.persist(name="replay-from-records")

7. equity_curve JSON Schema

Each element in result.equity_curve corresponds to one bar:

// equity_curve: list of objects, one per bar
[
  {
    "dt":       "20240101",   // bar date (string, same format as date_column)
    "equity":   1_000_000.0, // total portfolio value (cash + position mark)
    "cash":     800_000.0,   // available cash
    "position": 100,         // shares held (int; portfolio mode: count of non-zero positions)
    "close":    55.80        // bar close price (portfolio mode: weighted mark equity)
  },
  ...
]

The UI at My Backtests renders this curve as a line chart (equity over time) and uses equity to compute drawdown. The cash and position fields are shown in the detail panel.

8. The __LG_BACKTEST_RESULT__ Sentinel

This line appears in the execution log automatically when you call result.export_to_context(). Do NOT copy it back into your Python code — it is machine output, not source code.

Two ways to surface results after a run:

// Emitted by result.export_to_context("name") — appears in the run log:
__LG_BACKTEST_RESULT__:<name>:<json-payload>

// Emitted by result.persist(...) — writes to process_backtest_result table:
// Returns the new row id. Visible at /profile/backtest-results.

9. Where Results Appear After Execution

Results saved with result.persist() or backtest(..., persist=True) are visible at Profile → My Backtests. The panel shows a sortable table of all your named runs with key metrics; click any row to open the equity-curve chart and trade log.

10. CLI Parameter Injection (get_variable)

The backend passes scheduling variables to the script as command-line flags. Both --start_date and -start_date are equivalent — the runtime strips all leading dashes before exposing the value via get_variable("start_date").

# Both forms are equivalent — the runtime strips leading dashes:
#   --start_date 2024-01-01
#    -start_date 2024-01-01
# Both surface as:
from lg_utils import get_variable
start = get_variable("start_date")   # => "2024-01-01"
Recommended workflow: Keep your live recommendation Process separate from your backtest Process. Backtesting in-place (adding a backtest() call to a production Process) risks persisting incomplete results or blocking live runs. Create a dedicated backtest Process, parameterize start_date / end_date / stock_num via get_variable(), and schedule it independently.

Schedule Studio (Automation Engine)

Replace local Cron — run data pipelines, quant strategies, or automation scripts on schedule in the cloud, with alerts on failure.

1. Configure Scheduled Jobs

Schedule Studio -> Job List -> [Add Job]
  • Bind the Process or script to execute. Use the built-in "Cron Expression Builder" to quickly generate timing strategies (e.g. daily at 2 AM). Configure Dependencies to ensure downstream triggers only after upstream success.

2. Instance Monitoring & Intervention

  • Turn on "Auto Refresh: ON" in the top-right corner to use as a real-time monitoring dashboard — track Pending/Running/Success/Failed status live.
  • Manual Intervention: View Logs (pull real execution logs), Kill (force-kill stuck tasks), Redo Job (one-click re-run after fixing logic), View Lineage (3-level dependency graph).

3. Dependency Groups: OR Within a Group, AND Across Groups

  • Give several dependencies the same Group name and any one of them satisfies the group (OR); different groups still all have to be satisfied for the job to trigger (AND). Dependencies with no Group are independent, matching the previous "all AND" behavior unchanged.
This is a semantics change: "the job only triggers once every upstream succeeds" now only holds ACROSS groups. Within a single OR group, satisfying just one member is enough to trigger the job — it does not wait for the rest of that group.

4. Remote Execution: Machine Pools + Dispatch Policy

  • Selecting "Remote machine (SSH)" as the execution mode lets you pick multiple machines from your existing data source connections into a pool, with a priority per machine. Dispatch policy supports Round Robin, Failover, or Random, determining which machine actually runs each invocation.

Wealth Studio

Say goodbye to expensive third-party market data APIs. Build and host your private quantitative monitoring engine in the cloud.
This module is an industry-specific extension. Contact your admin to authorize access via Admin Studio.
Built-in High-Frequency Data: The platform includes A-share and H-share real-time quotes and minute bars, plus daily-frequency fund NAV and gold spot data. No need to purchase TuShare, JoinQuant, or other expensive third-party accounts, nor maintain a heavy local historical database.
Automated P/L Calculation: Enter baseline data (cost price, quantity) and daily BUY/SELL transactions. The system auto-calculates real-time average cost and Unrealized P/L based on latest quotes. Supports safe rollback by deleting the last erroneous transaction.
Process Recommendations on Holdings: A Python step in any Process can call write_recommendations() to push per-stock signals (Action / Add1, Add2 / Reduce1, Reduce2 / no_more_add). Every call is an append — history is preserved. On the Holdings page each row has a "Rec history" button that opens a paginated, newest-first history modal for that stock. Pair with Schedule Studio to get a daily post-close signal feed.
AI Monitoring & Push: Combined with Schedule Studio and the official LLM plugin, easily achieve "price breakout alert -> Feishu/WeChat millisecond-level push". You can even ask the Agent: "Check my portfolio P/L for today."
Account-Type Filter Positions, trading records, the P&L calendar, and paper trading all have a Real / Paper dropdown at the top, filtering to one account type, with a matching badge on each row. Note: the dropdown no longer offers an "All Accounts" option — old bookmarks carrying accountType=all now default to "Real".
NAV Curve & Return Attribution The position detail page gained two widgets: a portfolio NAV curve (cumulative return vs. a benchmark) and return attribution (Alpha/Beta breakdown, expandable into a second tier); both follow the account-type filter at the top of the page.

Paper Trading

A separate ¥1,000,000 virtual account driven by live platform quotes. Validate a strategy in a zero-risk sandbox before risking real capital.
This module is an industry-specific extension. Contact your admin to authorize access via Admin Studio.
Access & Setup: Sidebar -> Wealth Studio -> Paper Trading. Requires the same investment_studio / stock_studio access as Holdings. First visit auto-creates a default account with ¥1,000,000 initial capital (the default account can't be archived). Use "Create Account" in the top-right to add more named accounts with custom initial capital; switch, archive, or unarchive accounts anytime. Reset acts on the currently selected account only — it clears that account's positions/orders and restores its own initial capital, leaving other accounts untouched.
MARKET / LIMIT Orders: MARKET orders fill synchronously at the current stock_day price during trading hours (otherwise REJECTED with OUTSIDE_TRADING_HOURS). LIMIT orders go SUBMITTED with cash reserved (BUY); a background scheduler scans every 60s and triggers BUY when live ≤ limit, SELL when live ≥ limit. At 15:00 close the sweeper expires remaining SUBMITTED orders (DAY TIF only) and refunds the reserved cash. SUBMITTED orders can be cancelled and the cash is refunded immediately.
A-share Rules & Limitations: Lot size enforced (100 shares per lot for stocks; funds/gold skip). T+1 enforced (same-day BUY cannot SELL — paper-only; the real-money path is untouched). Fees: commission 0.025% (min ¥5) + transfer 0.001% (BUY only) + stamp duty 0.05% (SELL only). 涨跌停 / suspended trading / call-auction / cross-day GTC LIMIT are NOT yet supported — all are on the v2 roadmap.
Process-driven Auto Trading: Advanced users can write a Python step in a Process that imports lg.paper and calls submit_order / get_account / get_positions / cancel_order. At dispatch the backend mints a short-lived, scope-limited Bearer token into the execution env; on terminal it auto-revokes. The marketplace ships a starter_paper_trade_strategy template — one-click subscribe to get started.
Historical Backtest (same script, two modes): Set three env vars on the Process execution — LG_PAPER_MODE=backtest + LG_PAPER_BACKTEST_FROM=2025-01-01 + LG_PAPER_BACKTEST_TO=2026-01-01 — and the SAME lg.paper script runs against historical stock_day data without a single line changed. Produces a BacktestResult row visible under Profile → Backtest Results. Fees / T+1 / lot-size (100 shares) / ±10% price-limit are byte-for-byte identical to live (Java backend is the canonical source; a Python simulator with fixture parity tests guards against drift — any rule mismatch turns CI red). End the script with an explicit lg.paper.persist_backtest(name=...) to persist. Once the strategy validates, drop the env vars and re-attach to the scheduler to switch to live paper-trading. The marketplace ships a starter_paper_trade_strategy_backtest template — one-click subscribe.
When to Reset: Strategy iteration checkpoints, monthly review, customer/peer demos. Reset is a single atomic transaction (cancel pending + snapshot positions + restore cash). It never touches real-money state.

Marketplace & API Consumption

Break data silos. Provide dead-simple data retrieval APIs, perfectly suited for automation scripts and AI LLMs.

1. Marketplace

  • Browse all published data assets and dashboards across the platform. Click "Subscribe" to add them to your available permission pool. A newly published or edited item first goes through admin review — it shows as "Under review" and stays invisible/unsubscribable to others until approved. If an admin force-unpublishes an item, existing subscribers' access is revoked at the same time.

2. Token Management

Profile Settings -> Token Management -> [Create Token]
  • Key Scenario: Configure the generated Token directly in the official OpenClaw Agent plugin, or pass it to your own Python script, enabling fully automated data retrieval without login.
  • Security: Fine-grained permission control (Scopes). Token is shown in plaintext only once at creation. If leaked, immediately click "Revoke" to block access.
  • Scope Selection: A newly created token already comes with a set of scopes that can fetch data right away (asset list/detail/data reads, etc.) — no need to guess which boxes to check. The picker groups scopes by the real HTTP method of each endpoint, and offers a few scenario-based presets for one-click selection.

3. Preview Without Logging In

  • You can open the Marketplace and browse published data assets and dashboards without logging in (it defaults to the "Data Assets" tab). Clicking a write action like "Get API / Subscribe" is intercepted and redirected to the login page — log in and retry to complete it.

4. Credits (Shadow Mode)

  • Profile Settings gained a "Credit Usage" card, and some dataset listings in the marketplace show a rate badge (e.g. "N rows / credit"); publishers can set a rate for their own dataset listings. This is currently shadow mode — it only displays estimated usage and does not actually charge or throttle anything.

LLM & Intelligent Plugins (Agent Skills)

Let AI be your 24/7 data assistant. Pull reports, monitor stocks, and submit bugs through natural language conversation.
Four ways in, and how to pick one
  • Official ClawHub skill package — install into Claude, OpenClaw or any ClawHub-compatible client and call it in natural language; see "Official Plugin Support" below.
  • MCP Server (stdio) — for native MCP clients such as Claude Code, Cursor, Windsurf, Cline, and Codex CLI; see "MCP Server" below.
  • Bearer Token + HTTP API — call the REST endpoints directly from your own script, the most flexible option; see "Bearer Token + HTTP API" below.
  • Anonymous read-only — run read-only queries with no sign-up, rate-limited; see "Anonymous read-only" below.

/agent-guide 是面向未登录访客的完整版接入指南(含安装命令与新手常见坑),本节是登录后的操作细节。

1. 3-step quick start

  1. Create a Bearer token at /profile/tokens, selecting the scopes you need
  2. Set environment variables LG_AGENT_TOKEN=<your-token> and LG_AGENT_BASE_URL=https://privora.cn
  3. Call GET /agent/skills to list available skills, then POST /agent/skills/execute to run one
Choosing scopes: The default "Read market & asset data" preset already includes dataasset.metadata.get. If you also need to subscribe to marketplace items (to obtain a clonedAssetId), tick the "Read data & manage marketplace subscriptions" preset when creating the token (it adds marketplace.item.subscribe) — the default preset does not include it. paper.* (paper-trading execution) is reserved for internal platform use and auto-issued when you bind a strategy; those checkboxes never appear on the create page, so there is nothing to look for.

2. Bearer Token + HTTP API

Common endpointsGET /agent/skills (list available skills)   POST /agent/skills/execute (execute a skill)   GET /api/public/agent/token-introspect (connectivity smoke test)

See the full skill catalog at the skill manifest (risk tier, required fields, and gotchas for every skill).

Note:In token mode, any operation marked risk 🔴 or with confirmRequired=true returns 409 — see that operation's metadata in the skill manifest. For example, schedule.job.delete (🔴) and schedule.instance.kill (🔴) both return 409 in token mode. The approval flow is only supported in session mode.

3. Official Plugin Support:

  • Run clawhub install privora-cn-quant in OpenClaw Hub / ClawHub to install the full official plugin (data, pipelines, backtesting, paper trading); if you only need real-time alerting, install the smaller clawhub install privora-alert instead. You can also search "Privora" in the Coze/GPT Store.

4. MCP Server (stdio)

Wraps GET /agent/skills / POST /agent/skills/execute as native MCP tools, for stdio MCP clients such as Claude Code, Cursor, Windsurf, Cline, and Codex CLI.

Install (no repo access needed, downloaded from the website)
npm install -g https://privora.cn/downloads/privora-mcp-server-0.1.1.tgz
curl -fsSLO https://privora.cn/downloads/privora-mcp-server-0.1.1.tgz
curl -fsSLO https://privora.cn/downloads/privora-mcp-server-0.1.1.tgz.sha256
sha256sum -c privora-mcp-server-0.1.1.tgz.sha256
{
  "mcpServers": {
    "lg-agent": {
      "command": "lg-agent-mcp-server",
      "env": {
        "LG_AGENT_BASE_URL": "https://privora.cn",
        "LG_AGENT_TOKEN": "lgatk_your_token_here"
      }
    }
  }
}
Where each client keeps this config
  • Claude Code — project scope writes .mcp.json (shared via version control); user and local scope live in ~/.claude.json. Or just use the claude mcp add command below. (official docs)
  • Cursor~/.cursor/mcp.json globally, .cursor/mcp.json per project. (official docs)
  • Windsurf~/.codeium/windsurf/mcp_config.json. (official docs)
  • Cline~/.cline/mcp.json for the CLI; for the IDE extension use the UI: MCP Servers in the top toolbar → ConfigureConfigure MCP Servers. (official docs)
  • Codex CLI~/.codex/config.toml (or .codex/config.toml per project). It is TOML, not the JSON above — see below. (official docs)
  • The first four all take the same mcpServers JSON shape, so the block above pastes in as-is; only the lg-agent name is yours to choose.
One-line add (Claude Code / Codex CLI)
claude mcp add --env LG_AGENT_BASE_URL=https://privora.cn --env LG_AGENT_TOKEN=lgatk_your_token_here --transport stdio lg-agent -- lg-agent-mcp-server
codex mcp add lg-agent --env LG_AGENT_BASE_URL=https://privora.cn --env LG_AGENT_TOKEN=lgatk_your_token_here -- lg-agent-mcp-server
Codex CLI uses TOML, not JSON. Pasting the mcpServers JSON above into ~/.codex/config.toml will fail to parse — use the block below instead. Also, Claude Code's --env must not be immediately followed by the server name, or the name is read as another KEY=VALUE and rejected — that is why the command above puts --transport stdio in between.
[mcp_servers.lg-agent]
command = "lg-agent-mcp-server"

[mcp_servers.lg-agent.env]
LG_AGENT_BASE_URL = "https://privora.cn"
LG_AGENT_TOKEN = "lgatk_your_token_here"
LG_AGENT_BASE_URL defaults to http://localhost:3000 — a website install MUST set it explicitly to https://privora.cn, or every call fails to connect. LG_AGENT_TOKEN is optional; omit it to run in anonymous mode.

Always 11 tools: 4 protocol-level meta-tools (list_skills / describe_skill / execute_skill / whoami) plus 7 first-class tools (dataasset_list, dataasset_get, dataasset_schema_get, dataasset_metadata_get, dataasset_data_get, dataasset_data_get_realtime, marketplace_item_list). All 11 always appear regardless of token; what actually succeeds depends on your token's scopes.

5. Anonymous read-only

  • Works with no LG_AGENT_TOKEN and no login: a fixed set of 10 read-only skills (asset listing, schema, marketplace items, etc.) is always allowed, rate-limited per IP across three buckets. Good for a first try before deciding whether to register.

6. Natural Language Interaction: / Zero-Friction Feedback:

  • Natural Language Interaction: After configuring the Token above, type commands directly in the chat: "Look up the latest data for asset ID 21" or "Monitor China Nuclear Power, notify me immediately if it drops below 9.5 yuan."
  • Zero-Friction Feedback: Found a bug? No need to hunt for support. Just tell the Agent "This feature has an error, please submit feedback" — the AI will auto-capture context and push it directly to our backend management system.
Traps first-time callers hit
  • Subscribing to a marketplace item gives your own team a new, cloned asset id — not the publisher's id shown on the marketplace page. Re-resolve the id from the marketplace.item.subscribe (or dataasset.list) response every time; never carry one over from a screenshot or chat log.
  • If you call dataasset.data.get with a filter_value but omit filter_op, the default is a substring match (LIKE '%v%'), not exact — pass filter_op=eq explicitly for an exact match.
  • The {id} in URL paths must be the numeric asset ID; passing an asset name returns a 500 with an unhelpful message.
  • Do not manually add an X-Agent-Mode header — the Node proxy layer injects it automatically based on your Bearer token, and adding it yourself can get the request rejected.
  • Discover available skills via GET /agent/skills, not /api/public/agent/capabilities — the latter is a trimmed view that omits the entire paper-trading surface.
  • On Windows Git Bash, curl -d 'non-ascii text' silently mangles non-ASCII bytes — use --data-binary @file instead.

Admin Studio (Admin Only)

Admin-facing controls: who can do what, which data AI agents may touch, and site-wide usage plus user feedback.
Authorization Management: Grant specific industry solutions (e.g. Wealth Studio) to designated users.
Agent Scope Policies: Strictly control the underlying data scope and pass-through policies for all AI Agents across the platform, ensuring LLM data access security boundaries.
Tracking & Feedback: View site-wide PV/DAU heat zones; centrally handle user-submitted bugs and suggestions. Admin status updates sync back to the frontend in real-time via Webhook for closed-loop support.

1. Component Permissions

Admin Studio → Step Permissions
  • Control which component types each team can use in the diagram editor. Enter a team name and check the components that team is allowed to use.
  • Unconfigured teams have access to all components by default.
  • After selecting specific components and saving, the team's diagram editor will only show the authorized components.
  • Selecting all components and saving removes restrictions, restoring full access.
After changing permissions, team users must refresh the diagram editor to see updates. Existing nodes using removed components won't be deleted, but new ones cannot be added.

Customer Use Cases — Real Workflows You Can Run

End-to-end operational walkthroughs of real customer use cases, with the exact UI paths to click.

1. Discretionary → Quant Workflow (MACD Bucket Win-Rate)

Turn your real trading history into a quantifiable edge analysis. Upload your broker CSV, compute MACD slope at each entry, group trades into buckets, and find your highest-win-rate range. 4 steps.

  1. Upload your broker CSV
    Wealth Studio → Trading Records → click the "Import CSV" button (top-right of the page, next to "Add Trade")

    Required CSV columns (header row mandatory):

    trade_date,symbol,side,quantity,price
    2024-03-15,600519,BUY,100,1700.50
    2024-04-02,600519,SELL,100,1820.00
    

    External AI agent alternative: create a Bearer token at /profile/tokens, then POST /api/wealth/trading-records/import-csv with multipart/form-data and the token in the Authorization header.

  2. Run the MACD-slope backtest pipeline
    /diagram → New Process → add a Python Script step → paste the quickstart snippet
    from lg_utils.backtest_examples.discretionary_macd_winrate import run
    
    run(persist_result=True)
    

    Defaults: MACD(12, 26, 9), 5-bar slope window, bucket width 0.1, 60 prior closes per trade. To customize: copy the full discretionary_macd_winrate.py script into your Process and edit the module-level constants. Or compose your own with lg_utils.indicators (sma / ema / macd / rsi / slope).

  3. Add the Bucket Win-rate widget to a dashboard
    /dashboards → choose or create a dashboard → Add Widget → Template dropdown → select "Bucket Win-rate (Discretionary → Quant)"

    The template auto-generates a Custom-SQL skeleton over process_backtest_result. You only need to replace three placeholders: <run_name> (the label you passed to result.persist(name=...), default "discretionary_macd_winrate") / <indicator_col> (the JSON key your script attached to each trade, default "macd_slope") / <bucket_width> (e.g. 0.1). Save → the table renders win-rate per bucket.

    Sample output:

    bucket_low  | n_trades | n_wins | win_rate_pct | avg_pnl
    -0.3        |    5     |    2   |    40.00     | -120.50
    -0.2        |    8     |    4   |    50.00     |   30.20
    -0.1        |   12     |    8   |    66.67     |  215.40
     0.0        |   15     |   11   |    73.33     |  380.10  <-- best
     0.1        |   10     |    6   |    60.00     |  180.50
     0.2        |    6     |    3   |    50.00     |   50.30
     0.3        |    3     |    1   |    33.33     | -250.10
    
  4. (Optional) Ask an external AI agent in natural language

    Tool layer (Privora) provides the data and computation; language layer (Coze / Qwen / Claude / OpenClaw) translates. Configure your Bearer token in the agent platform, then ask: "Which MACD-slope bucket has my highest win rate?" — the agent calls query_asset_data to read process_backtest_result and answers in natural language.