diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index caa7d611d258e3bc13f852c160b11aa770505816..9b9aae02757fdfad9377d9333de5f464dbb78683 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -175,7 +175,7 @@ class ParsingDriver;
 %token HETEROSKEDASTIC_FILTER TIME_SHIFT STRUCTURAL TERMINAL_STEADY_STATE_AS_GUESS_VALUE CONSTANT_SIMULATION_LENGTH
 %token SURPRISE OCCBIN_CONSTRAINTS
 %token PAC_TARGET_INFO COMPONENT TARGET AUXNAME AUXNAME_TARGET_NONSTATIONARY PAC_TARGET_NONSTATIONARY
-%token <string> KIND LL DL DD
+%token <string> KIND LL DL DD ADD MULTIPLY
 /* Method of Moments */
 %token METHOD_OF_MOMENTS MOM_METHOD
 %token BARTLETT_KERNEL_LAG WEIGHTING_MATRIX WEIGHTING_MATRIX_SCALING_FACTOR ANALYTIC_STANDARD_ERRORS ANALYTIC_JACOBIAN PENALIZED_ESTIMATOR VERBOSE
@@ -1152,7 +1152,11 @@ shock_elem : det_shock_elem
            ;
 
 det_shock_elem : VAR symbol ';' PERIODS period_list ';' VALUES value_list ';'
-                 { driver.add_det_shock($2, $5, $8, false); }
+                 { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::standard); }
+               | VAR symbol ';' PERIODS period_list ';' ADD value_list ';'
+                 { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::add); }
+               | VAR symbol ';' PERIODS period_list ';' MULTIPLY value_list ';'
+                 { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::multiply); }
                ;
 
 det_shock_list : det_shock_list det_shock_elem
@@ -3207,7 +3211,7 @@ conditional_forecast_paths_shock_list : conditional_forecast_paths_shock_elem
                                       ;
 
 conditional_forecast_paths_shock_elem : VAR symbol ';' PERIODS period_list ';' VALUES value_list ';'
-                                        { driver.add_det_shock($2, $5, $8, true); }
+                                        { driver.add_det_shock($2, $5, $8, ParsingDriver::DetShockType::conditional_forecast); }
                                       ;
 
 steady_state_model : STEADY_STATE_MODEL ';' { driver.begin_steady_state_model(); }
@@ -4284,6 +4288,8 @@ symbol : NAME
        | LL
        | DL
        | DD
+       | ADD
+       | MULTIPLY
        ;
 
 %%
diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll
index 40dc09bfd77c4730718f1b62d5351760bf77c3fb..8f0e1b1a9f7dfa10b027150e3f7930751ee87eb8 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -770,6 +770,14 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
 <DYNARE_BLOCK>corr {return token::CORR;}
 <DYNARE_BLOCK>periods {return token::PERIODS;}
 <DYNARE_BLOCK>scales {return token::SCALES;}
+<DYNARE_BLOCK>add {
+  yylval->build<string>(yytext);
+  return token::ADD;
+}
+<DYNARE_BLOCK>multiply {
+  yylval->build<string>(yytext);
+  return token::MULTIPLY;
+}
 <DYNARE_STATEMENT,DYNARE_BLOCK>cutoff {return token::CUTOFF;}
 <DYNARE_STATEMENT,DYNARE_BLOCK>mfs {return token::MFS;}
 <DYNARE_STATEMENT,DYNARE_BLOCK>balanced_growth_test_tol {return token::BALANCED_GROWTH_TEST_TOL;}
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index 806a4642816ea73dd298027ddad7783788b8eff3..2b1772487e734823449b08c612494baa6eb30dad 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -841,6 +841,10 @@ ParsingDriver::end_shocks(bool overwrite)
   mod_file->addStatement(make_unique<ShocksStatement>(overwrite, det_shocks, var_shocks, std_shocks,
                                                       covar_shocks, corr_shocks, mod_file->symbol_table));
   det_shocks.clear();
+  if (!learnt_shocks_add.empty())
+    error("shocks: 'add' keyword not allowed unless 'learnt_in' option is passed");
+  if (!learnt_shocks_multiply.empty())
+    error("shocks: 'multiply' keyword not allowed unless 'learnt_in' option is passed");
   var_shocks.clear();
   std_shocks.clear();
   covar_shocks.clear();
@@ -852,6 +856,10 @@ ParsingDriver::end_mshocks(bool overwrite)
 {
   mod_file->addStatement(make_unique<MShocksStatement>(overwrite, det_shocks, mod_file->symbol_table));
   det_shocks.clear();
+  if (!learnt_shocks_add.empty())
+    error("mshocks: 'add' keyword not allowed");
+  if (!learnt_shocks_multiply.empty())
+    error("mshocks: 'multiply' keyword not allowed");
 }
 
 void
@@ -859,6 +867,10 @@ ParsingDriver::end_shocks_surprise(bool overwrite)
 {
   mod_file->addStatement(make_unique<ShocksSurpriseStatement>(overwrite, det_shocks, mod_file->symbol_table));
   det_shocks.clear();
+  if (!learnt_shocks_add.empty())
+    error("shocks(surprise): 'add' keyword not allowed");
+  if (!learnt_shocks_multiply.empty())
+    error("shocks(surprise): 'multiply' keyword not allowed");
 }
 
 void
@@ -871,8 +883,35 @@ ParsingDriver::end_shocks_learnt_in(const string &learnt_in_period, bool overwri
     for (auto [period1, period2, expr] : vals)
       if (period1 < learnt_in_period_int)
         error("shocks: for variable " + mod_file->symbol_table.getName(symb_id) + ", shock period (" + to_string(period1) + ") is earlier than the period in which the shock is learnt (" + learnt_in_period + ")");
-  mod_file->addStatement(make_unique<ShocksLearntInStatement>(learnt_in_period_int, overwrite, det_shocks, mod_file->symbol_table));
+
+  // Aggregate the three types of shocks
+  ShocksLearntInStatement::learnt_shocks_t learnt_shocks;
+  for (const auto &[id, v] : det_shocks)
+    {
+      vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2;
+      for (auto [period1, period2, value] : v)
+        v2.emplace_back(ShocksLearntInStatement::LearntShockType::level, period1, period2, value);
+      learnt_shocks[id] = v2;
+    }
+  for (const auto &[id, v] : learnt_shocks_add)
+    {
+      vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2;
+      for (auto [period1, period2, value] : v)
+        v2.emplace_back(ShocksLearntInStatement::LearntShockType::add, period1, period2, value);
+      learnt_shocks[id] = v2;
+    }
+  for (const auto &[id, v] : learnt_shocks_multiply)
+    {
+      vector<tuple<ShocksLearntInStatement::LearntShockType, int, int, expr_t>> v2;
+      for (auto [period1, period2, value] : v)
+        v2.emplace_back(ShocksLearntInStatement::LearntShockType::multiply, period1, period2, value);
+      learnt_shocks[id] = v2;
+    }
+
+  mod_file->addStatement(make_unique<ShocksLearntInStatement>(learnt_in_period_int, overwrite, learnt_shocks, mod_file->symbol_table));
   det_shocks.clear();
+  learnt_shocks_add.clear();
+  learnt_shocks_multiply.clear();
 }
 
 void
@@ -885,16 +924,28 @@ ParsingDriver::end_heteroskedastic_shocks(bool overwrite)
 }
 
 void
-ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool conditional_forecast)
+ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, DetShockType type)
 {
-  if (conditional_forecast)
-    check_symbol_is_endogenous(var);
-  else
-    check_symbol_is_exogenous(var, true);
+  switch (type)
+    {
+    case DetShockType::conditional_forecast:
+      check_symbol_is_endogenous(var);
+      break;
+    case DetShockType::standard:
+       // Allow exo_det, for stochastic context
+      check_symbol_is_exogenous(var, true);
+      break;
+    case DetShockType::add:
+    case DetShockType::multiply:
+      check_symbol_is_exogenous(var, false);
+      break;
+    }
 
   int symb_id = mod_file->symbol_table.getID(var);
 
-  if (det_shocks.find(symb_id) != det_shocks.end())
+  if (det_shocks.find(symb_id) != det_shocks.end()
+      || learnt_shocks_add.find(symb_id) != learnt_shocks_add.end()
+      || learnt_shocks_multiply.find(symb_id) != learnt_shocks_multiply.end())
     error("shocks/conditional_forecast_paths: variable " + var + " declared twice");
 
   if (periods.size() != values.size())
@@ -905,7 +956,19 @@ ParsingDriver::add_det_shock(const string &var, const vector<pair<int, int>> &pe
   for (size_t i = 0; i < periods.size(); i++)
     v.emplace_back(periods[i].first, periods[i].second, values[i]);
 
-  det_shocks[symb_id] = v;
+  switch (type)
+    {
+    case DetShockType::standard:
+    case DetShockType::conditional_forecast:
+      det_shocks[symb_id] = v;
+      break;
+    case DetShockType::add:
+      learnt_shocks_add[symb_id] = v;
+      break;
+    case DetShockType::multiply:
+      learnt_shocks_multiply[symb_id] = v;
+      break;
+    }
 }
 
 void
@@ -2435,6 +2498,10 @@ ParsingDriver::conditional_forecast_paths()
 {
   mod_file->addStatement(make_unique<ConditionalForecastPathsStatement>(det_shocks, mod_file->symbol_table));
   det_shocks.clear();
+  if (!learnt_shocks_add.empty())
+    error("conditional_forecast_paths: 'add' keyword not allowed");
+  if (!learnt_shocks_multiply.empty())
+    error("conditional_forecast_paths: 'multiply' keyword not allowed");
 }
 
 void
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index 8b3af55fa14ef03810869214ebc1acea6a590a5a..e53b5307a15ea73d4dbb23c6469f04db84f0c8da 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -150,8 +150,11 @@ private:
   //! Temporary storage of covariances from optim_weights
   OptimWeightsStatement::covar_weights_t covar_weights;
   /* Temporary storage for deterministic shocks. Also used for
-     conditional_forecast paths and for surprise shocks. */
+     conditional_forecast paths, for shocks(surprise), and shocks(learnt_in=…)
+     (for the latter, only used for shocks declared in level through “values”). */
   ShocksStatement::det_shocks_t det_shocks;
+  // Temporary storage for shocks declared with “add” and “multiply” in shocks(learnt_in=…)
+  ShocksStatement::det_shocks_t learnt_shocks_add, learnt_shocks_multiply;
   //! Temporary storage for variances of shocks
   ShocksStatement::var_and_std_shocks_t var_shocks;
   //! Temporary storage for standard errors of shocks
@@ -455,7 +458,14 @@ public:
   void end_heteroskedastic_shocks(bool overwrite);
   /* Adds a deterministic shock, a path element inside a
      conditional_forecast_paths block, or a surprise shock */
-  void add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool conditional_forecast);
+  enum class DetShockType
+    {
+      standard,
+      add, // for “add” in “shocks(learnt_in)”
+      multiply, // for “multiply” in “shocks(learnt_in)”
+      conditional_forecast
+    };
+  void add_det_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, DetShockType type);
   //! Adds a heteroskedastic shock (either values or scales)
   void add_heteroskedastic_shock(const string &var, const vector<pair<int, int>> &periods, const vector<expr_t> &values, bool scales);
   //! Adds a std error shock
diff --git a/src/Shocks.cc b/src/Shocks.cc
index 240c464cae69b7463008e33a1762d65ea0834ec4..62e50d988d1b2bf9d0046bae9783430de6b3823c 100644
--- a/src/Shocks.cc
+++ b/src/Shocks.cc
@@ -492,7 +492,7 @@ ShocksSurpriseStatement::writeJsonOutput(ostream &output) const
 }
 
 ShocksLearntInStatement::ShocksLearntInStatement(int learnt_in_period_arg, bool overwrite_arg,
-                                                 AbstractShocksStatement::det_shocks_t learnt_shocks_arg,
+                                                 learnt_shocks_t learnt_shocks_arg,
                                                  const SymbolTable &symbol_table_arg) :
   learnt_in_period{learnt_in_period_arg}, overwrite{overwrite_arg},
   learnt_shocks{move(learnt_shocks_arg)}, symbol_table{symbol_table_arg}
@@ -505,6 +505,21 @@ ShocksLearntInStatement::checkPass(ModFileStructure &mod_file_struct, WarningCon
   mod_file_struct.shocks_learnt_in_present = true;
 }
 
+string
+ShocksLearntInStatement::typeToString(LearntShockType type)
+{
+  switch (type)
+    {
+    case LearntShockType::level:
+      return "level";
+    case LearntShockType::add:
+      return "add";
+    case LearntShockType::multiply:
+      return "multiply";
+    }
+  exit(EXIT_FAILURE); // Silence GCC warning
+}
+
 void
 ShocksLearntInStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const
 {
@@ -516,11 +531,12 @@ ShocksLearntInStatement::writeOutput(ostream &output, const string &basename, bo
   output << "M_.learnt_shocks = [ M_.learnt_shocks;" << endl;
   for (const auto &[id, shock_vec] : learnt_shocks)
     {
-      for (const auto &[period1, period2, value] : shock_vec)
+      for (const auto &[type, period1, period2, value] : shock_vec)
         {
           output << "struct('learnt_in'," << learnt_in_period
                  << ",'exo_id'," << symbol_table.getTypeSpecificID(id)+1
                  << ",'periods'," << period1 << ":" << period2
+                 << ",'type','" << typeToString(type) << "'"
                  << ",'value',";
           value->writeOutput(output);
           output << ");" << endl;
@@ -544,11 +560,12 @@ ShocksLearntInStatement::writeJsonOutput(ostream &output) const
              << R"("values": [)";
       for (auto it1 = it->second.begin(); it1 != it->second.end(); ++it1)
         {
-          auto [period1, period2, value] = *it1;
+          auto [type, period1, period2, value] = *it1;
           if (it1 != it->second.begin())
             output << ", ";
           output << R"({"period1": )" << period1 << ", "
                  << R"("period2": )" << period2 << ", "
+                 << R"("type": ")" << typeToString(type) << R"(", )"
                  << R"("value": ")";
           value->writeJsonOutput(output, {}, {});
           output << R"("})";
diff --git a/src/Shocks.hh b/src/Shocks.hh
index dac2ab0c224c8160f2a61470309980ad81321dd0..6280b1d330dba8435965a33b063c02947adcc60a 100644
--- a/src/Shocks.hh
+++ b/src/Shocks.hh
@@ -115,12 +115,21 @@ public:
   const int learnt_in_period;
   //! Does this "shocks(learnt_in=…)" statement replace the previous ones?
   const bool overwrite;
-  const AbstractShocksStatement::det_shocks_t learnt_shocks;
+  enum class LearntShockType
+    {
+      level,
+      add,
+      multiply
+    };
+  // The tuple is (type, period1, period2, value)
+  using learnt_shocks_t = map<int, vector<tuple<LearntShockType, int, int, expr_t>>>;
+  const learnt_shocks_t learnt_shocks;
 private:
   const SymbolTable &symbol_table;
+  static string typeToString(LearntShockType type);
 public:
   ShocksLearntInStatement(int learnt_in_period_arg, bool overwrite_arg,
-                          AbstractShocksStatement::det_shocks_t learnt_shocks_arg,
+                          learnt_shocks_t learnt_shocks_arg,
                           const SymbolTable &symbol_table_arg);
   void checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings) override;
   void writeOutput(ostream &output, const string &basename, bool minimal_workspace) const override;