股票虚拟仿真交易软件, 最真实的模拟炒股软件, 股票模拟器app

简介: 完整的股票模拟交易系统的JavaScript实现。这个系统包含实时行情模拟、交易引擎、账户管理、K线图

下载地址【已上传】:https://www.pan38.com/share.php?code=JCnzE 提取码:6666
声明:所下载的文件以及如下所示代码仅供学习参考用途,作者并不提供软件的相关服务。

完整的股票模拟交易系统的JavaScript实现。这个系统包含实时行情模拟、交易引擎、账户管理、K线图表等核心功能模块。

/**

  • 股票虚拟仿真交易系统
  • 功能包含:
    1. 实时行情模拟
    1. 交易引擎
    1. 账户管理
    1. K线图表
    1. 历史数据回放
      */

// 系统配置
const CONFIG = {
INITIAL_BALANCE: 100000, // 初始资金
COMMISSION_RATE: 0.0003, // 佣金费率
STAMP_TAX: 0.001, // 印花税
TRANSFER_FEE: 0.00002, // 过户费
UPDATE_INTERVAL: 1000 // 行情更新间隔(ms)
};

// 股票数据生成器
class StockDataGenerator {
constructor() {
this.stocks = {
'SH600000': { name: '浦发银行', price: 10.25, open: 10.20, high: 10.30, low: 10.15, vol: 500000 },
'SH601318': { name: '中国平安', price: 45.60, open: 45.50, high: 46.20, low: 45.30, vol: 800000 },
'SZ000001': { name: '平安银行', price: 12.75, open: 12.70, high: 12.85, low: 12.65, vol: 600000 },
'SZ000002': { name: '万科A', price: 22.40, open: 22.30, high: 22.50, low: 22.20, vol: 400000 }
};
this.historyData = {};
this.initHistoryData();
}

initHistoryData() {
for (const code in this.stocks) {
this.historyData[code] = this.generateHistoryData(this.stocks[code].price);
}
}

generateHistoryData(basePrice) {
const data = [];
const now = new Date();
const days = 30;

for (let i = days; i >= 0; i--) {
  const date = new Date(now);
  date.setDate(date.getDate() - i);

  const open = basePrice * (0.95 + Math.random() * 0.1);
  const close = open * (0.98 + Math.random() * 0.04);
  const high = Math.max(open, close) * (1 + Math.random() * 0.02);
  const low = Math.min(open, close) * (0.98 + Math.random() * 0.02);
  const vol = Math.floor(100000 + Math.random() * 900000);

  data.push({
    date: date.toISOString().split('T')[0],
    open: parseFloat(open.toFixed(2)),
    close: parseFloat(close.toFixed(2)),
    high: parseFloat(high.toFixed(2)),
    low: parseFloat(low.toFixed(2)),
    vol: vol
  });
}

return data;

}

updatePrices() {
for (const code in this.stocks) {
const stock = this.stocks[code];
const change = (Math.random() - 0.5) 0.1;
const newPrice = stock.price
(1 + change);

  stock.price = parseFloat(newPrice.toFixed(2));
  stock.high = Math.max(stock.high, stock.price);
  stock.low = Math.min(stock.low, stock.price);
  stock.vol = Math.floor(stock.vol * (0.9 + Math.random() * 0.2));

  // 添加到历史数据
  const now = new Date();
  const today = now.toISOString().split('T')[0];
  const lastDay = this.historyData[code][this.historyData[code].length - 1];

  if (lastDay.date === today) {
    lastDay.close = stock.price;
    lastDay.high = stock.high;
    lastDay.low = stock.low;
    lastDay.vol = stock.vol;
  } else {
    this.historyData[code].push({
      date: today,
      open: stock.open,
      close: stock.price,
      high: stock.high,
      low: stock.low,
      vol: stock.vol
    });

    if (this.historyData[code].length > 60) {
      this.historyData[code].shift();
    }
  }
}

return this.stocks;

}

getStock(code) {
return this.stocks[code];
}

getHistoryData(code, days = 30) {
if (!this.historyData[code]) return null;
return this.historyData[code].slice(-days);
}
}

// 交易引擎
class TradingEngine {
constructor(dataGenerator) {
this.dataGenerator = dataGenerator;
this.orders = [];
this.transactions = [];
this.account = {
balance: CONFIG.INITIAL_BALANCE,
positions: {},
totalValue: CONFIG.INITIAL_BALANCE,
history: []
};
}

// 计算交易费用
calculateFees(code, price, quantity, isBuy) {
const stock = this.dataGenerator.getStock(code);
if (!stock) return 0;

const amount = price * quantity;
let fees = 0;

// 佣金
fees += amount * CONFIG.COMMISSION_RATE;
if (fees < 5) fees = 5; // 最低5元

// 印花税 (卖出时收取)
if (!isBuy) {
  fees += amount * CONFIG.STAMP_TAX;
}

// 过户费
fees += amount * CONFIG.TRANSFER_FEE;

return parseFloat(fees.toFixed(2));

}

// 下单
placeOrder(code, price, quantity, type) {
const stock = this.dataGenerator.getStock(code);
if (!stock) return { success: false, message: '股票不存在' };

const isBuy = type === 'buy';
const amount = price * quantity;
const fees = this.calculateFees(code, price, quantity, isBuy);

if (isBuy) {
  const totalCost = amount + fees;
  if (totalCost > this.account.balance) {
    return { success: false, message: '资金不足' };
  }
} else {
  const position = this.account.positions[code] || { quantity: 0 };
  if (position.quantity < quantity) {
    return { success: false, message: '持仓不足' };
  }
}

const order = {
  id: Date.now().toString(),
  code,
  name: stock.name,
  price,
  quantity,
  type,
  status: 'pending',
  time: new Date().toISOString(),
  fees
};

this.orders.push(order);
this.matchOrder(order);

return { success: true, order };

}

// 撮合订单
matchOrder(order) {
const stock = this.dataGenerator.getStock(order.code);
if (!stock) return;

// 简单撮合逻辑 - 按当前价格成交
if ((order.type === 'buy' && order.price >= stock.price) || 
    (order.type === 'sell' && order.price <= stock.price)) {
  this.executeOrder(order, stock.price);
}

}

// 执行订单
executeOrder(order, executePrice) {
order.status = 'filled';
order.executePrice = executePrice;
order.executeTime = new Date().toISOString();

const isBuy = order.type === 'buy';
const amount = executePrice * order.quantity;
const fees = order.fees;

if (isBuy) {
  this.account.balance -= (amount + fees);

  if (!this.account.positions[order.code]) {
    this.account.positions[order.code] = {
      quantity: 0,
      cost: 0,
      profit: 0
    };
  }

  const position = this.account.positions[order.code];
  const totalCost = position.cost * position.quantity + amount;
  position.quantity += order.quantity;
  position.cost = totalCost / position.quantity;
} else {
  this.account.balance += (amount - fees);

  const position = this.account.positions[order.code];
  position.quantity -= order.quantity;
  position.profit += (executePrice - position.cost) * order.quantity;

  if (position.quantity === 0) {
    delete this.account.positions[order.code];
  }
}

const transaction = {
  id: Date.now().toString(),
  code: order.code,
  name: order.name,
  price: executePrice,
  quantity: order.quantity,
  type: order.type,
  amount,
  fees,
  time: order.executeTime
};

this.transactions.push(transaction);
this.updateAccountValue();

return transaction;

}

// 更新账户总价值
updateAccountValue() {
let positionsValue = 0;

for (const code in this.account.positions) {
  const stock = this.dataGenerator.getStock(code);
  if (stock) {
    const position = this.account.positions[code];
    positionsValue += stock.price * position.quantity;
  }
}

this.account.totalValue = this.account.balance + positionsValue;

// 记录账户历史
this.account.history.push({
  date: new Date().toISOString(),
  totalValue: this.account.totalValue,
  balance: this.account.balance,
  positionsValue
});

if (this.account.history.length > 100) {
  this.account.history.shift();
}

}

// 获取账户信息
getAccountInfo() {
this.updateAccountValue();
return {
balance: this.account.balance,
totalValue: this.account.totalValue,
positions: this.account.positions,
history: this.account.history
};
}

// 获取持仓详情
getPositionDetails() {
const details = [];

for (const code in this.account.positions) {
  const stock = this.dataGenerator.getStock(code);
  if (stock) {
    const position = this.account.positions[code];
    const marketValue = stock.price * position.quantity;
    const profit = (stock.price - position.cost) * position.quantity;
    const profitRate = (stock.price - position.cost) / position.cost;

    details.push({
      code,
      name: stock.name,
      quantity: position.quantity,
      cost: position.cost,
      currentPrice: stock.price,
      marketValue: parseFloat(marketValue.toFixed(2)),
      profit: parseFloat(profit.toFixed(2)),
      profitRate: parseFloat(profitRate.toFixed(4)),
      weight: parseFloat((marketValue / this.account.totalValue).toFixed(4))
    });
  }
}

return details;

}
}

// K线图表渲染器
class KLineRenderer {
constructor(canvasId, dataGenerator) {
this.canvas = document.getElementById(canvasId);
this.ctx = this.canvas.getContext('2d');
this.dataGenerator = dataGenerator;
this.config = {
padding: { top: 20, right: 20, bottom: 30, left: 50 },
colors: {
up: '#f6465d',
down: '#0ecb81',
grid: '#e6e6e6',
text: '#333',
background: '#fff'
},
candleWidth: 8,
gap: 2
};
}

render(code, days = 30) {
const data = this.dataGenerator.getHistoryData(code, days);
if (!data || data.length === 0) return;

const width = this.canvas.width;
const height = this.canvas.height;
const padding = this.config.padding;
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;

// 清空画布
this.ctx.fillStyle = this.config.colors.background;
this.ctx.fillRect(0, 0, width, height);

// 计算价格范围
let minPrice = Infinity;
let maxPrice = -Infinity;
let maxVol = 0;

data.forEach(item => {
  minPrice = Math.min(minPrice, item.low);
  maxPrice = Math.max(maxPrice, item.high);
  maxVol = Math.max(maxVol, item.vol);
});

const priceRange = maxPrice - minPrice;
const priceStep = priceRange / 5;

// 绘制网格和价格刻度
this.ctx.strokeStyle = this.config.colors.grid;
this.ctx.fillStyle = this.config.colors.text;
this.ctx.font = '10px Arial';
this.ctx.textAlign = 'right';

for (let i = 0; i <= 5; i++) {
  const price = maxPrice - i * priceStep;
  const y = padding.top + (i * chartHeight / 5);

  this.ctx.beginPath();
  this.ctx.moveTo(padding.left, y);
  this.ctx.lineTo(width - padding.right, y);
  this.ctx.stroke();

  this.ctx.fillText(price.toFixed(2), padding.left - 5, y + 4);
}

// 绘制日期刻度
this.ctx.textAlign = 'center';
const dateStep = Math.max(1, Math.floor(data.length / 5));

for (let i = 0; i < data.length; i += dateStep) {
  const x = padding.left + (i * (this.config.candleWidth + this.config.gap)) + 
            (this.config.candleWidth / 2);
  const date = data[i].date.split('-').slice(1).join('/');

  this.ctx.fillText(date, x, height - padding.bottom + 15);
}

// 绘制K线
const candleWidth = this.config.candleWidth;
const gap = this.config.gap;

for (let i = 0; i < data.length; i++) {
  const item = data[i];
  const isUp = item.close >= item.open;
  const color = isUp ? this.config.colors.up : this.config.colors.down;

  const x = padding.left + i * (candleWidth + gap);
  const openY = padding.top + chartHeight * (maxPrice - item.open) / priceRange;
  const closeY = padding.top + chartHeight * (maxPrice - item.close) / priceRange;
  const highY = padding.top + chartHeight * (maxPrice - item.high) / priceRange;
  const lowY = padding.top + chartHeight * (maxPrice - item.low) / priceRange;

  // 绘制上下影线
  this.ctx.strokeStyle = color;
  this.ctx.lineWidth = 1;
  this.ctx.beginPath();
  this.ctx.moveTo(x + candleWidth / 2, highY);
  this.ctx.lineTo(x + candleWidth / 2, lowY);
  this.ctx.stroke();

  // 绘制实体
  this.ctx.fillStyle = color;
  const bodyTop = Math.min(openY, closeY);
  const bodyHeight = Math.abs(openY - closeY);
  this.ctx.fillRect(x, bodyTop, candleWidth, bodyHeight || 1);

  // 绘制成交量
  const volHeight = (item.vol / maxVol) * (chartHeight * 0.2);
  this.ctx.fillStyle = isUp ? this.config.colors.up : this.config.colors.down;
  this.ctx.fillRect(x, height - padding.bottom - volHeight, candleWidth, volHeight);
}

}
}

// UI控制器
class UIController {
constructor(dataGenerator, tradingEngine) {
this.dataGenerator = dataGenerator;
this.tradingEngine = tradingEngine;
this.currentStock = 'SH600000';
this.initUI();
this.startDataUpdate();
}

initUI() {
// 初始化股票选择器
const stockSelect = document.getElementById('stock-select');
for (const code in this.dataGenerator.stocks) {
const stock = this.dataGenerator.stocks[code];
const option = document.createElement('option');
option.value = code;
option.textContent = ${code} ${stock.name};
stockSelect.appendChild(option);
}

stockSelect.addEventListener('change', (e) => {
  this.currentStock = e.target.value;
  this.updateStockInfo();
  this.updateChart();
});

// 初始化交易表单
const tradeForm = document.getElementById('trade-form');
tradeForm.addEventListener('submit', (e) => {
  e.preventDefault();
  const code = this.currentStock;
  const type = document.getElementById('trade-type').value;
  const price = parseFloat(document.getElementById('trade-price').value);
  const quantity = parseInt(document.getElementById('trade-quantity').value);

  const result = this.tradingEngine.placeOrder(code, price, quantity, type);
  if (result.success) {
    alert(`订单提交成功! 订单号: ${result.order.id}`);
    this.updateAccountInfo();
  } else {
    alert(`订单提交失败: ${result.message}`);
  }
});

// 初始化K线图
this.klineRenderer = new KLineRenderer('kline-canvas', this.dataGenerator);

// 初始更新
this.updateStockInfo();
this.updateAccountInfo();
this.updateChart();

}

startDataUpdate() {
setInterval(() => {
this.dataGenerator.updatePrices();
this.updateStockInfo();
this.updateChart();
}, CONFIG.UPDATE_INTERVAL);
}

updateStockInfo() {
const stock = this.dataGenerator.getStock(this.currentStock);
if (!stock) return;

const change = stock.price - stock.open;
const changePercent = (change / stock.open) * 100;
const is
相关文章
|
11天前
|
前端开发 数据安全/隐私保护
股票持仓截图生成器手机版, 股票持仓图生成器免费,交割单生成器制作工具
代码实现了一个完整的股票持仓截图生成器,包含数据模拟、表格绘制、汇总计算和水印添加功能。
|
19天前
|
NoSQL Java Shell
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
51 8
2025服务端java搭建篇:蜻蜓I即时通讯系统私有化部署深度指南-优雅草卓伊凡|麻子|贝贝
|
人工智能 安全 Java
Serverless JManus: 企业生产级通用智能体运行时
JManus 是面向 Java 的企业级通用智能体框架,支持多 Agent 框架、MCP 协议和 PLAN-ACT 模式,具备高可用、弹性伸缩的特性。结合阿里云 Serverless 运行时 SAE 和 FC,实现稳定安全的智能体应用部署与运行。
151 22
|
12天前
|
JSON 中间件 Go
Go语言实战指南 —— Go中的反射机制:reflect 包使用
Go语言中的反射机制通过`reflect`包实现,允许程序在运行时动态检查变量类型、获取或设置值、调用方法等。它适用于初中级开发者深入理解Go的动态能力,帮助构建通用工具、中间件和ORM系统等。
151 63
|
19天前
|
存储 运维 JavaScript
《HarmonyOSNext应用崩溃自救指南:零数据丢失的故障恢复黑科技》
本文详解HarmonyOS Next应用崩溃时如何实现零数据丢失的故障恢复机制,涵盖API差异、核心接口与实战代码,助开发者提升App稳定性和用户体验。
139 65
|
19天前
|
监控 前端开发 JavaScript
HarmonyOSNext 崩溃急救指南:全局监听+同步退出 = 优雅保命!
本文介绍了HarmonyOS Next中Ark Ts的错误管理技巧,通过全局监听和同步退出机制实现应用崩溃保护。涵盖单线程、Promise及主线程卡死监控方案,并提供实战代码与避坑指南,帮助开发者优雅处理异常,保障用户体验。
134 65
|
14天前
|
机器学习/深度学习 消息中间件 人工智能
别只会写脚本了!看看机器学习是怎么帮运维“摸鱼”的
别只会写脚本了!看看机器学习是怎么帮运维“摸鱼”的
47 13
|
7天前
|
存储 机器学习/深度学习 API
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
71 31
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
|
26天前
|
Kubernetes Cloud Native 安全
云原生机密计算新范式 PeerPods技术方案在阿里云上的落地和实践
PeerPods 技术价值已在阿里云实际场景中深度落地。