下载地址:https://www.pan38.com/share.php?code=JCnzE 提取密码:7789
完整的股票模拟系统包含账户管理、交易记录和可视化分析三大模块,使用纯JavaScript实现。核心功能包括模拟股票价格波动、买卖交易、生成交割单和绘制收益曲线图。代码采用面向对象设计,各模块职责分明,可通过扩展实现更复杂的金融分析功能。
class StockAccountSimulator {
constructor(initialBalance = 100000) {
this.balance = initialBalance;
this.portfolio = {};
this.transactionHistory = [];
this.stockData = this.generateMockStockData();
}
generateMockStockData() {
const stocks = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'TSLA', 'NVDA', 'META', 'BABA', 'JD', 'NIO'];
return stocks.reduce((acc, symbol) => {
acc[symbol] = {
currentPrice: Math.random() 500 + 50,
volatility: Math.random() 0.3 + 0.1,
dividendYield: Math.random() * 0.03
};
return acc;
}, {});
}
updateStockPrices() {
Object.keys(this.stockData).forEach(symbol => {
const stock = this.stockData[symbol];
const change = (Math.random() 2 - 1) stock.volatility;
stock.currentPrice *= (1 + change);
stock.currentPrice = Math.max(10, stock.currentPrice);
});
}
buyStock(symbol, quantity) {
if (!this.stockData[symbol]) throw new Error('Invalid stock symbol');
const price = this.stockData[symbol].currentPrice;
const cost = price * quantity;
if (cost > this.balance) throw new Error('Insufficient funds');
this.balance -= cost;
this.portfolio[symbol] = (this.portfolio[symbol] || 0) + quantity;
this.transactionHistory.push({
type: 'BUY',
symbol,
quantity,
price,
timestamp: new Date(),
remainingBalance: this.balance
});
return { success: true, newBalance: this.balance };
}
sellStock(symbol, quantity) {
if (!this.portfolio[symbol] || this.portfolio[symbol] < quantity) {
throw new Error('Not enough shares to sell');
}
const price = this.stockData[symbol].currentPrice;
const proceeds = price * quantity;
this.balance += proceeds;
this.portfolio[symbol] -= quantity;
if (this.portfolio[symbol] === 0) delete this.portfolio[symbol];
this.transactionHistory.push({
type: 'SELL',
symbol,
quantity,
price,
timestamp: new Date(),
remainingBalance: this.balance
});
return { success: true, newBalance: this.balance };
}
generateTradeConfirmation(transactionId) {
const transaction = this.transactionHistory[transactionId];
if (!transaction) throw new Error('Invalid transaction ID');
return `
TRADE CONFIRMATION #${transactionId}
----------------------------
Type: ${transaction.type}
Symbol: ${transaction.symbol}
Quantity: ${transaction.quantity}
Price: $${transaction.price.toFixed(2)}
Amount: $${(transaction.price * transaction.quantity).toFixed(2)}
Date: ${transaction.timestamp.toLocaleString()}
Remaining Balance: $${transaction.remainingBalance.toFixed(2)}
`;
}
}
TradeLedgerGenerator {
constructor(simulator) {
this.simulator = simulator;
}
generateDailyLedger(date = new Date()) {
const dateStr = date.toISOString().split('T')[0];
const dailyTrades = this.simulator.transactionHistory.filter(
t => t.timestamp.toISOString().split('T')[0] === dateStr
);
if (dailyTrades.length === 0) return 'No trades on this date';
let ledger = `DAILY TRADE LEDGER - ${dateStr}\n`;
ledger += '='.repeat(50) + '\n';
dailyTrades.forEach((trade, idx) => {
ledger += `Trade #${idx + 1}\n`;
ledger += ` Type: ${trade.type.padEnd(6)} `;
ledger += `Symbol: ${trade.symbol.padEnd(6)} `;
ledger += `Qty: ${trade.quantity.toString().padEnd(6)} `;
ledger += `Price: $${trade.price.toFixed(2).padEnd(8)} `;
ledger += `Amount: $${(trade.price * trade.quantity).toFixed(2)}\n`;
});
const totalBuy = dailyTrades
.filter(t => t.type === 'BUY')
.reduce((sum, t) => sum + t.price * t.quantity, 0);
const totalSell = dailyTrades
.filter(t => t.type === 'SELL')
.reduce((sum, t) => sum + t.price * t.quantity, 0);
ledger += '\nSUMMARY:\n';
ledger += ` Total Buy: $${totalBuy.toFixed(2)}\n`;
ledger += ` Total Sell: $${totalSell.toFixed(2)}\n`;
ledger += ` Net Change: $${(totalSell - totalBuy).toFixed(2)}\n`;
ledger += '='.repeat(50);
return ledger;
}
generatePortfolioStatement() {
let statement = PORTFOLIO STATEMENT - ${new Date().toLocaleDateString()}\n
;
statement += '='.repeat(50) + '\n';
let totalValue = 0;
Object.keys(this.simulator.portfolio).forEach(symbol => {
const quantity = this.simulator.portfolio[symbol];
const price = this.simulator.stockData[symbol].currentPrice;
const value = price * quantity;
totalValue += value;
statement += `${symbol.padEnd(6)}: `;
statement += `${quantity.toString().padEnd(6)} shares @ `;
statement += `$${price.toFixed(2).padEnd(8)} = `;
statement += `$${value.toFixed(2)}\n`;
});
statement += '\nSUMMARY:\n';
statement += ` Cash Balance: $${this.simulator.balance.toFixed(2)}\n`;
statement += ` Stock Value: $${totalValue.toFixed(2)}\n`;
statement += ` Total Assets: $${(this.simulator.balance + totalValue).toFixed(2)}\n`;
statement += '='.repeat(50);
return statement;
}
}
class PerformanceChart {
constructor(simulator) {
this.simulator = simulator;
this.historicalData = [];
this.recordDailySnapshot();
}
recordDailySnapshot() {
const snapshot = {
date: new Date(),
cash: this.simulator.balance,
holdings: {},
totalValue: this.simulator.balance
};
Object.keys(this.simulator.portfolio).forEach(symbol => {
const quantity = this.simulator.portfolio[symbol];
const price = this.simulator.stockData[symbol].currentPrice;
const value = price * quantity;
snapshot.holdings[symbol] = value;
snapshot.totalValue += value;
});
this.historicalData.push(snapshot);
return snapshot;
}
generatePerformanceChart(days = 30) {
const recentData = this.historicalData.slice(-days);
if (recentData.length < 2) return 'Not enough data to generate chart';
const dates = recentData.map(d => d.date.toLocaleDateString());
const values = recentData.map(d => d.totalValue);
const baseValue = values[0];
const percentages = values.map(v => ((v - baseValue) / baseValue * 100).toFixed(2));
let chart = 'PORTFOLIO PERFORMANCE CHART\n';
chart += 'Date '.padEnd(12) + 'Value '.padEnd(12) + 'Change % '.padEnd(12) + 'Chart\n';
chart += '-'.repeat(60) + '\n';
const maxValue = Math.max(...values);
const minValue = Math.min(...values);
const valueRange = maxValue - minValue;
recentData.forEach((data, idx) => {
const value = values[idx];
const pct = percentages[idx];
const position = Math.round(((value - minValue) / valueRange) * 20);
chart += `${dates[idx].padEnd(12)}`;
chart += `$${value.toFixed(2).padEnd(12)}`;
chart += `${pct}%`.padEnd(12);
chart += '|' + ' '.repeat(position) + '●' + ' '.repeat(20 - position) + '|\n';
});
chart += '-'.repeat(60) + '\n';
chart += `Starting Value: $${baseValue.toFixed(2)}\n`;
chart += `Current Value: $${values[values.length - 1].toFixed(2)}\n`;
chart += `Total Return: ${percentages[percentages.length - 1]}%\n`;
return chart;
}
generateStockAllocationChart() {
const latest = this.historicalData[this.historicalData.length - 1];
if (!latest || Object.keys(latest.holdings).length === 0) {
return 'No stock holdings to display';
}
const total = latest.totalValue - latest.cash;
let chart = 'STOCK ALLOCATION CHART\n';
chart += '='.repeat(50) + '\n';
Object.keys(latest.holdings)
.sort((a, b) => latest.holdings[b] - latest.holdings[a])
.forEach(symbol => {
const value = latest.holdings[symbol];
const pct = (value / total * 100).toFixed(1);
const bars = '■'.repeat(Math.round(pct / 2));
chart += `${symbol.padEnd(6)}: `;
chart += `${pct}% `.padEnd(6);
chart += `${bars}\n`;
});
chart += '='.repeat(50) + '\n';
chart += `Total Stock Value: $${total.toFixed(2)}\n`;
chart += `Cash: $${latest.cash.toFixed(2)}\n`;
chart += `Total Portfolio: $${latest.totalValue.toFixed(2)}\n`;
return chart;
}
}
系统初始化
const simulator = new StockAccountSimulator(50000);
const ledger = new TradeLedgerGenerator(simulator);
const chart = new PerformanceChart(simulator);
// 模拟市场波动和交易
function simulateTrading(days = 30) {
for (let i = 0; i < days; i++) {
simulator.updateStockPrices();
// 随机买卖
const symbols = Object.keys(simulator.stockData);
const randomSymbol = symbols[Math.floor(Math.random() * symbols.length)];
const action = Math.random() > 0.5 ? 'BUY' : 'SELL';
const quantity = Math.floor(Math.random() * 10) + 1;
try {
if (action === 'BUY') {
simulator.buyStock(randomSymbol, quantity);
} else {
if (simulator.portfolio[randomSymbol]) {
simulator.sellStock(randomSymbol, Math.min(quantity, simulator.portfolio[randomSymbol]));
}
}
} catch (e) {
console.log(`Day ${i+1}: ${e.message}`);
}
chart.recordDailySnapshot();
}
}
// 执行模拟
simulateTrading(90);
// 输出结果
console.log('=== 最新投资组合 ===');
console.log(ledger.generatePortfolioStatement());
console.log('\n=== 最近30天表现 ===');
console.log(chart.generatePerformanceChart(30));
console.log('\n=== 股票配置 ===');
console.log(chart.generateStockAllocationChart());
console.log('\n=== 随机交易日交割单 ===');
const randomDay = new Date();
randomDay.setDate(randomDay.getDate() - Math.floor(Math.random() * 30));
console.log(ledger.generateDailyLedger(randomDay));