cnhkmcp 2.3.4__py3-none-any.whl → 2.3.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. cnhkmcp/__init__.py +1 -1
  2. cnhkmcp/untracked/AI/321/206/320/231/320/243/321/205/342/225/226/320/265/321/204/342/225/221/342/225/221/BRAIN_AI/321/206/320/231/320/243/321/205/342/225/226/320/265/321/204/342/225/221/342/225/221Mac_Linux/321/207/320/231/320/230/321/206/320/254/320/274.zip +0 -0
  3. cnhkmcp/untracked/AI/321/206/320/231/320/243/321/205/342/225/226/320/265/321/204/342/225/221/342/225/221//321/205/320/237/320/234/321/205/320/227/342/225/227/321/205/320/276/320/231/321/210/320/263/320/225AI/321/206/320/231/320/243/321/205/342/225/226/320/265/321/204/342/225/221/342/225/221_Windows/321/207/320/231/320/230/321/206/320/254/320/274.exe +0 -0
  4. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242/main.py +6 -3
  5. cnhkmcp/untracked/AI/321/206/320/261/320/234/321/211/320/255/320/262/321/206/320/237/320/242/321/204/342/225/227/342/225/242//321/211/320/266/320/246/321/206/320/274/320/261/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +5 -3
  6. cnhkmcp/untracked/APP/Tranformer/validator.py +149 -32
  7. cnhkmcp/untracked/APP/ace_lib.py +5 -3
  8. cnhkmcp/untracked/APP/blueprints/parsetab.py +60 -0
  9. cnhkmcp/untracked/APP/blueprints/validator.py +1080 -0
  10. cnhkmcp/untracked/APP/requirements.txt +0 -0
  11. cnhkmcp/untracked/APP/static/decoder.js +164 -38
  12. cnhkmcp/untracked/APP/static/simulator.js +15 -15
  13. cnhkmcp/untracked/APP/templates/feature_engineering.html +78 -78
  14. cnhkmcp/untracked/APP/templates/idea_house.html +49 -49
  15. cnhkmcp/untracked/APP/templates/index.html +58 -58
  16. cnhkmcp/untracked/APP/templates/inspiration_house.html +64 -64
  17. cnhkmcp/untracked/APP/templates/paper_analysis.html +21 -21
  18. cnhkmcp/untracked/APP/templates/simulator.html +38 -38
  19. cnhkmcp/untracked/APP/templates/transformer_web.html +24 -24
  20. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-data-feature-engineering/SKILL.md +15 -15
  21. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/data/fundamental28_GLB_delay1/fundamental28_GLB_1_idea_1769927658009727000.json +10 -0
  22. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/data/fundamental28_GLB_delay1/fundamental28_GLB_1_idea_1769927658519220600.json +10 -0
  23. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/data/fundamental28_GLB_delay1/fundamental28_GLB_1_idea_1769927659002708800.json +10 -0
  24. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/data/fundamental28_GLB_delay1/fundamental28_GLB_1_idea_1769927659510920900.json +10 -0
  25. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/data/fundamental28_GLB_delay1/fundamental28_GLB_1_idea_1769927659982673800.json +10 -0
  26. cnhkmcp/untracked/APP/trailSomeAlphas/skills/brain-feature-implementation/scripts/validator.py +153 -32
  27. cnhkmcp/untracked/APP//321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/231/320/243/321/205/342/225/235/320/220/321/206/320/230/320/241.py +51 -3
  28. cnhkmcp/untracked/arxiv_api.py +5 -3
  29. cnhkmcp/untracked/back_up/forum_functions.py +5 -3
  30. cnhkmcp/untracked/forum_functions.py +5 -3
  31. cnhkmcp/untracked/mcp/321/206/320/246/320/227/321/204/342/225/227/342/225/242/321/210/320/276/342/225/221/321/205/320/255/320/253/321/207/320/231/320/2302_/321/205/320/266/320/222/321/206/320/256/320/254/321/205/320/236/320/257/321/207/320/231/320/230/321/205/320/240/320/277/321/205/320/232/320/270/321/204/342/225/225/320/235/321/204/342/225/221/320/226/321/206/342/225/241/320/237/321/210/320/267/320/230/321/205/320/251/320/270/321/205/342/226/221/342/226/222/321/210/320/277/320/245/321/210/342/224/220/320/251/321/204/342/225/225/320/272/forum_functions.py +5 -3
  32. cnhkmcp/untracked/platform_functions.py +5 -3
  33. cnhkmcp/untracked/skills/alpha-expression-verifier/scripts/validator.py +149 -32
  34. cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/SKILL.md +8 -2
  35. cnhkmcp/untracked/skills/brain-inspectRawTemplate-create-Setting/ace.log +0 -0
  36. cnhkmcp/untracked/skills/brain-inspectRawTemplate-create-Setting/idea_context.json +15 -0
  37. cnhkmcp/untracked/skills/brain-inspectRawTemplate-create-Setting/scripts/__init__.py +0 -0
  38. cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/parse_idea_file.py +33 -1
  39. cnhkmcp/untracked/skills/brain-inspectRawTemplate-create-Setting/scripts/parsetab.py +60 -0
  40. cnhkmcp/untracked/skills/brain-inspectRawTemplate-create-Setting/scripts/validator.py +1086 -0
  41. cnhkmcp/untracked//321/211/320/225/320/235/321/207/342/225/234/320/276/321/205/320/231/320/235/321/210/342/224/220/320/240/321/210/320/261/320/234/321/206/320/230/320/241_/321/205/320/276/320/231/321/210/320/263/320/225/321/205/342/224/220/320/225/321/210/320/266/320/221/321/204/342/225/233/320/255/321/210/342/225/241/320/246/321/205/320/234/320/225.py +5 -3
  42. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/METADATA +1 -1
  43. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/RECORD +60 -48
  44. /cnhkmcp/untracked/{skills/brain-inspectTemplate-create-Setting → APP/hkSimulator}/ace.log +0 -0
  45. /cnhkmcp/untracked/{skills/brain-inspectTemplate-create-Setting/scripts/__init__.py → APP/hkSimulator/autosim_20260201_172428.log} +0 -0
  46. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/.gitignore +0 -0
  47. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/ace_lib.py +0 -0
  48. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/config.json +0 -0
  49. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/fundamental28_GLB_1_idea_1769874845978315000.json +0 -0
  50. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/helpful_functions.py +0 -0
  51. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/build_alpha_list.py +0 -0
  52. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/fetch_sim_options.py +0 -0
  53. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/load_credentials.py +0 -0
  54. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/process_template.py +0 -0
  55. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/scripts/resolve_settings.py +0 -0
  56. /cnhkmcp/untracked/skills/{brain-inspectTemplate-create-Setting → brain-inspectRawTemplate-create-Setting}/sim_options_snapshot.json +0 -0
  57. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/WHEEL +0 -0
  58. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/entry_points.txt +0 -0
  59. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/licenses/LICENSE +0 -0
  60. {cnhkmcp-2.3.4.dist-info → cnhkmcp-2.3.6.dist-info}/top_level.txt +0 -0
Binary file
@@ -91,18 +91,116 @@ function decodeTemplates() {
91
91
  return decodedExpression;
92
92
  });
93
93
 
94
- // Store all expressions globally
95
- allDecodedExpressions = decodedExpressions;
96
-
97
- // Display results (limit to MAX_DISPLAY_RESULTS)
98
- displayDecodedResults(decodedExpressions.slice(0, MAX_DISPLAY_RESULTS), decodedExpressions.length);
99
-
100
- // Clear errors and show success
101
- errorsDiv.innerHTML = `<div class="success-message">
102
- ✓ Successfully decoded ${decodedExpressions.length} expressions using full list approach
103
- ${decodedExpressions.length > MAX_DISPLAY_RESULTS ?
104
- `<br>⚠️ Showing first ${MAX_DISPLAY_RESULTS} results. Use search to find specific expressions.` : ''}
105
- </div>`;
94
+ // Validate and display
95
+ validateAndDisplay(decodedExpressions, decodedExpressions.length, false);
96
+ }
97
+
98
+ // Validate and display expressions using backend validator
99
+ async function validateAndDisplay(expressions, totalCount = null, isRandom = false) {
100
+ const errorsDiv = document.getElementById('grammarErrors');
101
+ errorsDiv.innerHTML = '<div class="info-message">Validating expressions...</div>';
102
+
103
+ // Batch processing to prevent timeout
104
+ const BATCH_SIZE = 1000;
105
+ const totalExpressions = expressions.length;
106
+ let allResults = [];
107
+ let validCount = 0;
108
+ let invalidCount = 0;
109
+
110
+ try {
111
+ for (let i = 0; i < totalExpressions; i += BATCH_SIZE) {
112
+ const batch = expressions.slice(i, i + BATCH_SIZE);
113
+
114
+ // Update progress
115
+ errorsDiv.innerHTML = `<div class="info-message">Validating expressions... (${Math.min(i + BATCH_SIZE, totalExpressions)}/${totalExpressions})</div>`;
116
+
117
+ const response = await fetch('/api/validate_expressions', {
118
+ method: 'POST',
119
+ headers: {
120
+ 'Content-Type': 'application/json'
121
+ },
122
+ body: JSON.stringify({ expressions: batch })
123
+ });
124
+
125
+ if (!response.ok) {
126
+ throw new Error(`HTTP error! status: ${response.status}`);
127
+ }
128
+
129
+ const data = await response.json();
130
+
131
+ if (data.error) {
132
+ throw new Error(data.error);
133
+ }
134
+
135
+ // Aggregate results
136
+ if (data.results) {
137
+ allResults = allResults.concat(data.results);
138
+ if (data.summary) {
139
+ validCount += data.summary.valid;
140
+ invalidCount += data.summary.invalid;
141
+ }
142
+ }
143
+ }
144
+
145
+ // Final processing after all batches
146
+ const results = allResults;
147
+ const validExpressions = results.filter(r => r.valid).map(r => r.expression);
148
+ const invalidResults = results.filter(r => !r.valid);
149
+
150
+ // Store displayed expressions globally (valid ones)
151
+ allDecodedExpressions = validExpressions;
152
+
153
+ // Prepare validation stats
154
+ let validationStats = {
155
+ validCount: validCount,
156
+ invalidCount: invalidCount,
157
+ totalCount: totalExpressions,
158
+ errorCounts: null
159
+ };
160
+
161
+ let sortedErrors = [];
162
+ if (invalidCount > 0) {
163
+ // Group errors
164
+ const errorCounts = {};
165
+ invalidResults.forEach(r => {
166
+ const err = r.errors ? r.errors.join('; ') : 'Unknown error';
167
+ errorCounts[err] = (errorCounts[err] || 0) + 1;
168
+ });
169
+ validationStats.errorCounts = errorCounts;
170
+
171
+ // Sort by count for local display
172
+ sortedErrors = Object.entries(errorCounts).sort((a, b) => b[1] - a[1]);
173
+ }
174
+
175
+ // Display results with validation stats
176
+ displayDecodedResults(validExpressions.slice(0, MAX_DISPLAY_RESULTS), totalCount || validExpressions.length, isRandom, validationStats);
177
+
178
+ // Show summary in errorsDiv (for Editor page)
179
+ let summaryHtml = `
180
+ <div class="${validExpressions.length > 0 ? 'success-message' : 'error-item'}">
181
+ <strong>Validation Complete:</strong><br>
182
+ Valid: ${validCount} / ${totalExpressions}<br>
183
+ Invalid: ${invalidCount}
184
+ `;
185
+
186
+ if (invalidCount > 0) {
187
+ summaryHtml += `<br><br><strong>Top Invalid Reasons:</strong><ul style="max-height: 200px; overflow-y: auto;">`;
188
+ sortedErrors.slice(0, 50).forEach(([err, count]) => {
189
+ summaryHtml += `<li>${err} (${count} occurrences)</li>`;
190
+ });
191
+ if (sortedErrors.length > 50) {
192
+ summaryHtml += `<li>... (showing top 50 error types)</li>`;
193
+ }
194
+ summaryHtml += `</ul>`;
195
+ }
196
+
197
+ summaryHtml += `</div>`;
198
+ errorsDiv.innerHTML = summaryHtml;
199
+
200
+ } catch (error) {
201
+ console.error(error);
202
+ errorsDiv.innerHTML = `<div class="error-item"><strong>Network Error:</strong> ${error.message}</div>`;
203
+ }
106
204
  }
107
205
 
108
206
  // Generate all combinations (Cartesian product) of template values
@@ -133,11 +231,50 @@ function generateCombinations(templateValues) {
133
231
  }
134
232
 
135
233
  // Display decoded results
136
- function displayDecodedResults(expressions, totalCount = null, isRandom = false) {
234
+ function displayDecodedResults(expressions, totalCount = null, isRandom = false, validationStats = null) {
137
235
  const resultsList = document.getElementById('resultsList');
138
236
 
139
237
  // Clear previous results
140
238
  resultsList.innerHTML = '';
239
+
240
+ // Add validation summary if available
241
+ if (validationStats) {
242
+ const validationDiv = document.createElement('div');
243
+ validationDiv.className = validationStats.invalidCount > 0 ? 'validation-summary error-item' : 'validation-summary success-message';
244
+ validationDiv.style.marginBottom = '20px';
245
+ validationDiv.style.padding = '15px';
246
+ validationDiv.style.border = validationStats.invalidCount > 0 ? '1px solid #fbd5d5' : '1px solid #def7ec';
247
+
248
+ let summaryHtml = `
249
+ <strong>Validation Summary:</strong><br>
250
+ <div style="display: flex; gap: 20px; margin-top: 5px;">
251
+ <span style="color: green">✓ Valid: ${validationStats.validCount.toLocaleString()}</span>
252
+ <span style="color: ${validationStats.invalidCount > 0 ? 'red' : 'gray'}">
253
+ ${validationStats.invalidCount > 0 ? '❌' : '✓'} Invalid: ${validationStats.invalidCount.toLocaleString()}
254
+ </span>
255
+ <span style="color: gray">Total: ${validationStats.totalCount.toLocaleString()}</span>
256
+ </div>
257
+ `;
258
+
259
+ if (validationStats.invalidCount > 0 && validationStats.errorCounts) {
260
+ summaryHtml += `<div style="margin-top: 10px; border-top: 1px solid #eee; padding-top: 10px;">
261
+ <strong>Top Invalid Reasons:</strong>
262
+ <ul style="max-height: 150px; overflow-y: auto; margin: 5px 0 0 20px; padding: 0;">`;
263
+
264
+ const sortedErrors = Object.entries(validationStats.errorCounts).sort((a, b) => b[1] - a[1]);
265
+
266
+ sortedErrors.slice(0, 10).forEach(([err, count]) => {
267
+ summaryHtml += `<li>${err} (${count} occurrences)</li>`;
268
+ });
269
+ if (sortedErrors.length > 10) {
270
+ summaryHtml += `<li>... (showing top 10 error types)</li>`;
271
+ }
272
+ summaryHtml += `</ul></div>`;
273
+ }
274
+
275
+ validationDiv.innerHTML = summaryHtml;
276
+ resultsList.appendChild(validationDiv);
277
+ }
141
278
 
142
279
  // Add search box if there are more results than displayed (only for full iteration)
143
280
  if (!isRandom && totalCount && totalCount > MAX_DISPLAY_RESULTS) {
@@ -463,32 +600,24 @@ function randomIteration() {
463
600
  });
464
601
  }
465
602
 
466
- // Store ALL selected expressions globally for download (not limited by display)
467
- allDecodedExpressions = selectedExpressions;
468
-
469
- // For display, limit to MAX_DISPLAY_RESULTS but keep full set for download
470
- const displayExpressions = selectedExpressions.slice(0, MAX_DISPLAY_RESULTS);
471
-
472
- // Display results (limited for display, but full count for download)
473
- displayDecodedResults(displayExpressions, allExpressions.length, true);
474
-
475
- // Clear errors and show success with clear indication about display vs download
476
- if (selectedExpressions.length > MAX_DISPLAY_RESULTS) {
477
- errorsDiv.innerHTML = `<div class="success-message">
478
- ✓ Randomly selected ${selectedExpressions.length} expressions from ${allExpressions.length} total combinations<br>
479
- 📺 Displaying first ${MAX_DISPLAY_RESULTS} results. Download will include all ${selectedExpressions.length} expressions.
480
- </div>`;
481
- } else {
482
- errorsDiv.innerHTML = `<div class="success-message">
483
- ✓ Randomly selected ${selectedExpressions.length} expressions from ${allExpressions.length} total combinations
484
- </div>`;
485
- }
603
+ // Validate and display
604
+ validateAndDisplay(selectedExpressions, allExpressions.length, true);
486
605
  }
487
606
 
488
607
  // Open settings modal for Next Move
489
608
  function openSettingsModal() {
490
609
  const modal = document.getElementById('settingsModal');
491
610
  modal.style.display = 'block';
611
+
612
+ // Make nanHandling and maxTrade support multi-selection (like neutralization)
613
+ // by converting them into checkbox groups.
614
+ // This enables generating iterations across ON/OFF without forcing users to type CSV
615
+ // or rely on Ctrl+click multi-select behavior.
616
+ convertInputToCheckboxGroup('nanHandling', { placeholder: '' });
617
+ setCheckboxGroupOptions('nanHandling', ['OFF', 'ON']);
618
+ convertInputToCheckboxGroup('maxTrade', { placeholder: '' });
619
+ // Preserve the UI's existing default (ON selected in HTML) by ordering ON first.
620
+ setCheckboxGroupOptions('maxTrade', ['ON', 'OFF']);
492
621
 
493
622
  // Check if we have simulation options and update UI
494
623
  if (typeof simulationOptions !== 'undefined' && Object.keys(simulationOptions).length > 0) {
@@ -1501,11 +1630,8 @@ function handleGeneratedExpressionsFileSelect(event) {
1501
1630
  return;
1502
1631
  }
1503
1632
 
1504
- // Set global variable
1505
- allDecodedExpressions = expressions;
1506
-
1507
- // Display results
1508
- displayDecodedResults(allDecodedExpressions.slice(0, MAX_DISPLAY_RESULTS), allDecodedExpressions.length);
1633
+ // Validate and display results
1634
+ validateAndDisplay(expressions);
1509
1635
 
1510
1636
  // Switch to results tab
1511
1637
  const resultsTab = document.querySelector('[data-page="results"]');
@@ -216,7 +216,7 @@ async function testConnection() {
216
216
  const password = document.getElementById('password').value;
217
217
 
218
218
  if (!username || !password) {
219
- updateStatus('Please enter username and password first', 'error');
219
+ updateStatus('请先输入用户名和密码', 'error');
220
220
  return;
221
221
  }
222
222
 
@@ -224,7 +224,7 @@ async function testConnection() {
224
224
  testBtn.disabled = true;
225
225
  testBtn.textContent = '🔄 Testing...';
226
226
 
227
- updateStatus('Testing BRAIN API connection...', 'running');
227
+ updateStatus('正在测试 BRAIN API 连接...', 'running');
228
228
 
229
229
  try {
230
230
  const response = await fetch('/api/simulator/test-connection', {
@@ -241,7 +241,7 @@ async function testConnection() {
241
241
  const data = await response.json();
242
242
 
243
243
  if (data.success) {
244
- updateStatus('✅ Connection successful! Ready to run simulation.', 'success');
244
+ updateStatus('✅ 连接成功! 准备开始回测.', 'success');
245
245
  saveSimulatorDefaults();
246
246
  } else {
247
247
  updateStatus(`❌ Connection failed: ${data.error}`, 'error');
@@ -259,7 +259,7 @@ async function testConnection() {
259
259
  */
260
260
  async function runSimulator() {
261
261
  if (isSimulationRunning) {
262
- updateStatus('Simulation is already running', 'error');
262
+ updateStatus('回测正在进行中', 'error');
263
263
  return;
264
264
  }
265
265
 
@@ -272,7 +272,7 @@ async function runSimulator() {
272
272
 
273
273
  const jsonFile = document.getElementById('jsonFile').files[0];
274
274
  if (!jsonFile) {
275
- updateStatus('Please select a JSON file', 'error');
275
+ updateStatus('请选择一个 JSON 文件', 'error');
276
276
  return;
277
277
  }
278
278
 
@@ -293,10 +293,10 @@ async function runSimulator() {
293
293
  const stopBtn = document.getElementById('stopBtn');
294
294
 
295
295
  runBtn.disabled = true;
296
- runBtn.textContent = '🔄 Running...';
296
+ runBtn.textContent = '🔄 运行中...';
297
297
  stopBtn.style.display = 'inline-block';
298
298
 
299
- updateStatus('Starting simulation...', 'running');
299
+ updateStatus('正在启动回测...', 'running');
300
300
  showProgress(true);
301
301
 
302
302
  // Create abort controller for stopping simulation
@@ -314,7 +314,7 @@ async function runSimulator() {
314
314
  const data = await response.json();
315
315
 
316
316
  if (data.success) {
317
- updateStatus('✅ Simulator launched in new terminal回测器启动成功! Check the terminal window for progress.请检查新唤起的python程序', 'success');
317
+ updateStatus('✅ 回测器已在终端启动! 请查看终端窗口了解进度。', 'success');
318
318
 
319
319
  // Show launch information
320
320
  if (data.parameters) {
@@ -329,18 +329,18 @@ async function runSimulator() {
329
329
  refreshLogFiles();
330
330
  }, 3000);
331
331
  } else {
332
- updateStatus(`❌ Failed to launch simulator: ${data.error}`, 'error');
332
+ updateStatus(`❌ 启动回测器失败: ${data.error}`, 'error');
333
333
  }
334
334
  } catch (error) {
335
335
  if (error.name === 'AbortError') {
336
- updateStatus('⏹️ Simulation stopped by user', 'idle');
336
+ updateStatus('⏹️ 回测已被用户停止', 'idle');
337
337
  } else {
338
- updateStatus(`❌ Simulation error: ${error.message}`, 'error');
338
+ updateStatus(`❌ 回测错误: ${error.message}`, 'error');
339
339
  }
340
340
  } finally {
341
341
  isSimulationRunning = false;
342
342
  runBtn.disabled = false;
343
- runBtn.textContent = '🚀 Start Simulation';
343
+ runBtn.textContent = '🚀 开始回测';
344
344
  stopBtn.style.display = 'none';
345
345
  simulationAbortController = null;
346
346
  showProgress(false);
@@ -361,7 +361,7 @@ async function stopSimulation() {
361
361
  console.error('Error stopping simulation:', error);
362
362
  }
363
363
 
364
- updateStatus('Stopping simulation...', 'idle');
364
+ updateStatus('正在停止回测...', 'idle');
365
365
  }
366
366
 
367
367
  /**
@@ -578,10 +578,10 @@ function copyAlphaIds() {
578
578
  if (window.lastSimulationResults && window.lastSimulationResults.alphaIds) {
579
579
  const alphaIds = window.lastSimulationResults.alphaIds.join('\n');
580
580
  navigator.clipboard.writeText(alphaIds).then(() => {
581
- updateStatus('✅ Alpha IDs copied to clipboard!', 'success');
581
+ updateStatus('✅ Alpha IDs 已复制到剪贴板!', 'success');
582
582
  }).catch(err => {
583
583
  console.error('Failed to copy: ', err);
584
- updateStatus('❌ Failed to copy Alpha IDs', 'error');
584
+ updateStatus('❌ 复制 Alpha IDs 失败', 'error');
585
585
  });
586
586
  }
587
587
  }