diff --git a/.github/workflows/wiki.yml b/.github/workflows/wiki.yml new file mode 100644 index 00000000..0d32b0b8 --- /dev/null +++ b/.github/workflows/wiki.yml @@ -0,0 +1,49 @@ +name: Wiki + +on: + push: + branches: [main] + paths: + - "wiki/**" + - ".github/workflows/wiki.yml" + pull_request: + branches: [main] + paths: + - "wiki/**" + - ".github/workflows/wiki.yml" + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Check JavaScript syntax + run: | + node --check wiki/main.js + node --check wiki/theme.js + node --check wiki/docs/content.js + node --check wiki/docs/main.js + + - name: Check locale JSON + run: python -m json.tool wiki/locales/en.json >/dev/null + + - name: Check static site files + run: | + test -f wiki/index.html + test -f wiki/home/index.html + test -f wiki/docs/index.html + test -f wiki/_redirects + test -f wiki/_headers + test -f wiki/assets/icon.png + ! grep -R "localhost\\|nanobot" wiki --exclude=README.md diff --git a/.gitignore b/.gitignore index 7341d93f..0573517b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,12 @@ agent/tests/*_result.json .ruff_cache/ frontend/dist/ frontend/tsconfig.tsbuildinfo +wiki/node_modules/ +wiki/dist/ +wiki/.astro/ +wiki/.vite/ +wiki/.wrangler/ +wiki/.cache/ .vibe-dev/ .vibe-record/ .cache/ @@ -35,6 +41,8 @@ data/backup/ # Internal docs (plans, specs) docs/ +!wiki/docs/ +!wiki/docs/** data/minutes/ data/overnight*.log data/*.log diff --git a/wiki/README.md b/wiki/README.md new file mode 100644 index 00000000..2d16e3ed --- /dev/null +++ b/wiki/README.md @@ -0,0 +1,23 @@ +# Vibe-Trading Wiki + +Static source for `https://vibetrading.wiki`. + +## Local preview + +```bash +cd wiki +python3 -m http.server 8088 +``` + +Open `http://localhost:8088/home/` for the landing page and `http://localhost:8088/docs/` for the docs app. + +Direct docs URLs such as `/docs/latest/getting-started/vibe-trading-overview` are handled by Cloudflare Pages via `_redirects`. The simple Python preview server does not apply those rewrite rules, so use `/docs/` as the local entry point. + +## Cloudflare Pages + +- Project root: `wiki` +- Build command: leave empty +- Output directory: `.` +- Custom domain: `vibetrading.wiki` + +The site is intentionally static. No server, database, or build step is required. diff --git a/wiki/_headers b/wiki/_headers new file mode 100644 index 00000000..89d13fce --- /dev/null +++ b/wiki/_headers @@ -0,0 +1,14 @@ +/* + X-Frame-Options: SAMEORIGIN + X-Content-Type-Options: nosniff + Referrer-Policy: strict-origin-when-cross-origin + Permissions-Policy: camera=(), microphone=(), geolocation=() + +/assets/* + Cache-Control: public, max-age=31536000, immutable + +/*.js + Cache-Control: public, max-age=3600 + +/*.css + Cache-Control: public, max-age=3600 diff --git a/wiki/_redirects b/wiki/_redirects new file mode 100644 index 00000000..69d61bbf --- /dev/null +++ b/wiki/_redirects @@ -0,0 +1,5 @@ +/ /home/ 302 +/docs /docs/latest/getting-started/vibe-trading-overview 302 +/docs/ /docs/latest/getting-started/vibe-trading-overview 302 +/docs/latest/* /docs/index.html 200 +/docs/0.1.7/* /docs/index.html 200 diff --git a/wiki/assets/feature-cross-market-data-backtesting.png b/wiki/assets/feature-cross-market-data-backtesting.png new file mode 100644 index 00000000..c72dcb20 Binary files /dev/null and b/wiki/assets/feature-cross-market-data-backtesting.png differ diff --git a/wiki/assets/feature-multi-agent-trading-teams.png b/wiki/assets/feature-multi-agent-trading-teams.png new file mode 100644 index 00000000..0fb578ad Binary files /dev/null and b/wiki/assets/feature-multi-agent-trading-teams.png differ diff --git a/wiki/assets/feature-self-improving-trading-agent.png b/wiki/assets/feature-self-improving-trading-agent.png new file mode 100644 index 00000000..dae0c115 Binary files /dev/null and b/wiki/assets/feature-self-improving-trading-agent.png differ diff --git a/wiki/assets/feature-shadow-account.png b/wiki/assets/feature-shadow-account.png new file mode 100644 index 00000000..077353cb Binary files /dev/null and b/wiki/assets/feature-shadow-account.png differ diff --git a/wiki/assets/icon.png b/wiki/assets/icon.png new file mode 100644 index 00000000..aad9f9a8 Binary files /dev/null and b/wiki/assets/icon.png differ diff --git a/wiki/docs/content.js b/wiki/docs/content.js new file mode 100644 index 00000000..473bf58b --- /dev/null +++ b/wiki/docs/content.js @@ -0,0 +1,396 @@ +export const DOCS_DEFAULT_VERSION = "0.1.7"; +export const DOCS_LATEST_ALIAS = "latest"; +export const DOCS_DEFAULT_PAGE = "getting-started/vibe-trading-overview"; + +export const DOCS_VERSIONS = [ + { name: "0.1.7", label: "0.1.7 (latest)" } +]; + +export const DOCS_STRUCTURE = [ + { + id: "getting-started", + label: "Getting started", + pages: [ + { + id: "getting-started/vibe-trading-overview", + title: "Vibe-Trading overview", + description: "What Vibe-Trading is, where it fits, and what boundary it keeps.", + lead: "Vibe-Trading is an open-source finance research workspace that turns natural-language prompts into market-data pulls, backtests, reports, and reusable research context.", + sections: [ + { + id: "what-it-is", + title: "What it is", + body: ` +

Vibe-Trading connects an agent loop to finance tools: data loaders, strategy generation, backtest engines, document readers, trade-journal analysis, persistent memory, and multi-agent research teams.

+

The goal is not to replace judgment. The goal is to make every research step runnable, inspectable, and easy to repeat.

+ ` + }, + { + id: "research-only", + title: "Research only", + body: ` +

Vibe-Trading does not execute live trades. It is designed for simulation, backtesting, audit trails, and research workflows. Outputs are not investment advice.

+ ` + }, + { + id: "capabilities", + title: "Core capabilities", + body: ` + + ` + } + ] + }, + { + id: "getting-started/quick-start", + title: "Quick start", + description: "Install Vibe-Trading, initialize configuration, and run the first research task.", + lead: "The fastest path is PyPI install, interactive setup, then either CLI research or the local web UI.", + sections: [ + { + id: "install", + title: "Install", + body: ` +
pip install vibe-trading-ai
+vibe-trading init
+vibe-trading
+ ` + }, + { + id: "first-run", + title: "First run", + body: ` +
vibe-trading run -p "Backtest a BTC-USDT 20/50 moving-average strategy for 2024, summarize return and drawdown, then export the report"
+ ` + }, + { + id: "web-ui", + title: "Open the web UI", + body: ` +
vibe-trading serve --port 8899
+

The local web UI is useful when you want uploaded files, streaming swarm progress, Settings, and generated artifacts in one place.

+ ` + } + ] + }, + { + id: "getting-started/configuration", + title: "Configuration", + description: "Provider, model, market-data, and deployment settings.", + lead: "Vibe-Trading keeps secrets and deployment-specific choices in environment variables or local Settings, not in source files.", + sections: [ + { + id: "env", + title: "Environment file", + body: ` +
LANGCHAIN_PROVIDER=deepseek
+LANGCHAIN_MODEL_NAME=deepseek-v4-pro
+TUSHARE_TOKEN=your-token
+TIMEOUT_SECONDS=2400
+

Run vibe-trading init to bootstrap the local configuration interactively.

+ ` + }, + { + id: "keys", + title: "Keys and data sources", + body: ` +

Many HK, US, crypto, document, journal, and static analysis workflows work without paid market-data keys. A-share fundamental enrichment and some provider-specific workflows need their matching credentials.

+

For non-local API or web deployments, configure API_AUTH_KEY and send requests with Authorization: Bearer <key>.

+ ` + }, + { + id: "models", + title: "Model choice", + body: ` +

Agent quality depends on tool use. Prefer strong tool-calling models for long research runs, swarms, and multi-step backtests. Avoid small distilled models for workflows where fabricated answers are costly.

+ ` + } + ] + } + ] + }, + { + id: "core-concepts", + label: "Core concepts", + pages: [ + { + id: "core-concepts/research-workflow", + title: "Research workflow", + description: "How a Vibe-Trading run moves from prompt to evidence.", + lead: "A good run routes the request, grounds it in data, executes tools, validates outputs, and leaves artifacts behind.", + sections: [ + { + id: "pipeline", + title: "Pipeline", + body: ` +
    +
  1. Plan: choose relevant skills, tools, data sources, and swarm presets.
  2. +
  3. Ground: fetch market bars, documents, URLs, broker journals, or local files at runtime.
  4. +
  5. Execute: run backtests, factor analysis, options checks, exports, or report generation.
  6. +
  7. Validate: attach metrics, benchmark comparison, Monte Carlo, Bootstrap, Walk-Forward, warnings, and run cards when applicable.
  8. +
  9. Deliver: return the answer plus inspectable artifacts.
  10. +
+ ` + }, + { + id: "artifacts", + title: "Artifacts", + body: ` +

Backtests and research runs can produce reports, charts, generated strategy files, run metadata, and reusable context. The artifact trail matters because finance research often needs later inspection.

+ ` + } + ] + }, + { + id: "core-concepts/backtesting", + title: "Backtesting", + description: "Market coverage, engines, metrics, and validation tools.", + lead: "Vibe-Trading backtests daily and minute strategies across multiple asset classes, then keeps outputs auditable with metrics and run cards.", + sections: [ + { + id: "engines", + title: "Engines", + body: ` + + ` + }, + { + id: "validation", + title: "Validation", + body: ` +

Research runs can include benchmark comparison, Monte Carlo, Bootstrap confidence intervals, Walk-Forward validation, and run cards. Treat these as evidence helpers, not guarantees.

+ ` + }, + { + id: "example", + title: "Example", + body: ` +
vibe-trading run -p "Backtest an equal-weight SPY and BTC-USDT momentum rotation strategy for 2024 with benchmark comparison"
+ ` + } + ] + }, + { + id: "core-concepts/swarm-teams", + title: "Swarm teams", + description: "Preset research teams for committee-style analysis.", + lead: "Swarm presets turn a research question into a small DAG of specialist workers, then stream progress and persist the final report.", + sections: [ + { + id: "presets", + title: "Presets", + body: ` +

Vibe-Trading includes 29 presets such as investment committee, quant strategy desk, crypto trading desk, macro rates and FX desk, and risk committee.

+
vibe-trading --swarm-presets
+vibe-trading --swarm-run investment_committee '{"topic":"BTC outlook"}'
+ ` + }, + { + id: "keys", + title: "Model requirements", + body: ` +

Most MCP tools work without an LLM key after install. run_swarm needs an LLM provider because it spawns internal worker agents.

+ ` + } + ] + } + ] + }, + { + id: "tools", + label: "Tools", + pages: [ + { + id: "tools/data-sources", + title: "Data sources", + description: "How Vibe-Trading routes symbols and market data providers.", + lead: "Data routing is provider-aware: mixed symbols can use source=\"auto\" while each market keeps its own data rules.", + sections: [ + { + id: "providers", + title: "Providers", + body: ` + + ` + }, + { + id: "symbols", + title: "Symbol conventions", + body: ` +

Crypto pairs use uppercase hyphen format, for example BTC-USDT. Mixed-market research should prefer automatic source routing where possible.

+ ` + } + ] + }, + { + id: "tools/shadow-account", + title: "Shadow Account", + description: "Turn a broker journal into behavior diagnostics and a counterfactual strategy path.", + lead: "Shadow Account starts with your real trading records, extracts recurring rules, and compares actual trades with a rule-based shadow strategy.", + sections: [ + { + id: "flow", + title: "Workflow", + body: ` +
    +
  1. Read a broker export from supported formats or a generic CSV.
  2. +
  3. Profile holding time, win rate, drawdown, PnL ratio, and behavior signals.
  4. +
  5. Extract recurring if-then strategy rules.
  6. +
  7. Run a shadow backtest and attribute delta-PnL.
  8. +
  9. Render an HTML/PDF audit report.
  10. +
+ ` + }, + { + id: "example", + title: "Example", + body: ` +
vibe-trading --upload trades_export.csv
+vibe-trading run -p "Analyze my trading behavior, extract my shadow strategy, and compare it with my actual trades"
+ ` + } + ] + }, + { + id: "tools/finance-skills", + title: "Finance skills", + description: "Reusable finance knowledge modules loaded into research runs.", + lead: "Skills keep domain knowledge close to the agent without hardcoding every behavior into the core loop.", + sections: [ + { + id: "library", + title: "Library", + body: ` +

Vibe-Trading bundles specialized skills across data sources, strategy generation, analysis, options, reporting, tools, and risk workflows.

+

Good prompts can ask the agent to load or create skills for repeated research patterns.

+ ` + }, + { + id: "examples", + title: "Examples", + body: ` + + ` + } + ] + } + ] + }, + { + id: "reference", + label: "Reference", + pages: [ + { + id: "reference/cli", + title: "CLI reference", + description: "Common commands for local research, web UI, memory, swarms, and files.", + lead: "The CLI is the fastest operator surface for repeatable research tasks.", + sections: [ + { + id: "commands", + title: "Common commands", + body: ` +
vibe-trading
+vibe-trading init
+vibe-trading run -p "your research prompt"
+vibe-trading --upload report.pdf
+vibe-trading memory list
+vibe-trading serve --port 8899
+vibe-trading-mcp
+ ` + }, + { + id: "interactive", + title: "Interactive mode", + body: ` +

Interactive mode supports slash commands for recent runs, swarm presets, memory, and research navigation.

+ ` + } + ] + }, + { + id: "reference/mcp-server", + title: "MCP server", + description: "Expose Vibe-Trading tools to MCP-compatible clients.", + lead: "The MCP server runs as a stdio subprocess and exposes Vibe-Trading tools to agent clients.", + sections: [ + { + id: "start", + title: "Start", + body: ` +
vibe-trading-mcp
+ ` + }, + { + id: "config", + title: "Client config", + body: ` +
{
+  "mcpServers": {
+    "vibe-trading": {
+      "command": "vibe-trading-mcp"
+    }
+  }
+}
+ ` + }, + { + id: "tools", + title: "Tool surface", + body: ` +

The server exposes tools for skills, market data, backtesting, factor analysis, options, web/document reading, trade journals, Shadow Account, and swarm runs.

+ ` + } + ] + }, + { + id: "reference/cloudflare-pages", + title: "Cloudflare Pages", + description: "Deploy this wiki without running a server.", + lead: "The wiki is static. Cloudflare Pages can serve it directly from the repository.", + sections: [ + { + id: "settings", + title: "Pages settings", + body: ` + + ` + }, + { + id: "why-static", + title: "Why static", + body: ` +

Docs, landing copy, redirects, theme state, and client-side search all work as static files. No VPS, database, or server process is required.

+ ` + } + ] + } + ] + } +]; diff --git a/wiki/docs/index.html b/wiki/docs/index.html new file mode 100644 index 00000000..d50b0a83 --- /dev/null +++ b/wiki/docs/index.html @@ -0,0 +1,84 @@ + + + + + + + Vibe-Trading Docs + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + diff --git a/wiki/docs/main.js b/wiki/docs/main.js new file mode 100644 index 00000000..d0005918 --- /dev/null +++ b/wiki/docs/main.js @@ -0,0 +1,223 @@ +import { initTheme } from "/theme.js"; +import { + DOCS_DEFAULT_PAGE, + DOCS_DEFAULT_VERSION, + DOCS_LATEST_ALIAS, + DOCS_STRUCTURE, + DOCS_VERSIONS +} from "/docs/content.js"; + +const REPO = "HKUDS/Vibe-Trading"; +const API = `https://api.github.com/repos/${REPO}`; +const STARS_CACHE_KEY = "vibetrading-github-stars"; +const STARS_TTL_MS = 12 * 60 * 60 * 1000; + +const allPages = DOCS_STRUCTURE.flatMap((group) => + group.pages.map((page) => ({ ...page, group: group.label })) +); + +function formatStarCount(n) { + if (typeof n !== "number" || !Number.isFinite(n) || n < 0) return "--"; + if (n < 1000) return String(Math.round(n)); + if (n < 1_000_000) return `${(n / 1000).toFixed(1)}k`; + return `${(n / 1_000_000).toFixed(1)}m`; +} + +function initStars() { + const el = document.getElementById("star-count"); + if (!el) return; + + let cached = null; + try { + cached = JSON.parse(localStorage.getItem(STARS_CACHE_KEY) || "null"); + } catch { + cached = null; + } + if (cached && typeof cached.count === "number") el.textContent = formatStarCount(cached.count); + if (cached && Date.now() - cached.at < STARS_TTL_MS) return; + + fetch(API, { headers: { Accept: "application/vnd.github+json" } }) + .then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status))))) + .then((data) => { + if (typeof data.stargazers_count !== "number") return; + try { + localStorage.setItem(STARS_CACHE_KEY, JSON.stringify({ count: data.stargazers_count, at: Date.now() })); + } catch { + /* ignore */ + } + el.textContent = formatStarCount(data.stargazers_count); + }) + .catch(() => { + if (!cached) el.textContent = "--"; + }); +} + +function slugify(text) { + return String(text) + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, ""); +} + +function routeParts() { + const normalized = location.pathname.replace(/\/+$/, ""); + if (!normalized || normalized === "/docs" || normalized === "/docs/index.html") { + return { version: DOCS_LATEST_ALIAS, pageId: DOCS_DEFAULT_PAGE }; + } + const match = normalized.match(/^\/docs\/([^/]+)\/(.+)$/); + if (!match) return { version: DOCS_LATEST_ALIAS, pageId: DOCS_DEFAULT_PAGE }; + return { version: match[1], pageId: match[2] || DOCS_DEFAULT_PAGE }; +} + +function canonicalPath(pageId, version = DOCS_LATEST_ALIAS) { + return `/docs/${version}/${pageId}`; +} + +function resolvePage(pageId) { + return allPages.find((page) => page.id === pageId) || allPages.find((page) => page.id === DOCS_DEFAULT_PAGE); +} + +function setMeta(page, version) { + document.title = `${page.title} - Vibe-Trading Docs`; + const description = page.description || page.lead || "Vibe-Trading documentation."; + document.querySelector('meta[name="description"]')?.setAttribute("content", description); + document.querySelector('meta[property="og:title"]')?.setAttribute("content", `${page.title} - Vibe-Trading Docs`); + document.querySelector('meta[property="og:description"]')?.setAttribute("content", description); + const canonical = `https://vibetrading.wiki${canonicalPath(page.id, version)}`; + document.querySelector('meta[property="og:url"]')?.setAttribute("content", canonical); + document.querySelector("link[rel='canonical']")?.setAttribute("href", canonical); +} + +function navLink(page, currentId) { + const active = page.id === currentId ? "is-active" : ""; + return ` + ${page.title} + ${page.description} + `; +} + +function renderNav(currentId, filter = "") { + const nav = document.getElementById("docs-nav"); + if (!nav) return; + const q = filter.trim().toLowerCase(); + const groups = DOCS_STRUCTURE.map((group) => { + const pages = group.pages.filter((page) => { + if (!q) return true; + return `${page.title} ${page.description} ${page.lead}`.toLowerCase().includes(q); + }); + if (!pages.length) return ""; + return `
+

${group.label}

+ ${pages.map((page) => navLink(page, currentId)).join("")} +
`; + }).join(""); + nav.innerHTML = groups || `

No pages match that search.

`; +} + +function renderOutline(page) { + const outline = document.getElementById("docs-outline"); + if (!outline) return; + outline.innerHTML = page.sections.map((section) => + `${section.title}` + ).join(""); +} + +function renderArticle(page, version) { + const article = document.getElementById("docs-article"); + if (!article) return; + + const index = allPages.findIndex((candidate) => candidate.id === page.id); + const previous = index > 0 ? allPages[index - 1] : null; + const next = index < allPages.length - 1 ? allPages[index + 1] : null; + + article.innerHTML = ` +
+

${page.group}

+

${page.title}

+

${page.lead}

+
+ ${page.sections.map((section) => ` +
+

${section.title}

+ ${section.body} +
+ `).join("")} + + `; +} + +function renderVersionSelect(version) { + const select = document.getElementById("version-select"); + if (!select) return; + select.innerHTML = DOCS_VERSIONS.map((item) => + `` + ).join(""); + select.addEventListener("change", () => { + const { pageId } = routeParts(); + navigate(canonicalPath(pageId, DOCS_LATEST_ALIAS)); + }); +} + +function navigate(path) { + history.pushState({}, "", path); + renderCurrent(); + window.scrollTo({ top: 0, behavior: "smooth" }); +} + +function bindDocLinks(root = document) { + root.querySelectorAll("[data-doc-link]").forEach((link) => { + link.addEventListener("click", (event) => { + event.preventDefault(); + const pageId = link.getAttribute("data-doc-link"); + if (!pageId) return; + navigate(canonicalPath(pageId)); + }); + }); +} + +function renderCurrent() { + const { version, pageId } = routeParts(); + const resolvedVersion = version === DOCS_LATEST_ALIAS ? DOCS_LATEST_ALIAS : DOCS_DEFAULT_VERSION; + const page = resolvePage(pageId); + + if (location.pathname === "/docs" || location.pathname === "/docs/" || location.pathname === "/docs/index.html") { + history.replaceState({}, "", canonicalPath(page.id, DOCS_LATEST_ALIAS)); + } + + setMeta(page, resolvedVersion); + renderNav(page.id, document.getElementById("docs-search")?.value || ""); + renderArticle(page, resolvedVersion); + renderOutline(page); + bindDocLinks(document); + document.getElementById("docs-article")?.focus({ preventScroll: true }); +} + +function initSearch() { + const search = document.getElementById("docs-search"); + if (!search) return; + search.addEventListener("input", () => { + const { pageId } = routeParts(); + renderNav(resolvePage(pageId).id, search.value); + bindDocLinks(document.getElementById("docs-nav") || document); + }); +} + +function initHeaderScroll() { + const header = document.getElementById("site-header"); + if (!header) return; + const sync = () => header.classList.toggle("is-scrolled", window.scrollY > 10); + sync(); + window.addEventListener("scroll", sync, { passive: true }); +} + +window.addEventListener("popstate", renderCurrent); + +initTheme(); +initStars(); +renderVersionSelect(DOCS_DEFAULT_VERSION); +initSearch(); +initHeaderScroll(); +renderCurrent(); diff --git a/wiki/docs/styles.css b/wiki/docs/styles.css new file mode 100644 index 00000000..0594c4ba --- /dev/null +++ b/wiki/docs/styles.css @@ -0,0 +1,286 @@ +.docs-body { + background: + linear-gradient(90deg, rgba(11, 143, 112, 0.045) 1px, transparent 1px), + linear-gradient(0deg, rgba(35, 102, 168, 0.04) 1px, transparent 1px), + var(--bg); + background-size: 36px 36px, 36px 36px, auto; +} + +.brand__text em { + color: var(--muted); + font-family: var(--mono); + font-size: 0.72rem; + font-style: normal; + font-weight: 600; + text-transform: uppercase; +} + +.docs-shell { + display: grid; + grid-template-columns: 280px minmax(0, 1fr) 210px; + gap: 32px; + width: min(1320px, calc(100% - 32px)); + margin: 0 auto; + padding: 34px 0 80px; +} + +.docs-sidebar, +.docs-outline { + position: sticky; + top: 96px; + align-self: start; + max-height: calc(100vh - 118px); + overflow: auto; +} + +.docs-sidebar { + border: 1px solid var(--line); + border-radius: 8px; + background: color-mix(in srgb, var(--surface) 90%, transparent); +} + +.docs-sidebar__tools { + display: grid; + gap: 12px; + border-bottom: 1px solid var(--line); + padding: 14px; +} + +.search-box, +.version-box { + display: grid; + gap: 6px; + color: var(--muted); + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; +} + +.search-box input, +.version-box select { + width: 100%; + min-height: 40px; + border: 1px solid var(--line); + border-radius: 6px; + background: var(--surface); + color: var(--ink); + font: 500 0.92rem var(--sans); + padding: 0 10px; +} + +#docs-nav { + padding: 8px; +} + +#docs-nav section + section { + margin-top: 8px; +} + +#docs-nav h2 { + margin: 18px 8px 8px; + color: var(--accent-2); + font-family: var(--mono); + font-size: 0.74rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +#docs-nav a { + display: grid; + gap: 4px; + border: 1px solid transparent; + border-radius: 6px; + padding: 10px; +} + +#docs-nav a:hover, +#docs-nav a.is-active { + border-color: var(--line); + background: var(--surface-2); +} + +#docs-nav span { + font-weight: 700; +} + +#docs-nav small { + color: var(--muted); + font-size: 0.78rem; + line-height: 1.35; +} + +.empty-state { + margin: 8px; + color: var(--muted); + font-size: 0.9rem; +} + +.docs-article { + min-width: 0; + outline: none; +} + +.doc-hero { + border-bottom: 1px solid var(--line); + padding: 28px 0 34px; +} + +.doc-hero h1 { + max-width: 840px; + font-size: clamp(2.6rem, 6vw, 5.6rem); +} + +.doc-hero p:last-child { + max-width: 760px; + margin: 18px 0 0; + color: var(--muted); + font-size: 1.15rem; +} + +.doc-section { + border-bottom: 1px solid var(--line); + padding: 34px 0; +} + +.doc-section h2 { + margin-bottom: 14px; + font-size: clamp(1.7rem, 3vw, 2.8rem); +} + +.doc-section p, +.doc-section li { + max-width: 780px; + color: var(--muted); +} + +.doc-section strong { + color: var(--ink); +} + +.doc-section a { + color: var(--accent-3); + font-weight: 700; +} + +.doc-section ul, +.doc-section ol { + display: grid; + gap: 10px; + margin: 16px 0 0; + padding-left: 22px; +} + +.doc-section pre { + max-width: 860px; + border: 1px solid var(--line); + border-radius: 8px; + background: #101312; + color: #edf5ef; + margin: 18px 0 0; +} + +.doc-section code { + font-family: var(--mono); + font-size: 0.92em; +} + +.doc-section p code, +.doc-section li code { + border: 1px solid var(--line); + border-radius: 4px; + background: var(--surface-2); + color: var(--ink); + padding: 0.12em 0.3em; +} + +.docs-outline { + color: var(--muted); +} + +.docs-outline p { + margin: 0 0 10px; + color: var(--accent-2); + font-family: var(--mono); + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +#docs-outline { + display: grid; + gap: 8px; +} + +#docs-outline a { + border-left: 2px solid var(--line); + padding-left: 10px; + font-size: 0.88rem; +} + +#docs-outline a:hover { + border-color: var(--accent); + color: var(--ink); +} + +.doc-footer { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; + padding-top: 32px; +} + +.doc-footer a { + display: grid; + gap: 5px; + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + padding: 16px; +} + +.doc-footer a:last-child { + text-align: right; +} + +.doc-footer span { + color: var(--muted); + font-family: var(--mono); + font-size: 0.78rem; + text-transform: uppercase; +} + +.doc-footer strong { + font-size: 1rem; +} + +@media (max-width: 1080px) { + .docs-shell { + grid-template-columns: 260px minmax(0, 1fr); + } + + .docs-outline { + display: none; + } +} + +@media (max-width: 760px) { + .docs-shell { + grid-template-columns: 1fr; + padding-top: 18px; + } + + .docs-sidebar { + position: static; + max-height: none; + } + + .doc-footer { + grid-template-columns: 1fr; + } + + .doc-footer a:last-child { + text-align: left; + } +} diff --git a/wiki/home/index.html b/wiki/home/index.html new file mode 100644 index 00000000..7a761797 --- /dev/null +++ b/wiki/home/index.html @@ -0,0 +1,195 @@ + + + + + + + Vibe-Trading Wiki - Finance Research Agent + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

Open-source finance research agent

+

Turn market questions into runnable research.

+

Vibe-Trading connects natural-language prompts to market data, backtests, swarm analysis, trade-journal diagnostics, and reproducible research artifacts.

+ +
+
+
+
+ + vibe-trading +
+
+

$ vibe-trading run -p "Backtest BTC-USDT 20/50 MA for 2024"

+

route: crypto data -> strategy -> backtest -> run card

+

return +18.6% max drawdown -7.4%

+

$ vibe-trading --swarm-run investment_committee

+

workers: macro, quant, risk, catalyst

+

report saved with evidence, metrics, and caveats

+
+
+ +
+
+ +
+
+

Install

+

One command to start a research workspace.

+
+
+
+ + + +
+
pip install vibe-trading-ai
+vibe-trading init
+vibe-trading
+ + +
+
+ +
+
+

Research loop

+

Every answer keeps the trail inspectable.

+
+
+
+ 01 +

Route

+

Select skills, data sources, tools, and swarm teams from the request.

+
+
+ 02 +

Ground

+

Fetch market data, documents, URLs, broker journals, or local files at runtime.

+
+
+ 03 +

Test

+

Run backtests, factor analysis, options checks, validation, and report generation.

+
+
+ 04 +

Deliver

+

Return run cards, exports, reports, tool traces, and caveats for the next session.

+
+
+
+ +
+
+

Capabilities

+

Built for finance research, not brokerage execution.

+
+
+
+ Self-improving trading agent +

Agent Harness

+

Persistent memory, session search, editable skills, context compression, and tool-batched research runs.

+
+
+ Cross-market data and backtesting +

Backtesting

+

Seven engines across equities, crypto, futures, forex, composites, and options portfolios.

+
+
+ Multi-agent trading teams +

Swarm Teams

+

Investment committee, quant desk, risk review, macro, crypto, and other preset research teams.

+
+
+ Shadow Account workflow +

Shadow Account

+

Parse broker journals, extract implicit rules, run counterfactual backtests, and render audit reports.

+
+
+
+ +
+
+

Boundary

+

No live trading.

+
+

Vibe-Trading is for research, simulation, and backtesting. It does not execute live trades and does not provide investment advice.

+
+
+ + + + diff --git a/wiki/index.html b/wiki/index.html new file mode 100644 index 00000000..5135bcd9 --- /dev/null +++ b/wiki/index.html @@ -0,0 +1,14 @@ + + + + + + + Vibe-Trading Wiki + + + + +

Open Vibe-Trading Wiki

+ + diff --git a/wiki/locales/en.json b/wiki/locales/en.json new file mode 100644 index 00000000..3afa6394 --- /dev/null +++ b/wiki/locales/en.json @@ -0,0 +1,10 @@ +{ + "nav.docs": "Docs", + "nav.source": "Source", + "nav.pypi": "PyPI", + "hero.eyebrow": "Open-source finance research agent", + "hero.title": "Turn market questions into runnable research.", + "hero.lead": "Vibe-Trading connects natural-language prompts to market data, backtests, swarm analysis, trade-journal diagnostics, and reproducible research artifacts.", + "hero.cta_docs": "Read the docs", + "hero.cta_source": "View source" +} diff --git a/wiki/main.js b/wiki/main.js new file mode 100644 index 00000000..b895dbec --- /dev/null +++ b/wiki/main.js @@ -0,0 +1,119 @@ +import { initTheme } from "./theme.js"; + +const REPO = "HKUDS/Vibe-Trading"; +const API = `https://api.github.com/repos/${REPO}`; +const STARS_CACHE_KEY = "vibetrading-github-stars"; +const STARS_TTL_MS = 12 * 60 * 60 * 1000; + +function formatStarCount(n) { + if (typeof n !== "number" || !Number.isFinite(n) || n < 0) return "--"; + if (n < 1000) return String(Math.round(n)); + if (n < 1_000_000) return `${(n / 1000).toFixed(1)}k`; + return `${(n / 1_000_000).toFixed(1)}m`; +} + +function readStarsCache() { + try { + const raw = localStorage.getItem(STARS_CACHE_KEY); + if (!raw) return null; + const value = JSON.parse(raw); + if (typeof value.count !== "number" || typeof value.at !== "number") return null; + return value; + } catch { + return null; + } +} + +function writeStarsCache(count) { + try { + localStorage.setItem(STARS_CACHE_KEY, JSON.stringify({ count, at: Date.now() })); + } catch { + /* private mode */ + } +} + +function initStars() { + const el = document.getElementById("star-count"); + if (!el) return; + const cached = readStarsCache(); + if (cached) el.textContent = formatStarCount(cached.count); + if (cached && Date.now() - cached.at < STARS_TTL_MS) return; + + fetch(API, { headers: { Accept: "application/vnd.github+json" } }) + .then((r) => (r.ok ? r.json() : Promise.reject(new Error(String(r.status))))) + .then((data) => { + if (typeof data.stargazers_count !== "number") return; + writeStarsCache(data.stargazers_count); + el.textContent = formatStarCount(data.stargazers_count); + }) + .catch(() => { + if (!cached) el.textContent = "--"; + }); +} + +function initInstallTabs() { + const tabs = Array.from(document.querySelectorAll("[data-tab]")); + if (!tabs.length) return; + + for (const tab of tabs) { + tab.addEventListener("click", () => { + const key = tab.getAttribute("data-tab"); + for (const item of tabs) { + const selected = item === tab; + item.setAttribute("aria-selected", String(selected)); + const panelId = item.getAttribute("aria-controls"); + if (panelId) { + const panel = document.getElementById(panelId); + if (panel) panel.hidden = !selected; + } + } + if (key) { + try { + localStorage.setItem("vibetrading-install-tab", key); + } catch { + /* ignore */ + } + } + }); + } + + try { + const saved = localStorage.getItem("vibetrading-install-tab"); + const selected = tabs.find((tab) => tab.getAttribute("data-tab") === saved); + if (selected) selected.click(); + } catch { + /* ignore */ + } +} + +async function applyLocale() { + try { + const response = await fetch("/locales/en.json"); + if (!response.ok) return; + const messages = await response.json(); + document.querySelectorAll("[data-i18n]").forEach((el) => { + const key = el.getAttribute("data-i18n"); + if (key && messages[key] != null) el.textContent = messages[key]; + }); + document.querySelectorAll("[data-i18n-html]").forEach((el) => { + const key = el.getAttribute("data-i18n-html"); + if (key && messages[key] != null) el.innerHTML = messages[key]; + }); + } catch { + /* static fallback text is already in the HTML */ + } +} + +function initHeaderScroll() { + const header = document.getElementById("site-header"); + if (!header) return; + const sync = () => header.classList.toggle("is-scrolled", window.scrollY > 10); + sync(); + window.addEventListener("scroll", sync, { passive: true }); +} + +initTheme(); +initStars(); +initInstallTabs(); +initHeaderScroll(); +applyLocale(); diff --git a/wiki/styles.css b/wiki/styles.css new file mode 100644 index 00000000..12bd3cd4 --- /dev/null +++ b/wiki/styles.css @@ -0,0 +1,542 @@ +:root { + color-scheme: light; + --bg: #f7f8f4; + --surface: #ffffff; + --surface-2: #eff3ea; + --ink: #171b1a; + --muted: #606962; + --line: #d9ded2; + --accent: #0b8f70; + --accent-2: #d47719; + --accent-3: #2366a8; + --danger: #b0413e; + --shadow: 0 22px 60px rgba(24, 32, 28, 0.12); + --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace; + --sans: "IBM Plex Sans", system-ui, sans-serif; + --serif: "Fraunces", Georgia, serif; +} + +:root[data-theme="dark"] { + color-scheme: dark; + --bg: #101312; + --surface: #171b19; + --surface-2: #202821; + --ink: #f2f4ec; + --muted: #a8b1a9; + --line: #313a33; + --accent: #35c99b; + --accent-2: #f0a04c; + --accent-3: #6faee7; + --danger: #ff8a7b; + --shadow: 0 22px 70px rgba(0, 0, 0, 0.38); +} + +* { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + background: + linear-gradient(90deg, rgba(11, 143, 112, 0.08) 1px, transparent 1px), + linear-gradient(0deg, rgba(35, 102, 168, 0.06) 1px, transparent 1px), + var(--bg); + background-size: 44px 44px, 44px 44px, auto; + color: var(--ink); + font-family: var(--sans); + font-size: 16px; + line-height: 1.55; + letter-spacing: 0; +} + +a { + color: inherit; + text-decoration: none; +} + +img { + max-width: 100%; +} + +button { + font: inherit; +} + +.site-header { + position: sticky; + top: 0; + z-index: 30; + border-bottom: 1px solid transparent; + background: color-mix(in srgb, var(--bg) 82%, transparent); + backdrop-filter: blur(18px); +} + +.site-header.is-scrolled { + border-color: var(--line); +} + +.site-header__inner, +.site-footer { + width: min(1180px, calc(100% - 32px)); + margin: 0 auto; +} + +.site-header__inner { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 72px; + gap: 24px; +} + +.brand, +.site-actions, +.site-nav { + display: flex; + align-items: center; +} + +.brand { + gap: 12px; + min-width: max-content; + font-weight: 700; +} + +.brand__mark { + width: 38px; + height: 38px; + border: 1px solid var(--line); + border-radius: 6px; + background: var(--surface); +} + +.brand__text { + font-family: var(--serif); + font-size: 1.1rem; +} + +.site-nav { + gap: 24px; + color: var(--muted); + font-size: 0.95rem; + font-weight: 600; +} + +.site-nav a:hover { + color: var(--ink); +} + +.site-actions { + gap: 10px; +} + +.star-pill, +.icon-button, +.button, +.tabs button { + border: 1px solid var(--line); + border-radius: 6px; + background: var(--surface); + color: var(--ink); +} + +.star-pill { + display: flex; + align-items: center; + gap: 8px; + min-height: 38px; + padding: 0 12px; + font-family: var(--mono); + font-size: 0.82rem; +} + +.star-pill strong { + color: var(--accent); +} + +.icon-button { + display: grid; + place-items: center; + width: 38px; + height: 38px; + cursor: pointer; +} + +.theme-icon { + width: 16px; + height: 16px; + border: 2px solid var(--ink); + border-radius: 50%; + box-shadow: inset -5px -5px 0 var(--ink); +} + +:root[data-theme="dark"] .theme-icon { + box-shadow: inset 5px 0 0 var(--ink); +} + +.hero, +.install-band, +.workflow, +.feature-band, +.safety-band { + width: min(1180px, calc(100% - 32px)); + margin: 0 auto; +} + +.hero { + display: grid; + grid-template-columns: minmax(0, 1.02fr) minmax(360px, 0.98fr); + gap: 48px; + align-items: center; + min-height: calc(100vh - 72px); + padding: 74px 0 54px; +} + +.eyebrow { + margin: 0 0 12px; + color: var(--accent); + font-family: var(--mono); + font-size: 0.78rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +h1, +h2, +h3, +p { + overflow-wrap: anywhere; +} + +h1 { + max-width: 820px; + margin: 0; + font-family: var(--serif); + font-size: clamp(3.2rem, 8vw, 7.4rem); + line-height: 0.92; + letter-spacing: 0; +} + +h2 { + margin: 0; + font-family: var(--serif); + font-size: clamp(2rem, 4vw, 4rem); + line-height: 1; + letter-spacing: 0; +} + +h3 { + margin: 0 0 10px; + font-size: 1.1rem; + letter-spacing: 0; +} + +.hero__lead { + max-width: 670px; + margin: 24px 0 0; + color: var(--muted); + font-size: clamp(1rem, 2.1vw, 1.28rem); +} + +.hero__actions { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 34px; +} + +.button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 46px; + padding: 0 18px; + font-weight: 700; +} + +.button--primary { + border-color: var(--ink); + background: var(--ink); + color: var(--bg); +} + +.button--ghost { + background: color-mix(in srgb, var(--surface) 78%, transparent); +} + +.hero__visual { + display: grid; + gap: 16px; +} + +.terminal { + overflow: hidden; + border: 1px solid var(--line); + border-radius: 8px; + background: #101312; + color: #e7efe8; + box-shadow: var(--shadow); +} + +.terminal__bar { + display: flex; + align-items: center; + gap: 8px; + min-height: 42px; + border-bottom: 1px solid #2d3830; + padding: 0 14px; +} + +.terminal__bar span { + width: 10px; + height: 10px; + border-radius: 50%; + background: #f05d5e; +} + +.terminal__bar span:nth-child(2) { + background: #f3b23d; +} + +.terminal__bar span:nth-child(3) { + background: #35c99b; +} + +.terminal__bar strong { + margin-left: 8px; + color: #aeb8af; + font-family: var(--mono); + font-size: 0.82rem; +} + +.terminal__body { + padding: 20px; + font-family: var(--mono); + font-size: clamp(0.78rem, 1.6vw, 0.95rem); +} + +.terminal__body p { + margin: 0 0 14px; +} + +.terminal__body p:last-child { + margin-bottom: 0; +} + +.prompt { + color: var(--accent-2); +} + +.ok { + color: #35c99b; +} + +.warn { + color: #f0a04c; +} + +.muted { + color: #94a097; +} + +.market-strip { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 8px; +} + +.market-strip span { + border: 1px solid var(--line); + border-radius: 6px; + background: var(--surface); + padding: 10px 8px; + color: var(--muted); + font-family: var(--mono); + font-size: 0.78rem; + text-align: center; +} + +.section-head { + display: grid; + grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr); + gap: 24px; + align-items: end; + margin-bottom: 24px; +} + +.install-band, +.workflow, +.feature-band, +.safety-band { + padding: 68px 0; +} + +.install-panel { + border: 1px solid var(--line); + border-radius: 8px; + background: var(--surface); + box-shadow: var(--shadow); +} + +.tabs { + display: flex; + gap: 8px; + border-bottom: 1px solid var(--line); + padding: 10px; +} + +.tabs button { + min-height: 36px; + padding: 0 14px; + cursor: pointer; + font-family: var(--mono); + font-size: 0.82rem; +} + +.tabs button[aria-selected="true"] { + border-color: var(--ink); + background: var(--ink); + color: var(--bg); +} + +pre { + margin: 0; + overflow-x: auto; + padding: 24px; + font-family: var(--mono); + font-size: clamp(0.86rem, 1.7vw, 1rem); +} + +.step-grid, +.feature-grid { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px; +} + +.step-grid article, +.feature-card { + border: 1px solid var(--line); + border-radius: 8px; + background: color-mix(in srgb, var(--surface) 88%, transparent); + padding: 18px; +} + +.step-grid span { + display: block; + margin-bottom: 26px; + color: var(--accent-2); + font-family: var(--mono); + font-size: 0.82rem; + font-weight: 600; +} + +.step-grid p, +.feature-card p { + margin: 0; + color: var(--muted); +} + +.feature-card { + display: flex; + flex-direction: column; +} + +.feature-card img { + width: 100%; + aspect-ratio: 1 / 0.72; + object-fit: cover; + border-radius: 6px; + margin-bottom: 16px; + background: var(--surface-2); +} + +.safety-band { + display: grid; + grid-template-columns: minmax(220px, 0.8fr) minmax(0, 1.2fr); + gap: 24px; + align-items: center; + border-top: 1px solid var(--line); + border-bottom: 1px solid var(--line); +} + +.safety-band p:last-child { + margin: 0; + color: var(--muted); + font-size: 1.08rem; +} + +.site-footer { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 18px; + min-height: 92px; + color: var(--muted); + font-size: 0.92rem; +} + +.site-footer span { + margin-right: auto; + color: var(--ink); + font-weight: 700; +} + +@media (max-width: 920px) { + .hero { + grid-template-columns: 1fr; + min-height: auto; + } + + .step-grid, + .feature-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 680px) { + .site-header__inner { + min-height: 66px; + gap: 12px; + } + + .site-nav { + display: none; + } + + .brand__text { + font-size: 1rem; + } + + .star-pill { + display: none; + } + + .hero { + padding-top: 46px; + } + + h1 { + font-size: clamp(3rem, 16vw, 4.8rem); + } + + .section-head, + .safety-band { + grid-template-columns: 1fr; + } + + .step-grid, + .feature-grid, + .market-strip { + grid-template-columns: 1fr; + } + + .tabs { + overflow-x: auto; + } +} diff --git a/wiki/theme.js b/wiki/theme.js new file mode 100644 index 00000000..75ad4246 --- /dev/null +++ b/wiki/theme.js @@ -0,0 +1,31 @@ +const STORAGE_KEY = "vibetrading-theme"; + +function preferredTheme() { + try { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored === "dark" || stored === "light") return stored; + } catch { + /* ignore */ + } + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +} + +function applyTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); +} + +export function initTheme() { + applyTheme(preferredTheme()); + const button = document.getElementById("theme-toggle"); + if (!button) return; + + button.addEventListener("click", () => { + const next = document.documentElement.getAttribute("data-theme") === "dark" ? "light" : "dark"; + applyTheme(next); + try { + localStorage.setItem(STORAGE_KEY, next); + } catch { + /* ignore */ + } + }); +}