diff --git a/src/DynareBison.yy b/src/DynareBison.yy
index 98cee52eaf94b118dd10b088764cdc524f105015..ddcb24d833fc09be5eff8af92c2c6f5b03afeacd 100644
--- a/src/DynareBison.yy
+++ b/src/DynareBison.yy
@@ -172,6 +172,7 @@ class ParsingDriver;
 %token NUMBER_OF_POSTERIOR_DRAWS_AFTER_PERTURBATION MAX_NUMBER_OF_STAGES
 %token RANDOM_FUNCTION_CONVERGENCE_CRITERION RANDOM_PARAMETER_CONVERGENCE_CRITERION NO_INIT_ESTIMATION_CHECK_FIRST_OBS
 %token HETEROSKEDASTIC_FILTER TIME_SHIFT STRUCTURAL TERMINAL_STEADY_STATE_AS_GUESS_VALUE
+%token SURPRISE
 /* 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 
@@ -1028,6 +1029,9 @@ pound_expression: '#' symbol EQUAL hand_side ';'
 shocks : SHOCKS ';' shock_list END ';' { driver.end_shocks(false); }
        | SHOCKS '(' OVERWRITE ')' ';' shock_list END ';' { driver.end_shocks(true); }
        | SHOCKS '(' OVERWRITE ')' ';'  END ';' { driver.end_shocks(true); }
+       | SHOCKS '(' SURPRISE ')' ';' surprise_shock_list END ';' { driver.end_shocks_surprise(false); }
+       | SHOCKS '(' SURPRISE COMMA OVERWRITE ')' ';' surprise_shock_list END ';' { driver.end_shocks_surprise(true); }
+       | SHOCKS '(' OVERWRITE COMMA SURPRISE ')' ';' surprise_shock_list END ';' { driver.end_shocks_surprise(true); }
        ;
 
 shock_list : shock_list shock_elem
@@ -1049,6 +1053,10 @@ det_shock_elem : VAR symbol ';' PERIODS period_list ';' VALUES value_list ';'
                  { driver.add_det_shock($2, $5, $8, false); }
                ;
 
+surprise_shock_list : surprise_shock_list det_shock_elem
+                    | det_shock_elem
+                    ;
+
 heteroskedastic_shocks : HETEROSKEDASTIC_SHOCKS ';' heteroskedastic_shock_list END ';'
                          { driver.end_heteroskedastic_shocks(false); }
                        | HETEROSKEDASTIC_SHOCKS '(' OVERWRITE ')' ';' heteroskedastic_shock_list END ';'
diff --git a/src/DynareFlex.ll b/src/DynareFlex.ll
index 6816154d8bc760d25023ba790b804bc4b43b8ad2..fec10ec5b1edebfc3bbc6c7c8fdaaf7281f9ff2d 100644
--- a/src/DynareFlex.ll
+++ b/src/DynareFlex.ll
@@ -761,6 +761,7 @@ DATE -?[0-9]+([ya]|m([1-9]|1[0-2])|q[1-4])
 <DYNARE_BLOCK>uniform_pdf {return token::UNIFORM_PDF;}
 <DYNARE_BLOCK>weibull_pdf {return token::WEIBULL_PDF;}
 <DYNARE_BLOCK>dsge_prior_weight {return token::DSGE_PRIOR_WEIGHT;}
+<DYNARE_BLOCK>surprise {return token::SURPRISE;}
 
 <DYNARE_BLOCK>; {return Dynare::parser::token_type (yytext[0]);}
 <DYNARE_BLOCK># {return Dynare::parser::token_type (yytext[0]);}
diff --git a/src/ModFile.cc b/src/ModFile.cc
index 8fc0b422b02ad9c414b78ced0981d826d37d47b1..6c8b6b6e0561d74c6c80f74d0c071e54ecbf14a5 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -700,6 +700,12 @@ ModFile::transformPass(bool nostrict, bool stochastic, bool compute_xrefs, bool
       exit(EXIT_FAILURE);
     }
 
+  if (mod_file_struct.shocks_surprise_present && !occbin)
+    {
+      cerr << "ERROR: the 'shocks(surprise)' block can only be used in conjunction with occbin commands." << endl;
+      exit(EXIT_FAILURE);
+    }
+
   if (!mod_file_struct.ramsey_model_present)
     cout << "Found " << dynamic_model.equation_number() << " equation(s)." << endl;
   else
@@ -985,8 +991,9 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global,
   // May be later modified by a shocks block
   mOutputFile << "M_.sigma_e_is_diagonal = true;" << endl;
 
-  // Initialize M_.det_shocks and M_.heteroskedastic_shocks
+  // Initialize M_.det_shocks, M_.surprise_shocks and M_.heteroskedastic_shocks
   mOutputFile << "M_.det_shocks = [];" << endl
+              << "M_.surprise_shocks = [];" << endl
               << "M_.heteroskedastic_shocks.Qvalue_orig = [];" << endl
               << "M_.heteroskedastic_shocks.Qscale_orig = [];" << endl;
 
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index 1bf5618dbcba8c8a90c67390eca8f093edb26c24..2674f8f2ffd520ec69c028524ca6e212c87ed0dd 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -798,6 +798,13 @@ ParsingDriver::end_mshocks(bool overwrite)
   det_shocks.clear();
 }
 
+void
+ParsingDriver::end_shocks_surprise(bool overwrite)
+{
+  mod_file->addStatement(make_unique<ShocksSurpriseStatement>(overwrite, det_shocks, mod_file->symbol_table));
+  det_shocks.clear();
+}
+
 void
 ParsingDriver::end_heteroskedastic_shocks(bool overwrite)
 {
diff --git a/src/ParsingDriver.hh b/src/ParsingDriver.hh
index 972bd40b1421eb3ff4e11cf8cb25fa7479cfad83..324883b4a9d848d086985d7a0448e1fba884c128 100644
--- a/src/ParsingDriver.hh
+++ b/src/ParsingDriver.hh
@@ -149,7 +149,8 @@ private:
   OptimWeightsStatement::var_weights_t var_weights;
   //! Temporary storage of covariances from optim_weights
   OptimWeightsStatement::covar_weights_t covar_weights;
-  //! Temporary storage for deterministic shocks
+  /* Temporary storage for deterministic shocks. Also used for
+     conditional_forecast paths and for surprise shocks. */
   ShocksStatement::det_shocks_t det_shocks;
   //! Temporary storage for variances of shocks
   ShocksStatement::var_and_std_shocks_t var_shocks;
@@ -436,9 +437,12 @@ public:
   void end_shocks(bool overwrite);
   //! Writes a mshocks statement
   void end_mshocks(bool overwrite);
+  //! Writes a shocks(surprise) statement
+  void end_shocks_surprise(bool overwrite);
   //! Writes a heteroskedastic_shocks statement
   void end_heteroskedastic_shocks(bool overwrite);
-  //! Adds a deterministic shock or a path element inside a conditional_forecast_paths block
+  /* 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);
   //! 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);
diff --git a/src/Shocks.cc b/src/Shocks.cc
index 4fbf8607726770a8b88d94aff1632052eb670f75..0e77c0c9d8dd9fb628110a6c17e485f87fdb24e0 100644
--- a/src/Shocks.cc
+++ b/src/Shocks.cc
@@ -428,6 +428,69 @@ MShocksStatement::writeJsonOutput(ostream &output) const
   output << "}";
 }
 
+ShocksSurpriseStatement::ShocksSurpriseStatement(bool overwrite_arg,
+                                                 AbstractShocksStatement::det_shocks_t surprise_shocks_arg,
+                                                 const SymbolTable &symbol_table_arg) :
+  overwrite{overwrite_arg}, surprise_shocks{move(surprise_shocks_arg)},
+  symbol_table{symbol_table_arg}
+{
+}
+
+void
+ShocksSurpriseStatement::checkPass(ModFileStructure &mod_file_struct, WarningConsolidation &warnings)
+{
+  mod_file_struct.shocks_surprise_present = true;
+}
+
+void
+ShocksSurpriseStatement::writeOutput(ostream &output, const string &basename, bool minimal_workspace) const
+{
+  if (overwrite)
+    output << "M_.surprise_shocks = [" << endl;
+  else
+    output << "M_.surprise_shocks = [ M_.surprise_shocks;" << endl;
+  for (const auto &[id, shock_vec] : surprise_shocks)
+    {
+      for (const auto &[period1, period2, value] : shock_vec)
+        {
+          output << "struct('exo_id'," << symbol_table.getTypeSpecificID(id)+1
+                 << ",'periods'," << period1 << ":" << period2
+                 << ",'value',";
+          value->writeOutput(output);
+          output << ");" << endl;
+        }
+    }
+  output << "];" << endl;
+}
+
+void
+ShocksSurpriseStatement::writeJsonOutput(ostream &output) const
+{
+  output << R"({"statementName": "shocks")"
+         << R"(, "surprise": true)"
+         << R"(, "surprise_shocks": [)";
+  for (auto it = surprise_shocks.begin(); it != surprise_shocks.end(); ++it)
+    {
+      if (it != surprise_shocks.begin())
+        output << ", ";
+      output << R"({"var": ")" << symbol_table.getName(it->first) << R"(", )"
+             << R"("values": [)";
+      for (auto it1 = it->second.begin(); it1 != it->second.end(); ++it1)
+        {
+          auto [period1, period2, value] = *it1;
+          if (it1 != it->second.begin())
+            output << ", ";
+          output << R"({"period1": )" << period1 << ", "
+                 << R"("period2": )" << period2 << ", "
+                 << R"("value": ")";
+          value->writeJsonOutput(output, {}, {});
+          output << R"("})";
+        }
+      output << "]}";
+    }
+  output << "]}";
+}
+
 ConditionalForecastPathsStatement::ConditionalForecastPathsStatement(AbstractShocksStatement::det_shocks_t paths_arg,
                                                                      const SymbolTable &symbol_table_arg) :
   paths{move(paths_arg)},
diff --git a/src/Shocks.hh b/src/Shocks.hh
index 243768832acbf5f0f9cffdc8f1ee28eb0cd818a2..b2b22155ba672863d3f4e053a288ed89ec5decd4 100644
--- a/src/Shocks.hh
+++ b/src/Shocks.hh
@@ -86,6 +86,26 @@ public:
   void writeJsonOutput(ostream &output) const override;
 };
 
+/* Represents a shocks(surprise) block.
+   Given the differences with the plain “shocks” block, it was easier to make
+   it a separate class. */
+class ShocksSurpriseStatement : public Statement
+{
+public:
+  //! Does this "shocks(surprise)" statement replace the previous ones?
+  const bool overwrite;
+  const AbstractShocksStatement::det_shocks_t surprise_shocks;
+private:
+  const SymbolTable &symbol_table;
+public:
+  ShocksSurpriseStatement(bool overwrite_arg,
+                          AbstractShocksStatement::det_shocks_t surprise_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;
+  void writeJsonOutput(ostream &output) const override;
+};
+
 class ConditionalForecastPathsStatement : public Statement
 {
 private:
diff --git a/src/Statement.hh b/src/Statement.hh
index 32e020a8b196af042192fe82ad45c3b57b4c4f17..0b23aad61522fefb350aded009fe2b7fed698301 100644
--- a/src/Statement.hh
+++ b/src/Statement.hh
@@ -145,6 +145,8 @@ public:
   /* Lists symbol IDs of parameters that appear in a “planner_discount” option.
      See dynare#1173 for more details. */
   set<int> parameters_in_planner_discount;
+  // Whether a shocks(surprise) block appears
+  bool shocks_surprise_present{false};
 };
 
 class Statement