diff --git a/pages/config/config.vue b/pages/config/config.vue index 7225e4c..9e6bfc1 100644 --- a/pages/config/config.vue +++ b/pages/config/config.vue @@ -55,6 +55,7 @@ class="mini-input" v-model="item.name" placeholder="如 TMF" + :disabled="selectedStrategy?.type === 'risk_parity'" @input="(e) => searchStock(e.detail.value, index)" @focus="() => { activeSearchIndex.value = -1; searchResults.value = []; }" /> @@ -235,6 +236,41 @@ const submitForm = async () => { if (strategyIndex.value === -1) return uni.showToast({ title: '请选择策略', icon: 'none' }); const selected = strategies.value[strategyIndex.value]; + + // 风险平价策略权重校验 + if (selected.type === 'risk_parity' && selected.parameters?.assets) { + let totalWeight = 0; + const targetAssets = selected.parameters.assets; + + for (let i = 0; i < form.value.stocks.length; i++) { + const stock = form.value.stocks[i]; + const target = targetAssets.find(a => a.symbol === stock.name); + if (!target) continue; + + // 计算实际持仓市值 = 价格 * 数量 + const marketValue = (parseFloat(stock.price) || 0) * (parseFloat(stock.amount) || 0); + totalWeight += marketValue; + } + + // 校验每个标的的实际权重和目标权重偏差不超过5% + for (let i = 0; i < form.value.stocks.length; i++) { + const stock = form.value.stocks[i]; + const target = targetAssets.find(a => a.symbol === stock.name); + if (!target) continue; + + const marketValue = (parseFloat(stock.price) || 0) * (parseFloat(stock.amount) || 0); + const actualWeight = totalWeight > 0 ? marketValue / totalWeight : 0; + const deviation = Math.abs(actualWeight - target.targetWeight); + + if (deviation > 0.05) { + return uni.showToast({ + title: `${stock.name} 权重偏差超过5%,目标${(target.targetWeight*100).toFixed(0)}%,实际${(actualWeight*100).toFixed(0)}%`, + icon: 'none', + duration: 3000 + }); + } + } + } const requestData = { name: form.value.name,