|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const PRESETS = {
|
|
|
conservative: {
|
|
|
targetStocks: 30,
|
|
|
maxSector: 15,
|
|
|
solverTime: 60,
|
|
|
description: "Max diversification: 30 stocks, tight sector limits, longer solve time."
|
|
|
},
|
|
|
balanced: {
|
|
|
targetStocks: 20,
|
|
|
maxSector: 25,
|
|
|
solverTime: 30,
|
|
|
description: "Balanced settings for typical portfolio optimization."
|
|
|
},
|
|
|
aggressive: {
|
|
|
targetStocks: 10,
|
|
|
maxSector: 40,
|
|
|
solverTime: 30,
|
|
|
description: "Concentrated bets: fewer stocks, looser sector limits."
|
|
|
},
|
|
|
quick: {
|
|
|
targetStocks: 20,
|
|
|
maxSector: 25,
|
|
|
solverTime: 10,
|
|
|
description: "Fast iteration: same as balanced but 10s solve time."
|
|
|
}
|
|
|
};
|
|
|
|
|
|
|
|
|
let currentConfig = { ...PRESETS.balanced };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function initAdvancedConfig() {
|
|
|
|
|
|
$("#presetSelector").change(function() {
|
|
|
const preset = $(this).val();
|
|
|
if (preset !== "custom" && PRESETS[preset]) {
|
|
|
|
|
|
Object.assign(currentConfig, PRESETS[preset]);
|
|
|
updateConfigSliders();
|
|
|
updatePresetDescription(preset);
|
|
|
applyConfigToLoadedPlan();
|
|
|
}
|
|
|
});
|
|
|
|
|
|
|
|
|
$("#targetStocksSlider").on("input", function() {
|
|
|
currentConfig.targetStocks = parseInt(this.value);
|
|
|
$("#targetStocksValue").text(this.value);
|
|
|
markAsCustom();
|
|
|
applyConfigToLoadedPlan();
|
|
|
});
|
|
|
|
|
|
|
|
|
$("#maxSectorSlider").on("input", function() {
|
|
|
currentConfig.maxSector = parseInt(this.value);
|
|
|
$("#maxSectorValue").text(this.value + "%");
|
|
|
markAsCustom();
|
|
|
applyConfigToLoadedPlan();
|
|
|
});
|
|
|
|
|
|
|
|
|
$("#solverTimeSlider").on("input", function() {
|
|
|
currentConfig.solverTime = parseInt(this.value);
|
|
|
$("#solverTimeValue").text(formatSolverTime(this.value));
|
|
|
markAsCustom();
|
|
|
applyConfigToLoadedPlan();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function updateConfigSliders() {
|
|
|
$("#targetStocksSlider").val(currentConfig.targetStocks);
|
|
|
$("#targetStocksValue").text(currentConfig.targetStocks);
|
|
|
|
|
|
$("#maxSectorSlider").val(currentConfig.maxSector);
|
|
|
$("#maxSectorValue").text(currentConfig.maxSector + "%");
|
|
|
|
|
|
$("#solverTimeSlider").val(currentConfig.solverTime);
|
|
|
$("#solverTimeValue").text(formatSolverTime(currentConfig.solverTime));
|
|
|
}
|
|
|
|
|
|
function formatSolverTime(seconds) {
|
|
|
if (seconds >= 60) {
|
|
|
const mins = Math.floor(seconds / 60);
|
|
|
const secs = seconds % 60;
|
|
|
return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
|
|
|
}
|
|
|
return `${seconds}s`;
|
|
|
}
|
|
|
|
|
|
function markAsCustom() {
|
|
|
|
|
|
for (const [name, preset] of Object.entries(PRESETS)) {
|
|
|
if (preset.targetStocks === currentConfig.targetStocks &&
|
|
|
preset.maxSector === currentConfig.maxSector &&
|
|
|
preset.solverTime === currentConfig.solverTime) {
|
|
|
$("#presetSelector").val(name);
|
|
|
updatePresetDescription(name);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
$("#presetSelector").val("custom");
|
|
|
updatePresetDescription("custom");
|
|
|
}
|
|
|
|
|
|
function updatePresetDescription(preset) {
|
|
|
const descriptions = {
|
|
|
conservative: PRESETS.conservative.description,
|
|
|
balanced: PRESETS.balanced.description,
|
|
|
aggressive: PRESETS.aggressive.description,
|
|
|
quick: PRESETS.quick.description,
|
|
|
custom: "Custom configuration. Adjust sliders to your needs."
|
|
|
};
|
|
|
$("#presetDescription").text(descriptions[preset] || descriptions.custom);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function applyConfigToLoadedPlan() {
|
|
|
|
|
|
if (typeof loadedPlan !== 'undefined' && loadedPlan) {
|
|
|
loadedPlan.targetPositionCount = currentConfig.targetStocks;
|
|
|
loadedPlan.maxSectorPercentage = currentConfig.maxSector / 100;
|
|
|
loadedPlan.solverConfig = {
|
|
|
terminationSeconds: currentConfig.solverTime
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getCurrentConfig() {
|
|
|
return { ...currentConfig };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getTargetStockCount() {
|
|
|
return currentConfig.targetStocks;
|
|
|
}
|
|
|
|
|
|
|
|
|
window.PRESETS = PRESETS;
|
|
|
window.currentConfig = currentConfig;
|
|
|
window.initAdvancedConfig = initAdvancedConfig;
|
|
|
window.applyConfigToLoadedPlan = applyConfigToLoadedPlan;
|
|
|
window.getCurrentConfig = getCurrentConfig;
|
|
|
window.getTargetStockCount = getTargetStockCount;
|
|
|
|