@gabriel3615/ta_analysis
Version:
stock ta analysis
325 lines (324 loc) • 14.5 kB
JavaScript
import { getStockDataForTimeframe } from '../util/util.js';
import { globalLogger } from '../util/logger.js';
/**
* 模拟特定波动率下的股价路径
* @returns 每日股价数组
* @param symbol
*/
async function simulatePricePath(symbol) {
const today = new Date();
globalLogger.log('正在获取数据与分析筹码分布...');
const startDate = new Date();
startDate.setDate(today.getDate() - 365); // 获取一年的数据
try {
const dailyData = await getStockDataForTimeframe(symbol, startDate, today, 'daily'); // 获取日线数据
return dailyData.map(c => c.close);
}
catch (error) {
globalLogger.error('获取股票数据失败:', error);
throw new Error(`获取${symbol}的股票数据失败`);
}
}
/**
* 计算在特定价格路径下做空滚仓策略的收益
* @param pricePath 价格路径数组
* @param params 策略参数
* @returns 策略收益结果
*/
function calculateShortStrategyReturns(pricePath, params) {
const { initialShortShares, feePerTrade, upPercent, downPercent, tradingRatio, stopLossPercent, } = params;
const initialPrice = pricePath[0];
// 初始化状态
let currentShortShares = initialShortShares;
let avgShortPrice = initialPrice; // 做空的平均价格
let cashBalance = initialShortShares * initialPrice; // 做空得到的现金
let totalFees = 0;
const tradingPosition = Math.floor(initialShortShares * tradingRatio);
// 跟踪做空状态
let hasShorted = false;
let lastShortPrice = 0;
let lastTradeDay = 0;
// 跟踪止损状态
let stopLossCount = 0;
let lastStopLossDay = 0;
const trades = [];
// 遍历价格路径
for (let day = 1; day < pricePath.length; day++) {
const currentPrice = pricePath[day];
// 修复: 动态止损,基于平均做空价格而不是初始价格
if (currentShortShares > 0 &&
currentPrice >= avgShortPrice * (1 + stopLossPercent)) {
// 执行平仓止损
const coverValue = currentShortShares * currentPrice;
const coverFee = feePerTrade;
cashBalance -= coverValue + coverFee;
totalFees += coverFee;
// 记录交易
const stopLossTrade = {
day,
type: 'stopLoss',
price: currentPrice,
shares: currentShortShares,
value: coverValue,
fee: coverFee,
newShortShares: 0,
newAvgShortPrice: 0,
cycleProfitLoss: currentShortShares * avgShortPrice - coverValue - coverFee,
};
trades.push(stopLossTrade);
// 更新状态
currentShortShares = 0;
avgShortPrice = 0;
hasShorted = false;
lastTradeDay = day;
lastStopLossDay = day;
stopLossCount++;
continue;
}
// 如果未做空状态,检查是否满足做空条件
if (!hasShorted) {
// 价格上涨达到触发条件
if (currentPrice >= pricePath[lastTradeDay] * (1 + upPercent)) {
// 执行做空
const shortValue = tradingPosition * currentPrice;
const shortFee = feePerTrade;
cashBalance += shortValue - shortFee;
totalFees += shortFee;
// 更新持仓和成本
currentShortShares += tradingPosition;
// 修复: 正确计算平均做空价格
avgShortPrice =
(avgShortPrice * (currentShortShares - tradingPosition) +
currentPrice * tradingPosition) /
currentShortShares;
// 记录交易
const shortTrade = {
day,
type: 'short',
price: currentPrice,
shares: tradingPosition,
value: shortValue,
fee: shortFee,
newShortShares: currentShortShares,
newAvgShortPrice: avgShortPrice,
};
trades.push(shortTrade);
// 更新状态
hasShorted = true;
lastShortPrice = currentPrice;
lastTradeDay = day;
}
}
// 如果已做空状态,检查是否满足平仓条件
else {
// 价格下跌达到触发条件
if (currentPrice <= lastShortPrice * (1 - downPercent)) {
// 执行平仓
const coverValue = tradingPosition * currentPrice;
const coverFee = feePerTrade;
cashBalance -= coverValue + coverFee;
totalFees += coverFee;
// 更新持仓和成本(保持平均做空价格不变,因为我们是按比例平仓)
currentShortShares -= tradingPosition;
// 修复: 仅当所有持仓都平仓时重置avgShortPrice
if (currentShortShares <= 0) {
currentShortShares = 0;
avgShortPrice = 0;
}
// 计算单次循环利润
const cycleProfitLoss = tradingPosition * lastShortPrice - coverValue - feePerTrade * 2;
// 记录交易
const coverTrade = {
day,
type: 'cover',
price: currentPrice,
shares: tradingPosition,
value: coverValue,
fee: coverFee,
newShortShares: currentShortShares,
newAvgShortPrice: avgShortPrice,
cycleProfitLoss,
};
trades.push(coverTrade);
// 更新状态
if (currentShortShares === 0) {
hasShorted = false;
}
lastTradeDay = day;
}
}
}
// 计算最终结果
const finalPrice = pricePath[pricePath.length - 1];
const finalShortLiability = currentShortShares > 0 ? currentShortShares * finalPrice : 0;
const totalReturn = cashBalance - finalShortLiability;
const returnRate = (totalReturn / (initialShortShares * initialPrice)) * 100;
return {
initialInvestment: initialShortShares * initialPrice,
finalShortShares: currentShortShares,
finalAvgShortPrice: avgShortPrice,
finalShortLiability,
cashBalance,
totalFees,
totalTrades: trades.length,
totalReturn,
returnRate,
breakevenDropNeeded: currentShortShares > 0 ? (finalPrice / avgShortPrice - 1) * 100 : 0,
trades,
stopLossCount,
};
}
/**
* 分析做空滚仓策略在多种市场情景下的预期收益
* @param params 策略和模拟参数
* @returns 综合分析结果
*/
async function analyzeShortExpectedReturns(params) {
try {
const pricePath = await simulatePricePath(params.symbol);
// 计算策略收益
const result = calculateShortStrategyReturns(pricePath, params);
// 修复: 添加成功率计算
const successfulTrades = result.trades.filter(trade => trade.type === 'cover' && trade.cycleProfitLoss > 0).length;
const totalCloseTrades = result.trades.filter(trade => trade.type === 'cover' || trade.type === 'stopLoss').length;
const successRate = totalCloseTrades > 0 ? (successfulTrades / totalCloseTrades) * 100 : 0;
return {
simulationParams: {
initialPrice: pricePath[0],
currentPrice: pricePath[pricePath.length - 1],
stopLossPercent: params.stopLossPercent,
volatility: params.volatility, // 添加波动率参数
},
averageResults: {
averageTotalReturn: result.totalReturn,
averageReturnRate: result.returnRate,
averageTrades: result.trades.length,
averageFinalShortPrice: result.finalAvgShortPrice,
stopLossCount: result.stopLossCount,
successRate: successRate, // 添加成功率
},
allResults: result,
};
}
catch (error) {
globalLogger.error('分析失败:', error);
throw new Error('做空滚仓策略分析失败');
}
}
/**
* 格式化分析结果输出
* @param analysis 分析结果
* @returns 格式化的字符串输出
*/
function formatShortAnalysisResult(analysis) {
const { simulationParams, averageResults } = analysis;
let output = `# 做空滚仓策略预期收益分析报告\n\n`;
output += `## 模拟参数\n`;
output += `- 初始价格: ${simulationParams.initialPrice.toFixed(2)}元\n`;
output += `- 当前价格: ${simulationParams.currentPrice.toFixed(2)}元\n`;
output += `- 止损设置: ${(simulationParams.stopLossPercent * 100).toFixed(2)}%\n`;
if (simulationParams.volatility) {
output += `- 波动率: ${(simulationParams.volatility * 100).toFixed(2)}%\n`;
}
output += `## 平均预期结果\n`;
output += `- 平均总收益: ${averageResults.averageTotalReturn.toFixed(2)}元\n`;
output += `- 平均收益率: ${averageResults.averageReturnRate.toFixed(2)}%\n`;
output += `- 止损触发次数: ${averageResults.stopLossCount}\n`;
if (averageResults.successRate !== undefined) {
output += `- 交易成功率: ${averageResults.successRate.toFixed(2)}%\n`;
}
if (averageResults.averageFinalShortPrice) {
output += `- 平均做空价格: ${averageResults.averageFinalShortPrice.toFixed(2)}元\n`;
if (averageResults.averageFinalShortPrice > simulationParams.initialPrice) {
const priceImprovement = (averageResults.averageFinalShortPrice / simulationParams.initialPrice -
1) *
100;
output += `- 策略平均可提高做空价格${priceImprovement.toFixed(2)}%,增强了盈利潜力。\n`;
}
}
else {
output += `- 最终状态: 已全部平仓\n`;
}
// 修复: 仅输出摘要而不是完整交易记录
output += `- 总交易次数: ${averageResults.averageTrades}\n`;
return output;
}
/**
* 运行做空滚仓策略的期待收益分析
* @param params 分析参数
* @returns 格式化的分析结果
*/
async function runShortExpectedReturnsAnalysis(params) {
try {
const analysis = await analyzeShortExpectedReturns(params);
return formatShortAnalysisResult(analysis);
}
catch (error) {
globalLogger.error('运行分析失败:', error);
return `分析失败: ${error.message}`;
}
}
// 比较不同波动率下的期望收益
async function compareShortVolatilityScenarios(baseParams) {
let output = `# 不同波动率下的做空滚仓策略比较\n\n`;
const volatilities = [0.01, 0.02, 0.03, 0.05, 0.08];
const results = [];
try {
for (const vol of volatilities) {
// 修复: 正确传递波动率参数
const params = { ...baseParams, volatility: vol };
const analysis = await analyzeShortExpectedReturns(params);
results.push({
volatility: vol,
...analysis.averageResults,
});
}
output += `## 比较结果\n\n`;
output += `| 波动率 | 平均收益率 | 平均交易次数 | 成功率 | 平均做空价格 | 止损触发次数 |\n`;
output += `| ------ | ---------- | ------------ | ------ | ------------ | ------------ |\n`;
for (const result of results) {
output += `| ${(result.volatility * 100).toFixed(1)}% | ${result.averageReturnRate.toFixed(2)}% | ${result.averageTrades} | ${result.successRate ? result.successRate.toFixed(2) : 'N/A'}% | ${result.averageFinalShortPrice ? result.averageFinalShortPrice.toFixed(2) : 'N/A'} | ${result.stopLossCount} |\n`;
}
output += `\n## 结论\n\n`;
// 找出最佳波动率
const bestVol = results.reduce((best, current) => current.averageReturnRate > best.averageReturnRate ? current : best, results[0]);
output += `- 在测试的波动率范围内,${(bestVol.volatility * 100).toFixed(1)}%的波动率表现最佳,预期收益率为${bestVol.averageReturnRate.toFixed(2)}%。\n`;
// 检查止损触发情况
const totalStopLossCount = results.reduce((sum, r) => sum + (r.stopLossCount || 0), 0);
if (totalStopLossCount > 0) {
output += `- 在所有波动率场景中共触发了${totalStopLossCount}次止损机制,有效控制了风险。\n`;
}
// 比较交易频率
const highVolTrades = results[results.length - 1].averageTrades;
const lowVolTrades = results[0].averageTrades;
if (highVolTrades && lowVolTrades) {
output += `- 高波动率(${(volatilities[volatilities.length - 1] * 100).toFixed(1)}%)股票的平均交易次数(${highVolTrades})是低波动率(${(volatilities[0] * 100).toFixed(1)}%)股票(${lowVolTrades})的${(highVolTrades / lowVolTrades).toFixed(1)}倍。\n`;
}
// 比较成功率
if (results.some(r => r.successRate)) {
const successRates = results.map(r => r.successRate || 0);
const maxSuccessRate = Math.max(...successRates);
const optimalVolIndex = successRates.indexOf(maxSuccessRate);
output += `- 最高成功率${maxSuccessRate.toFixed(2)}%出现在波动率为${(volatilities[optimalVolIndex] * 100).toFixed(1)}%的情况下。\n`;
}
return output;
}
catch (error) {
globalLogger.error('比较不同波动率场景失败:', error);
return `比较失败: ${error.message}`;
}
}
// 使用示例
const sampleShortParams = {
symbol: 'TSLA',
initialShortShares: 1000,
feePerTrade: 5,
upPercent: 0.05, // 价格上涨10%时做空
downPercent: 0.1, // 价格下跌15%时平仓
tradingRatio: 0.3,
stopLossPercent: 0.2, // 20%止损线,价格上涨超过20%时强制平仓
};
const result = await runShortExpectedReturnsAnalysis(sampleShortParams);
console.log(result);
// 导出函数
export { simulatePricePath, calculateShortStrategyReturns, analyzeShortExpectedReturns, runShortExpectedReturnsAnalysis, compareShortVolatilityScenarios, };