Skip to content

Commit 18f1246

Browse files
committed
refactor: 更新市场模拟模块,增强价格生成与保护逻辑
- 新增随机基准价格生成函数 `getRandomBasePrice`,替代原有的固定基准价格 - 在历史数据生成中记录价格变化百分比,提升调试信息的可用性 - 在订单簿生成中引入最小价格限制,确保价格不低于 0.01 - 优化 K 线生成逻辑,增加价格偏离保护,防止价格过度波动 - 更新状态初始化逻辑,使用随机基准价格作为默认起始价格
1 parent aae30e6 commit 18f1246

6 files changed

Lines changed: 66 additions & 16 deletions

File tree

src/workers/marketSimulation/candles/generator.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,20 @@ export function generateNormalCandle(
134134
changePercent = applyTechnicalResponse(s, open, changePercent);
135135
}
136136

137-
// 均值回归
137+
// 均值回归 - 防止价格偏离基准价格太远
138138
const deviation = (open - s.basePrice) / s.basePrice;
139139
if (Math.abs(deviation) > 0.05) {
140-
changePercent -= deviation * 0.0008 * dtScale;
140+
// 偏离超过5%时,产生回归力
141+
const reversionForce = deviation * 0.3; // 回归强度
142+
changePercent -= reversionForce * dtScale;
143+
}
144+
145+
// 价格保护:防止价格偏离基准价格超过50%
146+
const maxDeviation = 0.5; // 最大偏离50%
147+
if (Math.abs(deviation) > maxDeviation) {
148+
// 如果偏离过大,强制回归
149+
const forcedReversion = -deviation * 0.5;
150+
changePercent = forcedReversion * dtScale;
141151
}
142152

143153
// 限制单根K线的最大变化幅度

src/workers/marketSimulation/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@
33
*/
44

55
export const SYMBOL = 'BTC-USDT';
6+
7+
/**
8+
* 生成随机基准价格(锚点价格)
9+
* 范围: 100000 - 200000
10+
*/
11+
export function getRandomBasePrice(): number {
12+
return 100000 + Math.random() * (200000 - 100000);
13+
}
14+
15+
/** @deprecated 使用 getRandomBasePrice() 替代 */
616
export const BASE_PRICE = 40000.0;
17+
718
export const LEVELS = 50; // 订单簿深度
819
export const PRICE_PRECISION = 100; // 价格精度 (0.01)
920

src/workers/marketSimulation/history.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,18 @@ export function generateHistoricalCandles(
3232
candles.push(candle);
3333
}
3434

35+
// 历史数据生成后,状态已保留最后一个价格(在 generateCandleFromState 中更新)
36+
const lastPrice = state.currentPrice;
37+
const startPrice = state.basePrice;
38+
const priceChange = ((lastPrice - startPrice) / startPrice) * 100;
39+
3540
const genTime = performance.now() - startGenTime;
3641
// eslint-disable-next-line no-console
3742
console.info(
3843
`[MockWorker] 生成 ${count} 根 K 线耗时: ${genTime.toFixed(1)}ms ` +
39-
`(${((count / genTime) * 1000).toFixed(0)} 根/秒)`,
44+
`(${((count / genTime) * 1000).toFixed(0)} 根/秒)\n` +
45+
` 起始价格: $${startPrice.toFixed(2)}, 结束价格: $${lastPrice.toFixed(2)} ` +
46+
`(变化: ${priceChange > 0 ? '+' : ''}${priceChange.toFixed(2)}%)`,
4047
);
4148

4249
return candles;

src/workers/marketSimulation/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type {
1515
} from './types';
1616

1717
// 常量导出
18-
export { SYMBOL, BASE_PRICE, LEVELS, PRICE_PRECISION } from './constants';
18+
export { SYMBOL, BASE_PRICE, getRandomBasePrice, LEVELS, PRICE_PRECISION } from './constants';
1919

2020
// 状态管理
2121
export {

src/workers/marketSimulation/orderbook.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { SYMBOL, LEVELS } from './constants';
88
import { round2, fatTailRandom } from './utils';
99
import { getNextPhase, getPhaseDuration } from './wyckoff';
1010

11+
/** 最小价格限制,防止负数或过小的价格 */
12+
const MIN_PRICE = 0.01;
13+
1114
/**
1215
* 生成订单簿数据
1316
*/
@@ -38,6 +41,11 @@ export function generateOrderBook(): OrderBook {
3841
const random = fatTailRandom() * baseVol;
3942
const changePercent = drift + random + state.momentum * 0.0001;
4043
state.currentPrice = prevPrice * (1 + changePercent);
44+
45+
// 价格保护:确保价格不会变成负数或过小
46+
if (state.currentPrice < MIN_PRICE) {
47+
state.currentPrice = Math.max(MIN_PRICE, prevPrice * 0.99); // 最多下跌 1%
48+
}
4149

4250
// 更新动量
4351
state.momentum = state.momentum * 0.98 + changePercent * 50;
@@ -56,20 +64,32 @@ export function generateOrderBook(): OrderBook {
5664
const bids: [number, number][] = [];
5765
const asks: [number, number][] = [];
5866

59-
let bidSpread = 0;
60-
let askSpread = 0;
67+
// 确保当前价格不低于最小价格
68+
const currentPrice = Math.max(state.currentPrice, MIN_PRICE);
69+
70+
// 使用百分比 spread,适应不同价格水平
71+
// 每层 spread: 0.05% - 0.3%,累加后最多约 15% 的深度
72+
let bidSpreadPercent = 0;
73+
let askSpreadPercent = 0;
6174

6275
for (let i = 0; i < LEVELS; i++) {
6376
const depthMultiplier = 1 + i * 0.15;
6477
const baseQty = Math.floor(Math.random() * 99) + 1;
6578

66-
bidSpread += 0.01 + Math.random() * 0.49;
67-
const bidPrice = state.currentPrice - bidSpread;
68-
bids.push([round2(bidPrice), Math.round(baseQty * depthMultiplier)]);
69-
70-
askSpread += 0.01 + Math.random() * 0.49;
71-
const askPrice = state.currentPrice + askSpread;
72-
asks.push([round2(askPrice), Math.round(baseQty * depthMultiplier)]);
79+
// 百分比 spread,每层增加 0.05% - 0.3%
80+
bidSpreadPercent += (0.0005 + Math.random() * 0.0025);
81+
const bidPrice = currentPrice * (1 - bidSpreadPercent);
82+
83+
// 确保买单价格不低于最小价格,且低于当前价格
84+
const safeBidPrice = Math.max(MIN_PRICE, Math.min(bidPrice, currentPrice * 0.999));
85+
bids.push([round2(safeBidPrice), Math.round(baseQty * depthMultiplier)]);
86+
87+
askSpreadPercent += (0.0005 + Math.random() * 0.0025);
88+
const askPrice = currentPrice * (1 + askSpreadPercent);
89+
90+
// 确保卖单价格高于当前价格
91+
const safeAskPrice = Math.max(askPrice, currentPrice * 1.001);
92+
asks.push([round2(safeAskPrice), Math.round(baseQty * depthMultiplier)]);
7393
}
7494

7595
bids.sort((a, b) => b[0] - a[0]);
@@ -78,7 +98,7 @@ export function generateOrderBook(): OrderBook {
7898
return {
7999
symbol: SYMBOL,
80100
timestamp: Date.now(),
81-
price: round2(state.currentPrice),
101+
price: round2(currentPrice),
82102
bids,
83103
asks,
84104
};

src/workers/marketSimulation/state.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import type { MarketState } from './types';
6-
import { BASE_PRICE } from './constants';
6+
import { getRandomBasePrice } from './constants';
77

88
/** 全局状态实例 */
99
let state: MarketState | null = null;
@@ -33,7 +33,9 @@ export function getStateOrNull(): MarketState | null {
3333
* 初始化市场状态
3434
*/
3535
export function initializeState(startPrice?: number): MarketState {
36-
const price = startPrice && startPrice > 0 ? startPrice : BASE_PRICE;
36+
// 如果提供了有效的起始价格,使用它;否则使用随机基准价格
37+
const price =
38+
startPrice && startPrice > 0 ? startPrice : getRandomBasePrice();
3739

3840
state = {
3941
phase: 'ACCUMULATION',

0 commit comments

Comments
 (0)