拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从零构建免费全球市场模拟交易平台:Paper Trading 与 AI 教练实战

在金融投资领域模拟交易Paper Trading一直是从理论学习走向实盘操作的“中间站”。过去我们想验证一个交易策略要么手动在 Excel 里记录要么使用券商提供的模拟账户但大多数模拟账户功能单一只解决了“下单”问题却没有解决“为什么这么下单”与“这笔交易到底错在哪”的复盘问题。最近看到不少团队在尝试把 AI 能力接入交易训练场景其中一类产品形态非常值得关注免费的全球市场模拟交易平台 AI 教练。它解决的痛点很明确——新手需要一个没有资金风险的环境来练习交易同时更需要一个能随时点评、解释、提醒风险的“教练”而不是冷冰冰的成交回报。本文将从一个实战构建者的视角拆解如何设计和实现一个类似的 Paper Trading 平台。我们不会只停留在概念介绍而是会完整覆盖系统架构、核心模块、模拟行情引擎、订单撮合逻辑、AI 教练模块的接法以及常见问题和工程落地的注意事项。无论你是后端开发者、量化初学者还是对 AI Agent 应用感兴趣的工程师都能从中找到可以直接复用的思路。1. Paper Trading 与 AI 教练先搞清概念1.1 什么是 Paper TradingPaper Trading 直译是“纸上交易”指在不投入真实资金的前提下按照真实市场规则进行模拟下单、持仓、平仓和盈亏计算。传统做法是在纸上记录价格和买卖决策现在则通过软件模拟实时接入行情数据让用户看到自己的虚拟账户净值变化。与实盘交易相比模拟交易有几个明显特征无真实资金风险账户里是虚拟货币亏损不会影响现实资产。真实行情驱动价格来自真实市场或仿真数据训练效果更贴近实际。允许高频试错可以放心测试激进策略、新开发的指标甚至反向验证失败案例。便于系统化复盘所有订单、持仓、盈亏、操作时间都被记录能生成完整的交易日志。模拟交易最常见的用途包括新手熟悉交易软件、验证新策略、测试算法交易代码、机构进行内部培训。1.2 AI 教练在模拟交易平台中的定位AI 教练不是一个“自动交易机器人”它的目标不是取代用户做决策而是扮演一个辅助角色。一个合格的 AI 教练至少需要具备三类能力实时提醒当市场出现异常波动、持仓风险暴露过大、用户频繁交易时给出提示。决策解释解释某一笔交易为何可能盈利或亏损拆解背后的市场逻辑。交易复盘交易日结束后根据用户的历史操作生成结构化复盘报告指出行为模式问题。实现方式上AI 教练可以走两条路线规则引擎 提示词模板当用户触发特定条件时调用预设的分析逻辑生成结构化建议。优点是可控、可解释、成本低。大语言模型LLMAgent 路线将市场数据、持仓信息、交易历史作为上下文交给 LLM 进行推理和自然语言生成。优点是灵活、交互自然但需要做好提示词工程和数据裁剪。实际项目中两者通常是结合使用的先用规则引擎做信号筛选和风险计算再用 LLM 生成人性化的表达。这样既保证了核心风控逻辑的确定性又保留了大模型的自然交互体验。1.3 为什么“免费 全球市场”是切入重点国内股票市场有涨跌停限制、T1 制度、监管要求等而全球市场涵盖了美股、港股、外汇、加密货币、期货等多种品种规则差异巨大。免费的模拟交易平台如果能覆盖多个市场就可以让用户在不用开户、不用入金的前提下提前体验不同市场的交易特征。平台免费开放的意义也不仅是降低门槛更重要的是积累真实用户的行为数据。用户在模拟盘中的操作日志本身就是非常好的模型训练语料——模型可以从大量模拟交易行为中学习到“新手容易犯什么错误”“哪些行为模式会稳定亏损”。2. 系统整体架构与模块拆解在设计这类平台时我的建议是“先定边界再写代码”。一个可运行的 Paper Trading 平台至少要包含以下模块模块职责关键点行情接入模块获取真实市场数据或生成模拟数据数据延迟、数据频率、数据源可靠性资产账户模块记录虚拟资金、持仓、冻结、可用余额资金计算精度、并发安全订单引擎模块接收买卖指令撮合成交市价单/限价单处理、滑点模拟风险控制模块强平线、最大回撤、集中度检查资金安全边界的模拟AI 教练模块实时提示、交易解释、复盘报告规则引擎 LLM 调用数据存储模块存储用户、订单、行情快照、对话记录时序数据与关系数据的选型前端控制台展示行情、下单面板、持仓、教练对话WebSocket 实时推送整体数据流向可以理解为行情源 → 行情总线 → 订单引擎 → 账户变动 → 风控检查 → AI 教练触发 → 前端推送。下面我们用 Python FastAPI 实现一个最小可运行的版本重点演示行情生成、订单撮合和 AI 教练这三个核心模块。3. 环境准备与技术选型3.1 运行环境本文示例代码基于以下环境版本可根据你的实际情况调整操作系统Windows / macOS / Linux 均可Python3.10Web 框架FastAPI异步 WebSocketFastAPI 原生支持数据存储先用内存 JSON 文件持久化生产环境可替换为 PostgreSQL RedisAI 服务OpenAI 兼容接口或本地部署的 LLM建议创建独立虚拟环境python -m venv venv source venv/bin/activate # Windows 使用 venv\Scripts\activate安装依赖pip install fastapi uvicorn pandas numpy websockets如果后续要接入 LLM再安装 OpenAI SDK 或对应的客户端库。3.2 项目结构我们采用模块化结构方便后续扩展paper_trading_ai/ ├── main.py # FastAPI 入口路由与 WebSocket ├── config.py # 全局配置 ├── models.py # 数据模型定义 ├── market_data.py # 模拟行情生成器 ├── order_engine.py # 订单撮合引擎 ├── account.py # 账户与持仓管理 ├── ai_coach.py # AI 教练模块 ├── storage.py # 简单持久化 └── static/ └── index.html # 前端控制台页面4. 核心代码逐步实现4.1 数据模型定义首先定义账户、订单、持仓、K线等基础数据模型。这里使用 Pydantic 做数据校验同时利用其.model_dump()方法方便序列化。# 文件路径models.py from enum import Enum from typing import List, Optional from datetime import datetime from pydantic import BaseModel, Field class OrderSide(str, Enum): BUY buy SELL sell class OrderType(str, Enum): MARKET market LIMIT limit class OrderStatus(str, Enum): PENDING pending FILLED filled CANCELLED cancelled REJECTED rejected class Order(BaseModel): id: str symbol: str side: OrderSide order_type: OrderType quantity: float price: Optional[float] None # 限价单需要 status: OrderStatus OrderStatus.PENDING filled_price: Optional[float] None created_at: datetime Field(default_factorydatetime.utcnow) filled_at: Optional[datetime] None class Position(BaseModel): symbol: str quantity: float 0 avg_price: float 0 realized_pnl: float 0 class Account(BaseModel): user_id: str cash: float 100000.0 # 初始虚拟资金 positions: dict[str, Position] {} orders: List[Order] [] def available_cash(self) - float: return self.cash这里的核心要点是每个账户包含现金、持仓字典和历史订单列表。为了简化我们暂不考虑保证金、融资融券等复杂场景但保留扩展空间。4.2 模拟行情生成器真实平台一般会接入券商或第三方数据源但本文示例为了让你能本地跑通实现一个基于随机游走Random Walk的模拟行情生成器。它会维护一个内存中的价格表并提供一个get_price(symbol)方法返回当前价格。你可以很容易替换成真实数据源的 WebSocket 客户端。# 文件路径market_data.py import random import time from datetime import datetime from typing import Dict class MarketDataSimulator: 简单的模拟行情生成器。 每个 symbol 维护一个当前价格价格根据几何布朗运动近似更新。 def __init__(self, symbols: Dict[str, float], volatility: float 0.002): self.prices: Dict[str, float] dict(symbols) self.volatility volatility def update_prices(self): 模拟一次行情更新 for symbol in self.prices: change random.gauss(0, self.volatility) self.prices[symbol] max(0.0001, self.prices[symbol] * (1 change)) def tick(self): 行情 tick放到异步循环中周期调用 self.update_prices() return self.get_snapshot() def get_price(self, symbol: str) - float: return self.prices.get(symbol, 0.0) def get_snapshot(self) - Dict[str, any]: return { timestamp: datetime.utcnow().isoformat(), prices: self.prices.copy(), }初始化时你可以设置几个常见的美股和加密货币代码simulator MarketDataSimulator( symbols{ AAPL: 193.50, TSLA: 248.20, BTCUSD: 67000.0, ETHUSD: 3500.0, EURUSD: 1.0850, }, volatility0.001, )随机游走模型虽然简单但对于模拟交易训练已经够用因为它能体现价格的不确定性。真实项目中你还可以加入跳空gap、波动率聚集volatility clustering等更贴近真实市场的行为。4.3 订单撮合引擎订单引擎是整个平台最核心的模块。它接收用户订单检查账户资金或持仓是否充足然后决定是否成交。市价单Market Order按当前最新价格立即成交限价单Limit Order只有在最新价格达到指定价格时才成交。为了模拟真实市场滑点我们可以在市价单成交时加一点随机滑点。# 文件路径order_engine.py import uuid from datetime import datetime from typing import Optional from models import Account, Order, OrderSide, OrderStatus, OrderType class OrderEngine: def __init__(self, market_data): self.market_data market_data def create_order( self, account: Account, symbol: str, side: OrderSide, order_type: OrderType, quantity: float, price: Optional[float] None, ) - Order: 创建订单并进行初步校验。 返回订单对象最终是否成交取决于撮合逻辑。 if quantity 0: raise ValueError(quantity must be positive) if order_type OrderType.LIMIT and price is None: raise ValueError(limit order requires price) order Order( iduuid.uuid4().hex[:12], symbolsymbol, sideside, order_typeorder_type, quantityquantity, priceprice, ) if order_type OrderType.MARKET: self._fill_market_order(account, order) else: # 限价单先进入挂单状态这里简化处理校验后立即尝试撮合 self._try_fill_limit_order(account, order) account.orders.append(order) return order def _fill_market_order(self, account: Account, order: Order): current_price self.market_data.get_price(order.symbol) if current_price 0: order.status OrderStatus.REJECTED return # 模拟滑点成交价格在现价上下 0.05% 范围内浮动 import random slippage random.uniform(-0.0005, 0.0005) filled_price current_price * (1 slippage) self._execute(account, order, filled_price) def _try_fill_limit_order(self, account: Account, order: Order): current_price self.market_data.get_price(order.symbol) buy_ok order.side OrderSide.BUY and current_price order.price sell_ok order.side OrderSide.SELL and current_price order.price if buy_ok or sell_ok: self._execute(account, order, order.price) else: # 实际项目里这里应该进入限价单队列等待行情触发 order.status OrderStatus.PENDING def _execute(self, account: Account, order: Order, fill_price: float): order.filled_price fill_price order.status OrderStatus.FILLED order.filled_at datetime.utcnow() if order.side OrderSide.BUY: cost fill_price * order.quantity if cost account.cash: order.status OrderStatus.REJECTED return account.cash - cost # 更新持仓 position account.positions.get(order.symbol) if position is None: from models import Position position Position(symbolorder.symbol) account.positions[order.symbol] position new_qty position.quantity order.quantity position.avg_price ( position.avg_price * position.quantity cost ) / new_qty position.quantity new_qty else: position account.positions.get(order.symbol) if position is None or position.quantity order.quantity: order.status OrderStatus.REJECTED return income fill_price * order.quantity account.cash income position.quantity - order.quantity realized_pnl (fill_price - position.avg_price) * order.quantity position.realized_pnl realized_pnl if position.quantity 0: del account.positions[order.symbol]这段代码有几点值得注意先校验再执行买入时检查现金卖出时检查持仓不足直接拒绝。滑点模拟通过随机数模拟滑点让模拟盘更接近真实。限价单简化这里只是简单判断一次实际上生产环境需要维护一个未成交订单容器每次行情更新都检查一遍。4.4 账户与持仓管理账户模块内部其实已经被Account模型覆盖了大部分功能。但为了隔离职责我们增加一个AccountService负责查询净值、计算收益率等。# 文件路径account.py from models import Account class AccountService: def __init__(self, market_data): self.market_data market_data def get_portfolio_value(self, account: Account) - float: 计算账户总资产 现金 持仓市值 total account.cash for symbol, pos in account.positions.items(): price self.market_data.get_price(symbol) total price * pos.quantity return total def get_account_snapshot(self, account: Account) - dict: 生成账户快照用于前端展示 portfolio_value self.get_portfolio_value(account) positions_detail [] for symbol, pos in account.positions.items(): price self.market_data.get_price(symbol) market_value price * pos.quantity unrealized_pnl (price - pos.avg_price) * pos.quantity positions_detail.append({ symbol: symbol, quantity: pos.quantity, avg_price: pos.avg_price, current_price: price, market_value: market_value, unrealized_pnl: round(unrealized_pnl, 2), }) return { user_id: account.user_id, cash: round(account.cash, 2), portfolio_value: round(portfolio_value, 2), positions: positions_detail, orders: [o.model_dump() for o in account.orders[-20:]], }4.5 AI 教练模块AI 教练是整个项目中最有想象力的部分。我会先展示一个基于规则的版本再说明如何接入 LLM。规则型 AI 教练通过检查账户状态和最近订单生成明确的教练建议。它的优点是稳定、无需外部 API、可解释。# 文件路径ai_coach.py from typing import List, Dict class RuleBasedCoach: 基于规则的 AI 教练。 通过分析账户状态、持仓和最近订单给出结构化建议。 def __init__(self, account_service): self.account_service account_service def analyze(self, account) - List[Dict[str, str]]: snap self.account_service.get_account_snapshot(account) suggestions [] # 风险1过度集中 positions snap[positions] if positions: max_pos max(positions, keylambda x: x[market_value]) total snap[portfolio_value] concentration max_pos[market_value] / total if concentration 0.4: suggestions.append({ level: warning, title: 仓位过于集中, message: f{max_pos[symbol]} 占当前持仓市值 {concentration:.0%}建议单品种仓位控制在 20%-30% 以内避免单一标的风险。, }) # 风险2现金比例过低 cash_ratio snap[cash] / snap[portfolio_value] if cash_ratio 0.1: suggestions.append({ level: warning, title: 可用现金不足, message: 当前现金比例较低市场出现回调时可能无法补仓或调整仓位。建议保留至少 10%-20% 现金。, }) # 风险3频繁交易 recent_orders snap[orders] if len(recent_orders) 5: suggestions.append({ level: notice, title: 交易频率偏高, message: 最近 20 笔订单中交易次数较多频繁交易会增加滑点和手续费成本。建议在入场前明确止盈止损。, }) # 机会市场趋势提醒 if not positions: suggestions.append({ level: info, title: 空仓观察, message: 当前没有持仓。如果市场方向不明确可以耐心等待不需要为了交易而交易。, }) return suggestions如果你希望教练有更自然的对话能力可以接入 OpenAI 兼容接口。关键是把账户数据转化为结构化的上下文再做提示词请求。# 文件路径ai_coach.py (扩展) import os from openai import OpenAI class LLMCoach: 基于大语言模型的 AI 教练。 将账户状态压缩为文本摘要向模型请求交易建议。 def __init__(self, api_key: str None, base_url: str None, model: str gpt-4o-mini): self.client OpenAI( api_keyapi_key or os.getenv(OPENAI_API_KEY), base_urlbase_url, ) self.model model def ask_for_advice(self, account_snapshot: dict) - str: # 先构造一个精简的数据摘要避免把全部原始 JSON 塞给模型 positions_str , .join( [f{p[symbol]} 数量{p[quantity]} 成本{p[avg_price]} 现价{p[current_price]} for p in account_snapshot[positions]] ) or 空仓 prompt f 你是一名专业的交易教练。请根据以下账户信息给出简洁、可执行的建议。 账户总资产: {account_snapshot[portfolio_value]} 当前现金: {account_snapshot[cash]} 持仓明细: {positions_str} 要求 1. 如果持仓过于集中请指出风险。 2. 如果现金比例过低请建议保留现金流。 3. 如果空仓请分析是否应该等待机会。 4. 不要给出确定性收益承诺不要引导重仓。 回复请控制在 200 字以内。 response self.client.chat.completions.create( modelself.model, messages[ {role: system, content: 你是一位谨慎、专业的交易教练。}, {role: user, content: prompt}, ], temperature0.7, ) return response.choices[0].message.content这里有一个非常重要的工程细节不能让 LLM 直接收到大量原始订单明细。这会造成两个问题——一是 token 消耗暴增二是模型被噪声干扰抓不住重点。在做任何 LLM Agent 项目时都应该先做数据摘要和裁剪。4.6 FastAPI 主程序与 WebSocket 推送最后把这些模块串起来。FastAPI 提供 HTTP 接口给前端调用下单、查询账户同时通过 WebSocket 推送行情和教练建议。# 文件路径main.py import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.staticfiles import StaticFiles from models import Account, OrderSide, OrderType from market_data import MarketDataSimulator from order_engine import OrderEngine from account import AccountService from ai_coach import RuleBasedCoach app FastAPI() market_data MarketDataSimulator({ AAPL: 193.50, TSLA: 248.20, BTCUSD: 67000.0, ETHUSD: 3500.0, EURUSD: 1.0850, }) account_service AccountService(market_data) order_engine OrderEngine(market_data) coach RuleBasedCoach(account_service) # 简单示例从文件加载或新建账户 accounts: dict[str, Account] {} websocket_clients: list[WebSocket] [] def get_or_create_account(user_id: str) - Account: if user_id not in accounts: accounts[user_id] Account(user_iduser_id) return accounts[user_id] app.get(/api/account/{user_id}) def get_account(user_id: str): account get_or_create_account(user_id) return account_service.get_account_snapshot(account) app.post(/api/order) async def place_order( user_id: str, symbol: str, side: OrderSide, order_type: OrderType, quantity: float, price: float None, ): account get_or_create_account(user_id) try: order order_engine.create_order( accountaccount, symbolsymbol.upper(), sideside, order_typeorder_type, quantityquantity, priceprice, ) return { status: order.status.value, order_id: order.id, filled_price: order.filled_price, message: 下单成功 if order.status.value filled else 订单处理失败, } except Exception as e: return {status: error, message: str(e)} app.websocket(/ws) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() websocket_clients.append(websocket) try: while True: data await websocket.receive_text() # 接收消息目前作为心跳使用 except WebSocketDisconnect: websocket_clients.remove(websocket) async def broadcast_snapshot(): 定时推送行情和教练建议 while True: await asyncio.sleep(2) market_snap market_data.tick() payload {market: market_snap} # 为每个在线用户生成账户快照和教练建议 for user_id, account in accounts.items(): account_snap account_service.get_account_snapshot(account) coach_advice coach.analyze(account) payload[account] account_snap payload[coach] coach_advice # 这里简化处理推送同一份数据。生产环境应根据 user_id 定向推送 for ws in websocket_clients: await ws.send_json(payload) asynccontextmanager async def lifespan(app: FastAPI): task asyncio.create_task(broadcast_snapshot()) yield task.cancel() app.router.lifespan_context lifespan # 挂载前端静态页面 app.mount(/, StaticFiles(directorystatic, htmlTrue), namestatic)4.7 前端控制台页面为了快速演示我们写一个极简的 HTML 页面包含行情列表、账户信息和下单表单。!-- 文件路径static/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 titlePaper Trading AI Coach/title style body { font-family: Arial, sans-serif; padding: 20px; background: #f5f7fa; } .container { display: flex; gap: 20px; } .card { background: #fff; padding: 20px; border-radius: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.06); flex: 1; } h3 { margin-top: 0; } .position-item { border-bottom: 1px solid #eee; padding: 8px 0; } .coach-warning { background: #fff7e6; border-left: 4px solid #faad14; padding: 12px; margin: 8px 0; } .coach-info { background: #e6f7ff; border-left: 4px solid #1890ff; padding: 12px; margin: 8px 0; } input, select, button { padding: 8px; margin: 4px; } button { background: #1890ff; color: #fff; border: none; border-radius: 4px; cursor: pointer; } /style /head body h1 模拟交易 AI 教练/h1 div classcontainer div classcard h3实时行情/h3 div idmarket/div /div div classcard h3账户信息/h3 div idaccount/div hr h3下单/h3 select idsymbol/select select idside option valuebuy买入/option option valuesell卖出/option /select input typenumber idquantity placeholder数量 value1 min0.001 step0.001 button onclickplaceOrder()提交订单/button /div div classcard h3AI 教练建议/h3 div idcoach/div /div /div script const user_id test_user_001; const ws new WebSocket(ws://${location.host}/ws); ws.onmessage (event) { const data JSON.parse(event.data); if (data.market) { const marketDiv document.getElementById(market); marketDiv.innerHTML Object.entries(data.market.prices) .map(([symbol, price]) div${symbol}: ${Number(price).toFixed(2)}/div) .join(); } if (data.account) { renderAccount(data.account); const symbolSelect document.getElementById(symbol); if (symbolSelect.options.length 0) { Object.keys(data.market.prices).forEach(symbol { const opt document.createElement(option); opt.value symbol; opt.textContent symbol; symbolSelect.appendChild(opt); }); } } if (data.coach) { const coachDiv document.getElementById(coach); coachDiv.innerHTML data.coach.map(item div class${item.level warning ? coach-warning : coach-info} strong${item.title}/strongbr${item.message} /div ).join(); } }; function renderAccount(snap) { const div document.getElementById(account); let html div现金: ${snap.cash}/div; html div总资产: ${snap.portfolio_value}/div; html hr; if (snap.positions.length 0) { html div暂无持仓/div; } snap.positions.forEach(p { html div classposition-item ${p.symbol} | 数量 ${p.quantity} | 成本 ${p.avg_price} | 现价 ${p.current_price} br浮动盈亏: ${p.unrealized_pnl} /div; }); div.innerHTML html; } async function placeOrder() { const symbol document.getElementById(symbol).value; const side document.getElementById(side).value; const quantity parseFloat(document.getElementById(quantity).value); const params new URLSearchParams({ user_id: user_id, symbol: symbol, side: side, order_type: market, quantity: quantity, }); const response await fetch(/api/order?${params}, { method: POST }); const result await response.json(); alert(${result.status}: ${result.message}); } /script /body /html5. 运行与验证完成以上代码后在项目根目录执行uvicorn main:app --reload --port 8000浏览器访问http://localhost:8000你可以看到页面左侧实时显示行情每 2 秒更新一次。中间区域显示账户现金、总资产和持仓。右下角 AI 教练会根据账户状态给出建议。选择股票代码和买卖方向输入数量点击提交订单。预期效果下单买入后现金减少持仓中出现对应 symbol总资产基本不变因为按市价单成交。卖出时持仓减少现金增加浮动盈亏转成已实现盈亏。如果持仓占比过高AI 教练会提示“仓位过于集中”。6. 常见问题与排查思路在实际运行或扩展这个项目的过程中有几个问题是高频出现的。问题现象常见原因解决思路WebSocket 连接不稳定代理、浏览器限制或服务端未处理异常检查浏览器控制台确认/ws路由可访问后端增加try/except保证单客户端断开不影响整体循环买入后总资产减少滑点导致成交价高于现价这是正常现象说明滑点逻辑生效。可调整滑点比例或改为固定价格限价单没有成交当前价格未达到限定价需要实现挂单队列每次行情更新时检查所有 pending 订单多用户数据互相影响全局账户存储在内存中生产环境应使用 Redis 或数据库按 user_id 隔离LLM 响应超时网络或模型服务不稳定添加超时控制、重试机制和降级方案降级到规则引擎精度问题浮点数计算导致账单不一致金额建议用 Decimal 表示涉及资金必须避免 float 直接运算订单重复提交前端重复点击或网络重试后端应实现幂等机制例如前端生成client_order_id排查这类系统问题时我建议按以下顺序先看订单状态是pending、filled还是rejected再看账户余额是否现金或持仓不足检查行情数据当前价格是否异常为 0 或负数确认 WebSocket 推送浏览器 Network 面板中能看到消息吗查看日志服务端打印的关键节点信息比如订单创建、成交价格、AI 分析输入。7. 最佳实践与工程建议7.1 事件驱动架构真实的模拟交易平台不应该像本文示例这样用定时器扫描所有账户。更推荐的做法是行情 tick 到达时触发“价格更新”事件订单引擎监听事件并检查待成交订单。这样能极大降低无效计算。7.2 数据存储选型用户账户PostgreSQL 或 MySQL强一致。订单流水可以用时序数据库如 TimescaleDB、ClickHouse或 MongoDB。行情快照高频数据建议用 Redis 存缓存InfluxDB 或 ClickHouse 做持久化。AI 对话记录普通 JSON 存储即可或存入 ES 便于检索复盘。7.3 安全与合规边界这一点非常重要。模拟交易平台虽然没有真实资金但依然要注意只做“教学工具”定位不要承诺收益。页面显著位置提示“模拟交易不涉及真实资金仅供学习使用”。不提供任何形式的杠杆推荐或喊单引导。如果需要接入真实行情 API注意数据授权和 API Key 保护不要把 Key 暴露在前端。7.4 AI 教练模块的可靠性AI 教练的输出如果不加约束可能产生误导性建议。工程上建议核心风险参数集中度、现金比例先用规则引擎计算LLM 只能解释不能修改结果。给 LLM 设定行为边界要求不得建议使用高杠杆不得承诺收益。对 LLM 输出做后置校验检测是否包含敏感词或违规信息。记录每次 AI 建议方便追溯和评估模型效果。7.5 从模拟盘到实盘的平滑迁移如果你的最终目标是让用户把模拟盘策略迁移到真实交易那么平台在设计时就应该预留统一接口。例如订单引擎和行情模块都抽象成接口模拟实现和实盘实现可以互相替换。这样才能避免策略在模拟盘有效、换到实盘就失效的问题。8. 总结与下一步方向这篇文章围绕“免费 Paper Trading 平台 AI 教练”这个项目主题从一个可运行的最小系统出发完整拆解了行情模拟、订单撮合、账户管理、AI 教练接入和前端推送的全链路实现。核心收获有几点Paper Trading 平台的本质是“真实交易系统的过程仿真”核心价值在于安全地试错和数据沉淀。AI 教练不是要取代用户决策而是要做“可解释的提醒者”规则引擎负责确定性风险LLM 负责语言交互。工程落地的关键是模块解耦行情、订单、账户、AI 各有边界这样后续扩展真实数据源或实盘通道时才不会乱成一团。如果你想在这个项目基础上继续深入建议按这几个方向探索接入真实行情替换MarketDataSimulator为真实数据源 SDK比如券商 API、交易所 WebSocket。实现限价单队列当前限价单只做一次判断可以扩展为完整的挂单簿模型。实现交易回放功能保存所有 tick 数据让用户可以回看自己当时为什么在某价位下单。AI 复盘报告每天定时调用 LLM 生成个人交易行为分析报告分析错误模式。模拟交易平台的价值不在于代码量多少而在于它能否帮用户建立正确的交易认知。AI 教练的价值也恰恰体现在这里——它可以把交易中的模糊感觉变成清晰、可验证、可复盘的行为原则。希望这篇文章能给你提供一个扎实的起点后续的玩法就可以按你的需求自由发挥了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门