Optimal (optimized) simple rules ================================ An *optimal simple rule* (OSR) keeps the model as written -- including a simple, parametric feedback rule such as a Taylor rule -- and chooses the **rule coefficients** that minimise a policy loss function. Unlike fully optimal policy (see :doc:`../OptimalPolicy/Optimal Policy`), the model still has as many equations as endogenous variables: no equation is dropped, only some of its parameters are picked optimally. The loss function ----------------- The loss is supplied to ``optimal_simple_rule`` at solve time, as a string in terms of the model's variables and parameters (it is the *planner's objective*, i.e. RISE maximises it -- write a welfare measure, or minus a quadratic loss). A discount factor can be attached; it does not affect the ranking of *unconditional* losses, so it is often left at the default ``0.99``:: % minus a weighted quadratic loss in inflation, the output gap and the % change in the policy rate Loss = '-.5*(1*PAI^2 + .3*Y^2 + 0.9*DI^2)'; % with an explicit discount factor: Loss = {0.99, '-.5*(1*PAI^2 + .3*Y^2 + 0.9*DI^2)'}; Equivalently, the objective can be declared inside the model file as ``planner_objective{discount = 0.99} -.5*(1*PAI^2 + .3*Y^2 + 0.9*DI^2);``. The rule coefficients to optimise are listed the way estimated parameters are -- through a ``priors`` structure giving each one a support (start value, lower bound, upper bound):: priors = struct(); priors.gam_lag = {0.60, 0.00, 1}; % {start, lb, ub} priors.gam_y = {0.50, 0.00, 10}; (Approximate) analytical loss ----------------------------- If the third argument is empty, ``optimal_simple_rule`` minimises the **theoretical (unconditional)** loss -- computed from the model's (perturbation-implied) second moments, with no simulation:: m = rise('Canonical_osr'); m = set(m, 'parameters', p); % p: a name/value list mest = optimal_simple_rule(m, Loss, [], 'priors', priors); % inspect the optimised rule coefficients print_estimation_results(mest) Simulated loss -------------- Passing a shocks database (or a struct / function handle producing one) as the third argument makes ``optimal_simple_rule`` minimise the **conditional** (simulated) loss along those shock paths instead -- useful for nonlinear models or when you care about a specific scenario:: shocks_db = ... ; % e.g. a ts of shock draws mest = optimal_simple_rule(m, Loss, shocks_db, 'priors', priors); You can evaluate the loss of any parameterisation directly with ``calculate_loss(m, Loss)`` (unconditional) or ``calculate_loss(m, Loss, shocks_db)`` (conditional) -- handy for comparing a hand-chosen rule with the optimised one. OSR and indirect inference -------------------------- Because OSR is solved exactly like an estimation problem -- an objective minimised over a set of parameters with bounds -- it composes with RISE's estimation machinery: the same optimisers (see :doc:`../../Estimation/Posterior maximization`) are available, and OSR can be combined with :doc:`indirect inference <../../Estimation/Indirect Inference>` (choosing rule coefficients to match empirical moments or impulse responses) by supplying the corresponding objective. OSR with a user-defined objective --------------------------------- ``optimal_simple_rule`` takes the loss as an *expression* -- the string (or ``{discount, string}`` cell) described above; it does not accept a function handle. If the objective you want cannot be written that way -- for example matching empirical moments or impulse responses -- use :doc:`indirect inference <../../Estimation/Indirect Inference>` directly, treating the rule coefficients as the estimated parameters. There the objective *is* a function handle, with the signature :: [critmin, retcode] = myobjective(m) where ``m`` is the solved model object, ``critmin`` is the scalar criterion to be minimised, and ``retcode`` is ``0`` when the evaluation succeeded. The rule coefficients are selected exactly as in any indirect-inference exercise (supply them through ``priors``); see the indirect-inference page for a worked example. Policy frontiers ---------------- To trace a **policy frontier** -- how the model's volatilities trade off as a preference parameter (a weight in the loss, the persistence of the rule, ...) is varied -- use ``frontier``:: % vary the weight on the output gap over a 50-point grid between 0 and 2 f = frontier(mest, 'lamb_y', [0, 2]); % or pass an explicit grid f = frontier(mest, 'lamb_y', linspace(0, 2, 21)); % use simulated rather than theoretical moments f = frontier(mest, 'lamb_y', [0, 2], true); ``f`` is a structure of the standard deviations of every model variable at each grid point (plus a ``stats__`` sub-structure with the grid, the number of simulation periods, and a per-point return code); plot the columns of interest against each other to get the frontier. .. todo:: Add a complete worked example (model file, baseline rule, optimised rule, loss comparison, and a plotted frontier).