配置项如下
// -10% ~ 10% 随机数
const rnd = () => 0.2 * Math.random() - 0.1;
// 随机漫步
const randomWalk = (count) => {
const ps = [1];
for (let i = 1; i < count; i++) {
ps[i] = Number((ps[i - 1] * (1 + rnd())).toFixed(2));
}
return ps;
};
const prices = randomWalk(11);
// 交易次数不限制
const profit = (prices, kmax) => {
console.log('start======================>');
if (!prices || !prices.length) {
return [0, 0];
}
const dp0 = [];
const dp1 = [];
for (let i = 0; i < prices.length; i++) {
const p = prices[i];
dp0[i] = [];
dp1[i] = [];
for (let k = kmax; k >= 1; k--) {
if (i === 0) {
dp0[i][k] = 0;
dp1[i][k] = -p;
continue;
}
if (dp1[i -1][k] + p > dp0[i-1][k]) {
console.log('sell======>',i, k, p);
dp0[i][k] = dp1[i -1][k] + p;
} else {
dp0[i][k] = dp0[i-1][k];
}
if (dp0[i-1][k-1] - p > dp1[i-1][k]) {
console.log('buy======>', i, k, p);
dp1[i][k] = dp0[i-1][k-1] - p;
} else {
dp1[i][k] = dp1[i-1][k];
}
}
}
console.log('vvv', dp0[prices.length - 1][kmax]);
return [
[],[]
];
};
const [bIdxs, sIdxs] = profit(prices, 2);
// 波峰波谷
const findValleyPeaks = (prices) => {
if (!prices.length) {
return [];
}
const tradeList = [];
const buy = (index) => {
tradeList.push({
buyIndex: index,
buyPrice: prices[index],
sellIndex: -1,
sellPrice: -Infinity,
});
};
const sell = (index) => {
const trade = tradeList[tradeList.length - 1];
if (trade) {
trade.sellIndex = index;
trade.sellPrice = prices[index];
}
};
let prev = prices[0];
// 0-> 初始趋势不明 -1 -> 熊市 1 -> 牛市
let trend = 0;
for (let i = 0; i < prices.length; i++) {
const p = prices[i];
const delta = p - prev;
if (trend <= 0 && delta > 0) {
buy(i - 1);
trend = 1;
}
if (trend >= 0 && delta < 0) {
sell(i - 1);
trend = -1;
}
prev = p;
}
// 最后一天卖出
const lastTrade = tradeList[tradeList.length - 1];
if (lastTrade.sellIndex === -1) {
lastTrade.sellIndex === prices.length - 1;
lastTrade.sellPrice === prices[prices.length - 1];
}
return tradeList;
};
const mergeTrade = (trade1, trade2) => {
if (trade2.buyPrice > trade1.buyPrice && trade2.sellPrice > trade1.sellPrice) {
return {
buyIndex: trade1.buyIndex,
buyPrice: trade1.buyPrice,
sellIndex: trade2.sellIndex,
sellPrice: trade2.sellPrice,
};
} else if ((trade1.sellPrice - trade1.buyPrice) > (trade2.sellPrice - trade2.buyPrice)) {
return {
buyIndex: trade1.buyIndex,
buyPrice: trade1.buyPrice,
sellIndex: trade1.sellIndex,
sellPrice: trade1.sellPrice,
};
} else {
return {
buyIndex: trade2.buyIndex,
buyPrice: trade2.buyPrice,
sellIndex: trade2.sellIndex,
sellPrice: trade2.sellPrice,
};
}
};
const profit2 = (prices, kmax) => {
const tradeList = findValleyPeaks(prices);
if (kmax >= tradeList.length) {
return tradeList;
}
};
const data = prices.map((v, index) => [index, v]);
const markPoints = [];
bIdxs.forEach(v => {
markPoints.push({
name : '买点',
coord: [v, data[v][1]],
symbolOffset: [0, 10],
itemStyle: {
color:'red',
},
});
});
sIdxs.forEach(v => {
markPoints.push({
name : '卖点',
coord: [v, data[v][1]],
symbolOffset: [0, -10],
itemStyle: {
color:'yellowgreen',
},
});
});
option = {
backgroundColor: '#080b30',
title: {
text: 'quan-infinity',
textStyle: {
color: '#fff',
},
},
tooltip: {
trigger: 'axis',
},
grid: {
left: 64,
right: 16,
bottom: 80,
},
toolbox: {
feature: {
dataZoom: {
yAxisIndex: 'none'
},
restore: {},
saveAsImage: {}
}
},
xAxis: {
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,0.3)'
}
},
axisTick: {
show: false,
},
splitLine: {
show: false,
},
},
yAxis: {
axisLine: {
show: false,
},
axisTick: {
show: false,
},
axisLabel: {
color: 'rgba(255,255,255,0.3)',
},
splitLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,0.3)'
}
},
},
dataZoom: [
{
type: 'inside',
start: 0,
end: 1000
},
{
start: 0,
end: 1000,
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
}
}
],
series: [{
type: 'line',
lineStyle: {
color: "#6c50f3",
},
markPoint : {
symbol: 'circle',
symbolSize: 10,
data : markPoints,
},
data,
}],
};