File size: 6,301 Bytes
d9f5c15 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
/**
* Advanced Configuration Module
*
* This module provides advanced configuration options for the portfolio optimization
* quickstart, including presets and configurable solver parameters.
*
* NOTE: This is an optional enhancement. The core quickstart works without this module.
* To use it, include this script after app.js and call initAdvancedConfig().
*/
// =============================================================================
// Configuration Presets
// =============================================================================
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."
}
};
// Current configuration state
let currentConfig = { ...PRESETS.balanced };
// =============================================================================
// Initialization
// =============================================================================
/**
* Initialize advanced configuration UI and event handlers.
* Call this after the DOM is ready.
*/
function initAdvancedConfig() {
// Preset selector
$("#presetSelector").change(function() {
const preset = $(this).val();
if (preset !== "custom" && PRESETS[preset]) {
// Use Object.assign to mutate in place - preserves window.currentConfig reference
Object.assign(currentConfig, PRESETS[preset]);
updateConfigSliders();
updatePresetDescription(preset);
applyConfigToLoadedPlan();
}
});
// Target stocks slider
$("#targetStocksSlider").on("input", function() {
currentConfig.targetStocks = parseInt(this.value);
$("#targetStocksValue").text(this.value);
markAsCustom();
applyConfigToLoadedPlan();
});
// Max sector slider
$("#maxSectorSlider").on("input", function() {
currentConfig.maxSector = parseInt(this.value);
$("#maxSectorValue").text(this.value + "%");
markAsCustom();
applyConfigToLoadedPlan();
});
// Solver time slider
$("#solverTimeSlider").on("input", function() {
currentConfig.solverTime = parseInt(this.value);
$("#solverTimeValue").text(formatSolverTime(this.value));
markAsCustom();
applyConfigToLoadedPlan();
});
}
// =============================================================================
// Configuration UI Updates
// =============================================================================
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() {
// Check if current settings match any preset
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;
}
}
// No match - mark as custom
$("#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);
}
// =============================================================================
// Apply Configuration to Plan
// =============================================================================
/**
* Apply current configuration to the loaded plan.
* This updates the plan object that will be sent to the solver.
*/
function applyConfigToLoadedPlan() {
// loadedPlan is defined in app.js
if (typeof loadedPlan !== 'undefined' && loadedPlan) {
loadedPlan.targetPositionCount = currentConfig.targetStocks;
loadedPlan.maxSectorPercentage = currentConfig.maxSector / 100;
loadedPlan.solverConfig = {
terminationSeconds: currentConfig.solverTime
};
}
}
/**
* Get the current configuration.
* @returns {Object} Current configuration with targetStocks, maxSector, solverTime
*/
function getCurrentConfig() {
return { ...currentConfig };
}
/**
* Get target stock count from current configuration.
* Used by app.js for KPI display.
*/
function getTargetStockCount() {
return currentConfig.targetStocks;
}
// Export for use in app.js
window.PRESETS = PRESETS;
window.currentConfig = currentConfig;
window.initAdvancedConfig = initAdvancedConfig;
window.applyConfigToLoadedPlan = applyConfigToLoadedPlan;
window.getCurrentConfig = getCurrentConfig;
window.getTargetStockCount = getTargetStockCount;
|