diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index a5044784ddad2450093c743bfa478868025f3b49..ed424fbda3a32839b38177fc2bf4a77666a3ac20 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -1760,7 +1760,6 @@ DynamicModel::writeDynamicCFile(const string &basename, const int order) const
                   << "#include <uchar.h>" << endl // For MATLAB ≤ R2011a
                   << R"(#include "mex.h")" << endl
                   << endl
-                  << "const int ntt = " << ntt << ";" << endl
                   << "void dynamic_resid_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
                   << "void dynamic_resid(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *residual);" << endl
                   << "void dynamic_g1_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T);" << endl
@@ -1792,7 +1791,7 @@ DynamicModel::writeDynamicCFile(const string &basename, const int order) const
                   << "  /* Gets number of rows of matrix x. */" << endl
                   << "  int nb_row_x = mxGetM(prhs[1]);" << endl
                   << endl
-                  << "  double *T = (double *) malloc(sizeof(double)*ntt);"
+                  << "  double *T = (double *) malloc(sizeof(double)*" << ntt << ");"
                   << endl
                   << "  if (nlhs >= 1)" << endl
                   << "  {" << endl
@@ -2463,14 +2462,8 @@ DynamicModel::writeDynamicModel(const string &basename, bool use_dll, bool julia
 void
 DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput, bool use_dll, bool julia) const
 {
-  ostringstream model_tt_output;             // Used for storing model temp vars
-  ostringstream model_output;                // Used for storing model equations
-  ostringstream jacobian_tt_output;          // Used for storing jacobian temp vars
-  ostringstream jacobian_output;             // Used for storing jacobian equations
-  ostringstream hessian_tt_output;           // Used for storing Hessian temp vars
-  ostringstream hessian_output;              // Used for storing Hessian equations
-  ostringstream third_derivatives_tt_output; // Used for storing third order derivatives temp terms
-  ostringstream third_derivatives_output;    // Used for storing third order derivatives equations
+  vector<ostringstream> d_output(derivatives.size());  // Derivatives output (at all orders, including 0=residual)
+  vector<ostringstream> tt_output(derivatives.size()); // Temp terms output (at all orders)
 
   ExprNodeOutputType output_type = (use_dll ? ExprNodeOutputType::CDynamicModel :
                                     julia ? ExprNodeOutputType::juliaDynamicModel : ExprNodeOutputType::matlabDynamicModel);
@@ -2479,14 +2472,14 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
   temporary_terms_t temp_term_union;
 
   writeModelLocalVariableTemporaryTerms(temp_term_union, temporary_terms_idxs,
-                                        model_tt_output, output_type, tef_terms);
+                                        tt_output[0], output_type, tef_terms);
 
   writeTemporaryTerms(temporary_terms_derivatives[0],
                       temp_term_union,
                       temporary_terms_idxs,
-                      model_tt_output, output_type, tef_terms);
+                      tt_output[0], output_type, tef_terms);
 
-  writeModelEquations(model_output, output_type, temp_term_union);
+  writeModelEquations(d_output[0], output_type, temp_term_union);
 
   int nrows = equations.size();
   int hessianColsNbr = dynJacobianColsNbr * dynJacobianColsNbr;
@@ -2497,7 +2490,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
       writeTemporaryTerms(temporary_terms_derivatives[1],
                           temp_term_union,
                           temporary_terms_idxs,
-                          jacobian_tt_output, output_type, tef_terms);
+                          tt_output[1], output_type, tef_terms);
 
       for (const auto & first_derivative : derivatives[1])
         {
@@ -2505,189 +2498,139 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
           tie(eq, var) = vectorToTuple<2>(first_derivative.first);
           expr_t d1 = first_derivative.second;
 
-          jacobianHelper(jacobian_output, eq, getDynJacobianCol(var), output_type);
-          jacobian_output << "=";
-          d1->writeOutput(jacobian_output, output_type,
+          jacobianHelper(d_output[1], eq, getDynJacobianCol(var), output_type);
+          d_output[1] << "=";
+          d1->writeOutput(d_output[1], output_type,
                           temp_term_union, temporary_terms_idxs, tef_terms);
-          jacobian_output << ";" << endl;
+          d_output[1] << ";" << endl;
         }
     }
 
-  // Writing Hessian
-  if (!derivatives[2].empty())
-    {
-      writeTemporaryTerms(temporary_terms_derivatives[2],
-                          temp_term_union,
-                          temporary_terms_idxs,
-                          hessian_tt_output, output_type, tef_terms);
-
-      /* When creating the sparse matrix (in MATLAB or C mode), since storage
-         is in column-major order, output the first column, then the second,
-         then the third. This gives a significant performance boost in use_dll
-         mode (at both compilation and runtime), because it facilitates memory
-         accesses and expression reusage. */
-      ostringstream col0_output, col1_output, col2_output;
-
-      int k = 0; // Keep the line of a 2nd derivative in v2
-      for (const auto & second_derivative : derivatives[2])
-        {
-          int eq, var1, var2;
-          tie(eq, var1, var2) = vectorToTuple<3>(second_derivative.first);
-          expr_t d2 = second_derivative.second;
-
-          int id1 = getDynJacobianCol(var1);
-          int id2 = getDynJacobianCol(var2);
-
-          int col_nb = id1 * dynJacobianColsNbr + id2;
-          int col_nb_sym = id2 * dynJacobianColsNbr + id1;
-
-          ostringstream for_sym;
-          if (output_type == ExprNodeOutputType::juliaDynamicModel)
-            {
-              for_sym << "g2[" << eq + 1 << "," << col_nb + 1 << "]";
-              hessian_output << "    @inbounds " << for_sym.str() << " = ";
-              d2->writeOutput(hessian_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              hessian_output << endl;
-            }
-          else
-            {
-              sparseHelper(2, col0_output, k, 0, output_type);
-              col0_output << "=" << eq + 1 << ";" << endl;
-
-              sparseHelper(2, col1_output, k, 1, output_type);
-              col1_output << "=" << col_nb + 1 << ";" << endl;
-
-              sparseHelper(2, col2_output, k, 2, output_type);
-              col2_output << "=";
-              d2->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              col2_output << ";" << endl;
+  // Write derivatives for order ≥ 2
+  for (size_t i = 2; i < derivatives.size(); i++)
+    if (!derivatives[i].empty())
+      {
+        writeTemporaryTerms(temporary_terms_derivatives[i],
+                            temp_term_union,
+                            temporary_terms_idxs,
+                            tt_output[i], output_type, tef_terms);
+
+        /* When creating the sparse matrix (in MATLAB or C mode), since storage
+           is in column-major order, output the first column, then the second,
+           then the third. This gives a significant performance boost in use_dll
+           mode (at both compilation and runtime), because it facilitates memory
+           accesses and expression reusage. */
+        ostringstream col0_output, col1_output, col2_output;
+
+        int k = 0; // Current line index in the 3-column matrix
+        for (const auto &dit : derivatives[i])
+          {
+            const vector<int> &vidx = dit.first;
+            expr_t d = dit.second;
+            int eq = vidx[0];
 
-              k++;
-            }
+            int col_idx = 0;
+            for (size_t j = 1; j < vidx.size(); j++)
+              {
+                col_idx *= dynJacobianColsNbr;
+                col_idx += getDynJacobianCol(vidx[j]);
+              }
 
-          // Treating symetric elements
-          if (id1 != id2)
             if (output_type == ExprNodeOutputType::juliaDynamicModel)
-              hessian_output << "    @inbounds g2[" << eq + 1 << "," << col_nb_sym + 1 << "] = "
-                             << for_sym.str() << endl;
+              {
+                d_output[i] << "    @inbounds " << "g" << i << "[" << eq + 1 << "," << col_idx + 1 << "] = ";
+                d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs, tef_terms);
+                d_output[i] << endl;
+              }
             else
               {
-                sparseHelper(2, col0_output, k, 0, output_type);
+                sparseHelper(i, col0_output, k, 0, output_type);
                 col0_output << "=" << eq + 1 << ";" << endl;
 
-                sparseHelper(2, col1_output, k, 1, output_type);
-                col1_output << "=" << col_nb_sym + 1 << ";" << endl;
+                sparseHelper(i, col1_output, k, 1, output_type);
+                col1_output << "=" << col_idx + 1 << ";" << endl;
 
-                sparseHelper(2, col2_output, k, 2, output_type);
+                sparseHelper(i, col2_output, k, 2, output_type);
                 col2_output << "=";
-                sparseHelper(2, col2_output, k-1, 2, output_type);
+                d->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
                 col2_output << ";" << endl;
 
                 k++;
               }
-        }
-
-      if (output_type != ExprNodeOutputType::juliaDynamicModel)
-        hessian_output << col0_output.str() << col1_output.str() << col2_output.str();
-    }
-
-  // Writing third derivatives
-  if (!derivatives[3].empty())
-    {
-      writeTemporaryTerms(temporary_terms_derivatives[3],
-                          temp_term_union,
-                          temporary_terms_idxs,
-                          third_derivatives_tt_output, output_type, tef_terms);
-
-      // See comment above for 2nd order
-      ostringstream col0_output, col1_output, col2_output;
 
-      int k = 0; // Keep the line of a 3rd derivative in v3
-      for (const auto & third_derivative : derivatives[3])
-        {
-          int eq, var1, var2, var3;
-          tie(eq, var1, var2, var3) = vectorToTuple<4>(third_derivative.first);
-          expr_t d3 = third_derivative.second;
+            // Output symetric elements, but only at order 2 and 3
+            if (i == 2 && vidx[1] != vidx[2])
+              {
+                int col_idx_sym = getDynJacobianCol(vidx[2]) * dynJacobianColsNbr + getDynJacobianCol(vidx[1]);
 
-          int id1 = getDynJacobianCol(var1);
-          int id2 = getDynJacobianCol(var2);
-          int id3 = getDynJacobianCol(var3);
+                if (output_type == ExprNodeOutputType::juliaDynamicModel)
+                  d_output[2] << "    @inbounds g2[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
+                              << "g2[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
+                else
+                  {
+                    sparseHelper(2, col0_output, k, 0, output_type);
+                    col0_output << "=" << eq + 1 << ";" << endl;
 
-          // Reference column number for the g3 matrix
-          int ref_col = id1 * hessianColsNbr + id2 * dynJacobianColsNbr + id3;
+                    sparseHelper(2, col1_output, k, 1, output_type);
+                    col1_output << "=" << col_idx_sym + 1 << ";" << endl;
 
-          ostringstream for_sym;
-          if (output_type == ExprNodeOutputType::juliaDynamicModel)
-            {
-              for_sym << "g3[" << eq + 1 << "," << ref_col + 1 << "]";
-              third_derivatives_output << "    @inbounds " << for_sym.str() << " = ";
-              d3->writeOutput(third_derivatives_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              third_derivatives_output << endl;
-            }
-          else
-            {
-              sparseHelper(3, col0_output, k, 0, output_type);
-              col0_output << "=" << eq + 1 << ";" << endl;
+                    sparseHelper(2, col2_output, k, 2, output_type);
+                    col2_output << "=";
+                    sparseHelper(2, col2_output, k-1, 2, output_type);
+                    col2_output << ";" << endl;
 
-              sparseHelper(3, col1_output, k, 1, output_type);
-              col1_output << "=" << ref_col + 1 << ";" << endl;
-
-              sparseHelper(3, col2_output, k, 2, output_type);
-              col2_output << "=";
-              d3->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              col2_output << ";" << endl;
-            }
+                    k++;
+                  }
+              }
+            if (i == 3)
+              {
+                // Use std::next_permutation() to explore all the permutations of the 3 indices
+                vector<int> idx3{getDynJacobianCol(vidx[1]), getDynJacobianCol(vidx[2]), getDynJacobianCol(vidx[3])};
+                sort(idx3.begin(), idx3.end());
 
-          // Compute the column numbers for the 5 other permutations of (id1,id2,id3)
-          // and store them in a set (to avoid duplicates if two indexes are equal)
-          set<int> cols;
-          cols.insert(id1 * hessianColsNbr + id3 * dynJacobianColsNbr + id2);
-          cols.insert(id2 * hessianColsNbr + id1 * dynJacobianColsNbr + id3);
-          cols.insert(id2 * hessianColsNbr + id3 * dynJacobianColsNbr + id1);
-          cols.insert(id3 * hessianColsNbr + id1 * dynJacobianColsNbr + id2);
-          cols.insert(id3 * hessianColsNbr + id2 * dynJacobianColsNbr + id1);
-
-          int k2 = 1; // Keeps the offset of the permutation relative to k
-          for (int col : cols)
-            if (col != ref_col)
-              if (output_type == ExprNodeOutputType::juliaDynamicModel)
-                third_derivatives_output << "    @inbounds g3[" << eq + 1 << "," << col + 1 << "] = "
-                                         << for_sym.str() << endl;
-              else
-                {
-                  sparseHelper(3, col0_output, k+k2, 0, output_type);
-                  col0_output << "=" << eq + 1 << ";" << endl;
+                int k2 = 0; // Keeps the offset of the permutation relative to k
+                do
+                  {
+                    int col_idx_sym = idx3[0]*hessianColsNbr + idx3[1]*dynJacobianColsNbr + idx3[2];
+                    if (col_idx_sym != col_idx)
+                      if (output_type == ExprNodeOutputType::juliaDynamicModel)
+                        d_output[3] << "    @inbounds g3[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
+                                    << "g3[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
+                      else
+                        {
+                          sparseHelper(3, col0_output, k+k2, 0, output_type);
+                          col0_output << "=" << eq + 1 << ";" << endl;
 
-                  sparseHelper(3, col1_output, k+k2, 1, output_type);
-                  col1_output << "=" << col + 1 << ";" << endl;
+                          sparseHelper(3, col1_output, k+k2, 1, output_type);
+                          col1_output << "=" << col_idx_sym + 1 << ";" << endl;
 
-                  sparseHelper(3, col2_output, k+k2, 2, output_type);
-                  col2_output << "=";
-                  sparseHelper(3, col2_output, k, 2, output_type);
-                  col2_output << ";" << endl;
+                          sparseHelper(3, col2_output, k+k2, 2, output_type);
+                          col2_output << "=";
+                          sparseHelper(3, col2_output, k-1, 2, output_type);
+                          col2_output << ";" << endl;
 
-                  k2++;
-                }
-          k += k2;
-        }
+                          k2++;
+                        }
+                  }
+                while (next_permutation(idx3.begin(), idx3.end()));
 
-      if (output_type != ExprNodeOutputType::juliaDynamicModel)
-        third_derivatives_output << col0_output.str() << col1_output.str() << col2_output.str();
-    }
+                if (output_type != ExprNodeOutputType::juliaDynamicModel)
+                  k += k2;
+              }
+          }
+        if (output_type != ExprNodeOutputType::juliaDynamicModel)
+          d_output[i] << col0_output.str() << col1_output.str() << col2_output.str();
+      }
 
   if (output_type == ExprNodeOutputType::matlabDynamicModel)
     {
       // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201
       map<string, string> tmp_paren_vars;
       bool message_printed = false;
-      fixNestedParenthesis(model_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(model_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(jacobian_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(jacobian_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(hessian_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(hessian_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(third_derivatives_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(third_derivatives_tt_output, tmp_paren_vars, message_printed);
+      for (auto &it : tt_output)
+        fixNestedParenthesis(it, tmp_paren_vars, message_printed);
+      for (auto &it : d_output)
+        fixNestedParenthesis(it, tmp_paren_vars, message_printed);
 
       ostringstream init_output, end_output;
       init_output << "residual = zeros(" << nrows << ", 1);";
@@ -2695,102 +2638,73 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
                               "dynamic_resid_tt",
                               temporary_terms_mlv.size() + temporary_terms_derivatives[0].size(),
                               "", init_output, end_output,
-                              model_output, model_tt_output);
+                              d_output[0], tt_output[0]);
 
-      init_output.str(string());
-      init_output.clear();
+      init_output.str("");
       init_output << "g1 = zeros(" << nrows << ", " << dynJacobianColsNbr << ");";
       writeDynamicModelHelper(basename, "dynamic_g1", "g1",
                               "dynamic_g1_tt",
                               temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size(),
                               "dynamic_resid_tt",
                               init_output, end_output,
-                              jacobian_output, jacobian_tt_output);
+                              d_output[1], tt_output[1]);
       writeWrapperFunctions(basename, "g1");
 
-      init_output.str(string());
-      init_output.clear();
-      if (derivatives[2].size())
+      // For order ≥ 2
+      int ncols = dynJacobianColsNbr;
+      int ntt = temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size();
+      for (size_t i = 2; i < derivatives.size(); i++)
         {
-          init_output << "v2 = zeros(" << NNZDerivatives[2] << ",3);";
-          end_output << "g2 = sparse(v2(:,1),v2(:,2),v2(:,3)," << nrows << "," << hessianColsNbr << ");";
-        }
-      else
-        init_output << "g2 = sparse([],[],[]," << nrows << "," << hessianColsNbr << ");";
-      writeDynamicModelHelper(basename, "dynamic_g2", "g2",
-                              "dynamic_g2_tt",
-                              temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
-                              + temporary_terms_derivatives[2].size(),
-                              "dynamic_g1_tt",
-                              init_output, end_output,
-                              hessian_output, hessian_tt_output);
-      writeWrapperFunctions(basename, "g2");
-
-      init_output.str(string());
-      init_output.clear();
-      end_output.str(string());
-      end_output.clear();
-      int ncols = hessianColsNbr * dynJacobianColsNbr;
-      if (derivatives[3].size())
-        {
-          init_output << "v3 = zeros(" << NNZDerivatives[3] << ",3);";
-          end_output << "g3 = sparse(v3(:,1),v3(:,2),v3(:,3)," << nrows << "," << ncols << ");";
+          ncols *= dynJacobianColsNbr;
+          ntt += temporary_terms_derivatives[i].size();
+          string gname = "g" + to_string(i);
+          string vname = "v" + to_string(i);
+          string gprevname = "g" + to_string(i-1);
+
+          init_output.str("");
+          end_output.str("");
+          if (derivatives[i].size())
+            {
+              init_output << vname << " = zeros(" << NNZDerivatives[i] << ",3);";
+              end_output << gname << " = sparse("
+                         << vname << "(:,1),"
+                         << vname << "(:,2),"
+                         << vname << "(:,3),"
+                         << nrows << "," << ncols << ");";
+            }
+          else
+            init_output << gname << " = sparse([],[],[]," << nrows << "," << ncols << ");";
+          writeDynamicModelHelper(basename, "dynamic_" + gname, gname,
+                                  "dynamic_" + gname + "_tt",
+                                  ntt,
+                                  "dynamic_" + gprevname + "_tt",
+                                  init_output, end_output,
+                                  d_output[i], tt_output[i]);
+          if (i <= 3)
+            writeWrapperFunctions(basename, gname);
         }
-      else
-        init_output << "g3 = sparse([],[],[]," << nrows << "," << ncols << ");";
-      writeDynamicModelHelper(basename, "dynamic_g3", "g3",
-                              "dynamic_g3_tt",
-                              temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
-                              + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size(),
-                              "dynamic_g2_tt",
-                              init_output, end_output,
-                              third_derivatives_output, third_derivatives_tt_output);
-      writeWrapperFunctions(basename, "g3");
 
       writeDynamicMatlabCompatLayer(basename);
     }
   else if (output_type == ExprNodeOutputType::CDynamicModel)
     {
-      DynamicOutput << "void dynamic_resid_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
-                    << "{" << endl
-                    << model_tt_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_resid(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *residual)" << endl
-                    << "{" << endl
-                    << "  double lhs, rhs;" << endl
-                    << model_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g1_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
-                    << "{" << endl
-                    << jacobian_tt_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g1(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *g1)" << endl
-                    << "{" << endl
-                    << jacobian_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g2_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
-                    << "{" << endl
-                    << hessian_tt_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g2(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *v2)" << endl
-                    << "{" << endl
-                    << hessian_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g3_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
-                    << "{" << endl
-                    << third_derivatives_tt_output.str()
-                    << "}" << endl
-                    << endl
-                    << "void dynamic_g3(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *v3)" << endl
-                    << "{" << endl
-                    << third_derivatives_output.str()
-                    << "}" << endl;
+      for (size_t i = 0; i < d_output.size(); i++)
+        {
+          string funcname = i == 0 ? "resid" : "g" + to_string(i);
+          string argname = i == 0 ? "residual" : i == 1 ? "g1" : "v" + to_string(i);
+          DynamicOutput << "void dynamic_" << funcname << "_tt(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, double *T)" << endl
+                        << "{" << endl
+                        << tt_output[i].str()
+                        << "}" << endl
+                        << endl
+                        << "void dynamic_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *steady_state, int it_, const double *T, double *" << argname << ")" << endl
+                        << "{" << endl;
+          if (i == 0)
+            DynamicOutput << "  double lhs, rhs;" << endl;
+          DynamicOutput << d_output[i].str()
+                        << "}" << endl
+                        << endl;
+        }
     }
   else
     {
@@ -2866,7 +2780,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
       output << "function dynamicResidTT!(T::Vector{Float64}," << endl
              << "                         y::Vector{Float64}, x::Matrix{Float64}, "
              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
-             << model_tt_output.str()
+             << tt_output[0].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2881,7 +2795,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "    if T_flag" << endl
              << "        dynamicResidTT!(T, y, x, params, steady_state, it_)" << endl
              << "    end" << endl
-             << model_output.str()
+             << d_output[0].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2890,7 +2804,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
              << "    dynamicResidTT!(T, y, x, params, steady_state, it_)" << endl
-             << jacobian_tt_output.str()
+             << tt_output[1].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2907,7 +2821,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "        dynamicG1TT!(T, y, x, params, steady_state, it_)" << endl
              << "    end" << endl
              << "    fill!(g1, 0.0)" << endl
-             << jacobian_output.str()
+             << d_output[1].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2916,7 +2830,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
              << "    dynamicG1TT!(T, y, x, params, steady_state, it_)" << endl
-             << hessian_tt_output.str()
+             << tt_output[2].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2932,7 +2846,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "        dynamicG2TT!(T, y, x, params, steady_state, it_)" << endl
              << "    end" << endl
              << "    fill!(g2, 0.0)" << endl
-             << hessian_output.str()
+             << d_output[2].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2941,7 +2855,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "                      y::Vector{Float64}, x::Matrix{Float64}, "
              << "params::Vector{Float64}, steady_state::Vector{Float64}, it_::Int)" << endl
              << "    dynamicG2TT!(T, y, x, params, steady_state, it_)" << endl
-             << third_derivatives_tt_output.str()
+             << tt_output[3].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2959,7 +2873,7 @@ DynamicModel::writeDynamicModel(const string &basename, ostream &DynamicOutput,
              << "      dynamicG3TT!(T, y, x, params, steady_state, it_)" << endl
              << "    end" << endl
              << "    fill!(g3, 0.0)" << endl
-             << third_derivatives_output.str()
+             << d_output[3].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -3096,11 +3010,12 @@ DynamicModel::writeOutput(ostream &output, const string &basename, bool block_de
          << modstruct << "nspred   = " << npred+nboth   << ";" << endl
          << modstruct << "ndynamic   = " << npred+nboth+nfwrd << ";" << endl;
   if (!julia)
-    output << modstruct << "dynamic_tmp_nbr = zeros(4,1); % Number of temporaries used for the dynamic model" << endl
-           << modstruct << "dynamic_tmp_nbr(1) = " << temporary_terms_derivatives[0].size() << "; % Number of temporaries used for the evaluation of the residuals" << endl
-           << modstruct << "dynamic_tmp_nbr(2) = " << temporary_terms_derivatives[1].size() << "; % Number of temporaries used for the evaluation of g1 (jacobian)" << endl
-           << modstruct << "dynamic_tmp_nbr(3) = " << temporary_terms_derivatives[2].size() << "; % Number of temporaries used for the evaluation of g2 (hessian)" << endl
-           << modstruct << "dynamic_tmp_nbr(4) = " << temporary_terms_derivatives[3].size() << "; % Number of temporaries used for the evaluation of g3 (third order derivatives)" << endl;
+    {
+      output << modstruct << "dynamic_tmp_nbr = [";
+      for (size_t i = 0; i < temporary_terms_derivatives.size(); i++)
+        output << temporary_terms_derivatives[i].size() << "; ";
+      output << "];" << endl;
+    }
 
   // Write equation tags
   if (julia)
@@ -3597,17 +3512,9 @@ DynamicModel::writeOutput(ostream &output, const string &basename, bool block_de
 
   // Write number of non-zero derivatives
   // Use -1 if the derivatives have not been computed
-  output << modstruct << (julia ? "nnzderivatives" : "NNZDerivatives")
-         << " = [" << NNZDerivatives[1] << "; ";
-  if (order > 1)
-    output << NNZDerivatives[2] << "; ";
-  else
-    output << "-1; ";
-
-  if (order > 2)
-    output << NNZDerivatives[3];
-  else
-    output << "-1";
+  output << modstruct << (julia ? "nnzderivatives" : "NNZDerivatives") << " = [";
+  for (int i = 1; i < static_cast<int>(NNZDerivatives.size()); i++)
+    output << (i > order ? -1 : NNZDerivatives[i]) << "; ";
   output << "];" << endl;
 
   // Write Pac Model Consistent Expectation parameter info
diff --git a/src/ModFile.cc b/src/ModFile.cc
index ce5867f4fed8d1456f68ff519201526526afc114..5c78c8076fb57a98cb4fd07426c9178f410e45bd 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -750,7 +750,7 @@ ModFile::computingPass(bool no_tmp_terms, FileOutputType output, int params_deri
                   || mod_file_struct.ramsey_model_present || mod_file_struct.identification_present
                   || mod_file_struct.calib_smoother_present)
                 dynamic_model.set_cutoff_to_zero();
-              if (mod_file_struct.order_option < 1 || mod_file_struct.order_option > 3)
+              if (mod_file_struct.order_option < 1)
                 {
                   cerr << "ERROR: Incorrect order option..." << endl;
                   exit(EXIT_FAILURE);
diff --git a/src/ModelTree.cc b/src/ModelTree.cc
index 9db06a0f6e6c0f7e22c635007a31b17fc32129bf..6322e0ccf3735db20e1182ec54c71502b595ca33 100644
--- a/src/ModelTree.cc
+++ b/src/ModelTree.cc
@@ -1276,12 +1276,12 @@ ModelTree::computeDerivatives(int order, const set<int> &vars)
   assert (order >= 1);
 
   // Do not shrink the vectors, since they have a minimal size of 4 (see constructor)
-  derivatives.resize(max(static_cast<size_t>(order), derivatives.size()));
-  NNZDerivatives.resize(max(static_cast<size_t>(order), NNZDerivatives.size()), 0);
+  derivatives.resize(max(static_cast<size_t>(order+1), derivatives.size()));
+  NNZDerivatives.resize(max(static_cast<size_t>(order+1), NNZDerivatives.size()), 0);
 
   // First-order derivatives
   for (int var : vars)
-    for (int eq = 0; eq < (int) equations.size(); eq++)
+    for (int eq = 0; eq < static_cast<int>(equations.size()); eq++)
       {
         expr_t d1 = equations[eq]->getDerivative(var);
         if (d1 == Zero)
@@ -1306,9 +1306,15 @@ ModelTree::computeDerivatives(int order, const set<int> &vars)
           indices.push_back(var);
           // At this point, indices of endogenous variables are sorted in non-decreasing order
           derivatives[o][indices] = d;
-          do
+          // We output symmetries at order ≤ 3
+          if (o <= 3)
+            {
+              do
+                NNZDerivatives[o]++;
+              while (next_permutation(next(indices.begin()), indices.end()));
+            }
+          else
             NNZDerivatives[o]++;
-          while (next_permutation(next(indices.begin()), indices.end()));
         }
 }
 
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 6e093cb2205c8b067d9c280b013e7680d0c5d2f6..9ed4e05a6767cddd9e774d4f59a19e1f995e515c 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -1459,15 +1459,8 @@ void
 StaticModel::writeStaticModel(const string &basename,
                               ostream &StaticOutput, bool use_dll, bool julia) const
 {
-  ostringstream model_tt_output;             // Used for storing model temp vars
-  ostringstream model_output;                // Used for storing model equations
-  ostringstream jacobian_tt_output;          // Used for storing jacobian temp vars
-  ostringstream jacobian_output;             // Used for storing jacobian equations
-  ostringstream hessian_tt_output;           // Used for storing Hessian temp vars
-  ostringstream hessian_output;              // Used for storing Hessian equations
-  ostringstream third_derivatives_tt_output; // Used for storing third order derivatives temp terms
-  ostringstream third_derivatives_output;    // Used for storing third order derivatives equations
-  ostringstream for_sym;
+  vector<ostringstream> d_output(derivatives.size());  // Derivatives output (at all orders, including 0=residual)
+  vector<ostringstream> tt_output(derivatives.size()); // Temp terms output (at all orders)
 
   ExprNodeOutputType output_type = (use_dll ? ExprNodeOutputType::CStaticModel :
                                     julia ? ExprNodeOutputType::juliaStaticModel : ExprNodeOutputType::matlabStaticModel);
@@ -1476,219 +1469,168 @@ StaticModel::writeStaticModel(const string &basename,
   temporary_terms_t temp_term_union;
 
   writeModelLocalVariableTemporaryTerms(temp_term_union, temporary_terms_idxs,
-                                        model_tt_output, output_type, tef_terms);
+                                        tt_output[0], output_type, tef_terms);
 
   writeTemporaryTerms(temporary_terms_derivatives[0],
                       temp_term_union,
                       temporary_terms_idxs,
-                      model_tt_output, output_type, tef_terms);
+                      tt_output[0], output_type, tef_terms);
 
-  writeModelEquations(model_output, output_type, temp_term_union);
+  writeModelEquations(d_output[0], output_type, temp_term_union);
 
   int nrows = equations.size();
   int JacobianColsNbr = symbol_table.endo_nbr();
   int hessianColsNbr = JacobianColsNbr*JacobianColsNbr;
 
+  auto getJacobCol = [this](int var) { return symbol_table.getTypeSpecificID(getSymbIDByDerivID(var)); };;
+
   // Write Jacobian w.r. to endogenous only
   if (!derivatives[1].empty())
     {
       writeTemporaryTerms(temporary_terms_derivatives[1],
                           temp_term_union,
                           temporary_terms_idxs,
-                          jacobian_tt_output, output_type, tef_terms);
+                          tt_output[1], output_type, tef_terms);
 
       for (const auto & first_derivative : derivatives[1])
         {
           int eq, var;
           tie(eq, var) = vectorToTuple<2>(first_derivative.first);
           expr_t d1 = first_derivative.second;
-          int symb_id = getSymbIDByDerivID(var);
 
-          jacobianHelper(jacobian_output, eq, symbol_table.getTypeSpecificID(symb_id), output_type);
-          jacobian_output << "=";
-          d1->writeOutput(jacobian_output, output_type,
+          jacobianHelper(d_output[1], eq, getJacobCol(var), output_type);
+          d_output[1] << "=";
+          d1->writeOutput(d_output[1], output_type,
                           temp_term_union, temporary_terms_idxs, tef_terms);
-          jacobian_output << ";" << endl;
+          d_output[1] << ";" << endl;
         }
     }
 
-  int g2ncols = symbol_table.endo_nbr() * symbol_table.endo_nbr();
-  // Write Hessian w.r. to endogenous only (only if 2nd order derivatives have been computed)
-  if (!derivatives[2].empty())
-    {
-      writeTemporaryTerms(temporary_terms_derivatives[2],
-                          temp_term_union,
-                          temporary_terms_idxs,
-                          hessian_tt_output, output_type, tef_terms);
-
-      /* When creating the sparse matrix (in MATLAB or C mode), since storage
-         is in column-major order, output the first column, then the second,
-         then the third. This gives a significant performance boost in use_dll
-         mode (at both compilation and runtime), because it facilitates memory
-         accesses and expression reusage. */
-      ostringstream col0_output, col1_output, col2_output;
-
-      int k = 0; // Keep the line of a 2nd derivative in v2
-      for (const auto & second_derivative : derivatives[2])
-        {
-          int eq, var1, var2;
-          tie(eq, var1, var2) = vectorToTuple<3>(second_derivative.first);
-          expr_t d2 = second_derivative.second;
-
-          int symb_id1 = getSymbIDByDerivID(var1);
-          int symb_id2 = getSymbIDByDerivID(var2);
-
-          int tsid1 = symbol_table.getTypeSpecificID(symb_id1);
-          int tsid2 = symbol_table.getTypeSpecificID(symb_id2);
-
-          int col_nb = tsid1*symbol_table.endo_nbr()+tsid2;
-          int col_nb_sym = tsid2*symbol_table.endo_nbr()+tsid1;
-
-          if (output_type == ExprNodeOutputType::juliaStaticModel)
-            {
-              for_sym << "g2[" << eq + 1 << "," << col_nb + 1 << "]";
-              hessian_output << "  @inbounds " << for_sym.str() << " = ";
-              d2->writeOutput(hessian_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              hessian_output << endl;
-            }
-          else
-            {
-              sparseHelper(2, col0_output, k, 0, output_type);
-              col0_output << "=" << eq + 1 << ";" << endl;
-
-              sparseHelper(2, col1_output, k, 1, output_type);
-              col1_output << "=" << col_nb + 1 << ";" << endl;
-
-              sparseHelper(2, col2_output, k, 2, output_type);
-              col2_output << "=";
-              d2->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              col2_output << ";" << endl;
+  // Write derivatives for order ≥ 2
+  for (size_t i = 2; i < derivatives.size(); i++)
+    if (!derivatives[i].empty())
+      {
+        writeTemporaryTerms(temporary_terms_derivatives[i],
+                            temp_term_union,
+                            temporary_terms_idxs,
+                            tt_output[i], output_type, tef_terms);
+
+        /* When creating the sparse matrix (in MATLAB or C mode), since storage
+           is in column-major order, output the first column, then the second,
+           then the third. This gives a significant performance boost in use_dll
+           mode (at both compilation and runtime), because it facilitates memory
+           accesses and expression reusage. */
+        ostringstream col0_output, col1_output, col2_output;
+
+        int k = 0; // Current line index in the 3-column matrix
+        for (const auto &dit : derivatives[i])
+          {
+            const vector<int> &vidx = dit.first;
+            expr_t d = dit.second;
+            int eq = vidx[0];
 
-              k++;
-            }
+            int col_idx = 0;
+            for (size_t j = 1; j < vidx.size(); j++)
+              {
+                col_idx *= JacobianColsNbr;
+                col_idx += getJacobCol(vidx[j]);
+              }
 
-          // Treating symetric elements
-          if (symb_id1 != symb_id2)
             if (output_type == ExprNodeOutputType::juliaStaticModel)
-              hessian_output << "  @inbounds g2[" << eq + 1 << "," << col_nb_sym + 1 << "] = "
-                             << for_sym.str() << endl;
+              {
+                d_output[i] << "    @inbounds " << "g" << i << "[" << eq + 1 << "," << col_idx + 1 << "] = ";
+                d->writeOutput(d_output[i], output_type, temp_term_union, temporary_terms_idxs, tef_terms);
+                d_output[i] << endl;
+              }
             else
               {
-                sparseHelper(2, col0_output, k, 0, output_type);
+                sparseHelper(i, col0_output, k, 0, output_type);
                 col0_output << "=" << eq + 1 << ";" << endl;
 
-                sparseHelper(2, col1_output, k, 1, output_type);
-                col1_output << "=" << col_nb_sym + 1 << ";" << endl;
+                sparseHelper(i, col1_output, k, 1, output_type);
+                col1_output << "=" << col_idx + 1 << ";" << endl;
 
-                sparseHelper(2, col2_output, k, 2, output_type);
+                sparseHelper(i, col2_output, k, 2, output_type);
                 col2_output << "=";
-                sparseHelper(2, col2_output, k-1, 2, output_type);
+                d->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
                 col2_output << ";" << endl;
 
                 k++;
               }
-        }
-
-      if (output_type != ExprNodeOutputType::juliaStaticModel)
-        hessian_output << col0_output.str() << col1_output.str() << col2_output.str();
-    }
-
-  // Writing third derivatives
-  if (!derivatives[3].empty())
-    {
-      writeTemporaryTerms(temporary_terms_derivatives[3],
-                          temp_term_union,
-                          temporary_terms_idxs,
-                          third_derivatives_tt_output, output_type, tef_terms);
-
-      // See comment above for 2nd order
-      ostringstream col0_output, col1_output, col2_output;
-
-      int k = 0; // Keep the line of a 3rd derivative in v3
-      for (const auto & third_derivative : derivatives[3])
-        {
-          int eq, var1, var2, var3;
-          tie(eq, var1, var2, var3) = vectorToTuple<4>(third_derivative.first);
-          expr_t d3 = third_derivative.second;
-
-          int id1 = getSymbIDByDerivID(var1);
-          int id2 = getSymbIDByDerivID(var2);
-          int id3 = getSymbIDByDerivID(var3);
-
-          // Reference column number for the g3 matrix
-          int ref_col = id1 * hessianColsNbr + id2 * JacobianColsNbr + id3;
 
-          if (output_type == ExprNodeOutputType::juliaStaticModel)
-            {
-              for_sym << "g3[" << eq + 1 << "," << ref_col + 1 << "]";
-              third_derivatives_output << "  @inbounds " << for_sym.str() << " = ";
-              d3->writeOutput(third_derivatives_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              third_derivatives_output << endl;
-            }
-          else
-            {
-              sparseHelper(3, col0_output, k, 0, output_type);
-              col0_output << "=" << eq + 1 << ";" << endl;
-
-              sparseHelper(3, col1_output, k, 1, output_type);
-              col1_output << "=" << ref_col + 1 << ";" << endl;
-
-              sparseHelper(3, col2_output, k, 2, output_type);
-              col2_output << "=";
-              d3->writeOutput(col2_output, output_type, temp_term_union, temporary_terms_idxs, tef_terms);
-              col2_output << ";" << endl;
-            }
-
-          // Compute the column numbers for the 5 other permutations of (id1,id2,id3)
-          // and store them in a set (to avoid duplicates if two indexes are equal)
-          set<int> cols;
-          cols.insert(id1 * hessianColsNbr + id3 * JacobianColsNbr + id2);
-          cols.insert(id2 * hessianColsNbr + id1 * JacobianColsNbr + id3);
-          cols.insert(id2 * hessianColsNbr + id3 * JacobianColsNbr + id1);
-          cols.insert(id3 * hessianColsNbr + id1 * JacobianColsNbr + id2);
-          cols.insert(id3 * hessianColsNbr + id2 * JacobianColsNbr + id1);
-
-          int k2 = 1; // Keeps the offset of the permutation relative to k
-          for (int col : cols)
-            if (col != ref_col)
-              if (output_type == ExprNodeOutputType::juliaStaticModel)
-                third_derivatives_output << "  @inbounds g3[" << eq + 1 << "," << col + 1 << "] = "
-                                         << for_sym.str() << endl;
-              else
-                {
-                  sparseHelper(3, col0_output, k+k2, 0, output_type);
-                  col0_output << "=" << eq + 1 << ";" << endl;
+            // Output symetric elements, but only at order 2 and 3
+            if (i == 2 && vidx[1] != vidx[2])
+              {
+                int col_idx_sym = getJacobCol(vidx[2]) * JacobianColsNbr + getJacobCol(vidx[1]);
+
+                if (output_type == ExprNodeOutputType::juliaStaticModel)
+                  d_output[2] << "    @inbounds g2[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
+                              << "g2[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
+                else
+                  {
+                    sparseHelper(2, col0_output, k, 0, output_type);
+                    col0_output << "=" << eq + 1 << ";" << endl;
+
+                    sparseHelper(2, col1_output, k, 1, output_type);
+                    col1_output << "=" << col_idx_sym + 1 << ";" << endl;
+
+                    sparseHelper(2, col2_output, k, 2, output_type);
+                    col2_output << "=";
+                    sparseHelper(2, col2_output, k-1, 2, output_type);
+                    col2_output << ";" << endl;
+
+                    k++;
+                  }
+              }
+            if (i == 3)
+              {
+                // Use std::next_permutation() to explore all the permutations of the 3 indices
+                vector<int> idx3{getJacobCol(vidx[1]), getJacobCol(vidx[2]), getJacobCol(vidx[3])};
+                sort(idx3.begin(), idx3.end());
+
+                int k2 = 0; // Keeps the offset of the permutation relative to k
+                do
+                  {
+                    int col_idx_sym = idx3[0]*hessianColsNbr + idx3[1]*JacobianColsNbr + idx3[2];
+                    if (col_idx_sym != col_idx)
+                      if (output_type == ExprNodeOutputType::juliaStaticModel)
+                        d_output[3] << "    @inbounds g3[" << eq + 1 << "," << col_idx_sym + 1 << "] = "
+                                    << "g3[" << eq + 1 << "," << col_idx + 1 << "]" << endl;
+                      else
+                        {
+                          sparseHelper(3, col0_output, k+k2, 0, output_type);
+                          col0_output << "=" << eq + 1 << ";" << endl;
 
-                  sparseHelper(3, col1_output, k+k2, 1, output_type);
-                  col1_output << "=" << col + 1 << ";" << endl;
+                          sparseHelper(3, col1_output, k+k2, 1, output_type);
+                          col1_output << "=" << col_idx_sym + 1 << ";" << endl;
 
-                  sparseHelper(3, col2_output, k+k2, 2, output_type);
-                  col2_output << "=";
-                  sparseHelper(3, col2_output, k, 2, output_type);
-                  col2_output << ";" << endl;
+                          sparseHelper(3, col2_output, k+k2, 2, output_type);
+                          col2_output << "=";
+                          sparseHelper(3, col2_output, k-1, 2, output_type);
+                          col2_output << ";" << endl;
 
-                  k2++;
-                }
-          k += k2;
-        }
+                          k2++;
+                        }
+                  }
+                while (next_permutation(idx3.begin(), idx3.end()));
 
-      if (output_type != ExprNodeOutputType::juliaStaticModel)
-        third_derivatives_output << col0_output.str() << col1_output.str() << col2_output.str();
-    }
+                if (output_type != ExprNodeOutputType::juliaStaticModel)
+                  k += k2;
+              }
+          }
+        if (output_type != ExprNodeOutputType::juliaStaticModel)
+          d_output[i] << col0_output.str() << col1_output.str() << col2_output.str();
+      }
 
   if (output_type == ExprNodeOutputType::matlabStaticModel)
     {
       // Check that we don't have more than 32 nested parenthesis because Matlab does not suppor this. See Issue #1201
       map<string, string> tmp_paren_vars;
       bool message_printed = false;
-      fixNestedParenthesis(model_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(model_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(jacobian_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(jacobian_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(hessian_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(hessian_tt_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(third_derivatives_output, tmp_paren_vars, message_printed);
-      fixNestedParenthesis(third_derivatives_tt_output, tmp_paren_vars, message_printed);
+      for (auto &it : tt_output)
+        fixNestedParenthesis(it, tmp_paren_vars, message_printed);
+      for (auto &it : d_output)
+        fixNestedParenthesis(it, tmp_paren_vars, message_printed);
 
       ostringstream init_output, end_output;
       init_output << "residual = zeros(" << equations.size() << ", 1);";
@@ -1698,12 +1640,10 @@ StaticModel::writeStaticModel(const string &basename,
       writeStaticModelHelper(basename, "static_resid", "residual", "static_resid_tt",
                              temporary_terms_mlv.size() + temporary_terms_derivatives[0].size(),
                              "", init_output, end_output,
-                             model_output, model_tt_output);
+                             d_output[0], tt_output[0]);
 
-      init_output.str(string());
-      init_output.clear();
-      end_output.str(string());
-      end_output.clear();
+      init_output.str("");
+      end_output.str("");
       init_output << "g1 = zeros(" << equations.size() << ", " << symbol_table.endo_nbr() << ");";
       end_output << "if ~isreal(g1)" << endl
                  << "    g1 = real(g1)+2*imag(g1);" << endl
@@ -1712,82 +1652,64 @@ StaticModel::writeStaticModel(const string &basename,
                              temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size(),
                              "static_resid_tt",
                              init_output, end_output,
-                             jacobian_output, jacobian_tt_output);
+                             d_output[1], tt_output[1]);
       writeWrapperFunctions(basename, "g1");
 
-      init_output.str(string());
-      init_output.clear();
-      end_output.str(string());
-      end_output.clear();
-      if (derivatives[2].size())
+      // For order ≥ 2
+      int ncols = JacobianColsNbr;
+      int ntt = temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size();
+      for (size_t i = 2; i < derivatives.size(); i++)
         {
-          init_output << "v2 = zeros(" << NNZDerivatives[2] << ",3);";
-          end_output << "g2 = sparse(v2(:,1),v2(:,2),v2(:,3)," << equations.size() << "," << g2ncols << ");";
-        }
-      else
-        init_output << "g2 = sparse([],[],[]," << equations.size() << "," << g2ncols << ");";
-      writeStaticModelHelper(basename, "static_g2", "g2", "static_g2_tt",
-                             temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
-                             + temporary_terms_derivatives[2].size(),
-                             "static_g1_tt",
-                             init_output, end_output,
-                             hessian_output, hessian_tt_output);
-      writeWrapperFunctions(basename, "g2");
-
-      init_output.str(string());
-      init_output.clear();
-      end_output.str(string());
-      end_output.clear();
-      int ncols = hessianColsNbr * JacobianColsNbr;
-      if (derivatives[3].size())
-        {
-          init_output << "v3 = zeros(" << NNZDerivatives[3] << ",3);";
-          end_output << "g3 = sparse(v3(:,1),v3(:,2),v3(:,3)," << nrows << "," << ncols << ");";
+          ncols *= JacobianColsNbr;
+          ntt += temporary_terms_derivatives[i].size();
+          string gname = "g" + to_string(i);
+          string vname = "v" + to_string(i);
+          string gprevname = "g" + to_string(i-1);
+
+          init_output.str("");
+          end_output.str("");
+          if (derivatives[i].size())
+            {
+              init_output << vname << " = zeros(" << NNZDerivatives[i] << ",3);";
+              end_output << gname << " = sparse("
+                         << vname << "(:,1),"
+                         << vname << "(:,2),"
+                         << vname << "(:,3),"
+                         << nrows << "," << ncols << ");";
+            }
+          else
+            init_output << gname << " = sparse([],[],[]," << nrows << "," << ncols << ");";
+          writeStaticModelHelper(basename, "static_" + gname, gname,
+                                 "static_" + gname + "_tt",
+                                 ntt,
+                                 "static_" + gprevname + "_tt",
+                                 init_output, end_output,
+                                 d_output[i], tt_output[i]);
+          if (i <= 3)
+            writeWrapperFunctions(basename, gname);
         }
-      else
-        init_output << "g3 = sparse([],[],[]," << nrows << "," << ncols << ");";
-      writeStaticModelHelper(basename, "static_g3", "g3", "static_g3_tt",
-                             temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size()
-                             + temporary_terms_derivatives[2].size() + temporary_terms_derivatives[3].size(),
-                             "static_g2_tt",
-                             init_output, end_output,
-                             third_derivatives_output, third_derivatives_tt_output);
-      writeWrapperFunctions(basename, "g3");
 
       writeStaticMatlabCompatLayer(basename);
     }
   else if (output_type == ExprNodeOutputType::CStaticModel)
     {
-      StaticOutput << "void static_resid_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T)" << endl
-                   << "{" << endl
-                   << model_tt_output.str()
-                   << "}" << endl
-                   << endl
-                   << "void static_resid(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *residual)" << endl
-                   << "{" << endl
-                   << "  double lhs, rhs;" << endl
-                   << model_output.str()
-                   << "}" << endl
-                   << endl
-                   << "void static_g1_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T)" << endl
-                   << "{" << endl
-                   << jacobian_tt_output.str()
-                   << "}" << endl
-                   << endl
-                   << "void static_g1(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *g1)" << endl
-                   << "{" << endl
-                   << jacobian_output.str()
-                   << "}" << endl
-                   << endl
-                   << "void static_g2_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T)" << endl
-                   << "{" << endl
-                   << hessian_tt_output.str()
-                   << "}" << endl
-                   << endl
-                   << "void static_g2(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *v2)" << endl
-                   << "{" << endl
-                   << hessian_output.str()
-                   << "}" << endl;
+      for (size_t i = 0; i < d_output.size(); i++)
+        {
+          string funcname = i == 0 ? "resid" : "g" + to_string(i);
+          string argname = i == 0 ? "residual" : i == 1 ? "g1" : "v" + to_string(i);
+          StaticOutput << "void static_" << funcname << "_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T)" << endl
+                       << "{" << endl
+                       << tt_output[i].str()
+                       << "}" << endl
+                       << endl
+                       << "void static_" << funcname << "(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *" << argname << ")" << endl
+                       << "{" << endl;
+          if (i == 0)
+            StaticOutput << "  double lhs, rhs;" << endl;
+          StaticOutput << d_output[i].str()
+                       << "}" << endl
+                       << endl;
+        }
     }
   else
     {
@@ -1862,7 +1784,7 @@ StaticModel::writeStaticModel(const string &basename,
       output << "function staticResidTT!(T::Vector{Float64}," << endl
              << "                        y::Vector{Float64}, x::Vector{Float64}, params::Vector{Float64})" << endl
              << "    @assert length(T) >= " << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size()  << endl
-             << model_tt_output.str()
+             << tt_output[0].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -1876,7 +1798,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "    if T0_flag" << endl
              << "        staticResidTT!(T, y, x, params)" << endl
              << "    end" << endl
-             << model_output.str()
+             << d_output[0].str()
              << "    if ~isreal(residual)" << endl
              << "        residual = real(residual)+imag(residual).^2;" << endl
              << "    end" << endl
@@ -1889,7 +1811,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "    if T0_flag" << endl
              << "        staticResidTT!(T, y, x, params)" << endl
              << "    end" << endl
-             << jacobian_tt_output.str()
+             << tt_output[1].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -1906,7 +1828,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "        staticG1TT!(T, y, x, params, T0_flag)" << endl
              << "    end" << endl
              << "    fill!(g1, 0.0)" << endl
-             << jacobian_output.str()
+             << d_output[1].str()
              << "    if ~isreal(g1)" << endl
              << "        g1 = real(g1)+2*imag(g1);" << endl
              << "    end" << endl
@@ -1919,7 +1841,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "    if T1_flag" << endl
              << "        staticG1TT!(T, y, x, params, TO_flag)" << endl
              << "    end" << endl
-             << hessian_tt_output.str()
+             << tt_output[2].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -1928,7 +1850,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "                   y::Vector{Float64}, x::Vector{Float64}, params::Vector{Float64}, T2_flag::Bool, T1_flag::Bool, T0_flag::Bool)" << endl
              << "    @assert length(T) >= "
              << temporary_terms_mlv.size() + temporary_terms_derivatives[0].size() + temporary_terms_derivatives[1].size() + temporary_terms_derivatives[2].size() << endl
-             << "    @assert size(g2) == (" << equations.size() << ", " << g2ncols << ")" << endl
+             << "    @assert size(g2) == (" << equations.size() << ", " << hessianColsNbr << ")" << endl
              << "    @assert length(y) == " << symbol_table.endo_nbr() << endl
              << "    @assert length(x) == " << symbol_table.exo_nbr() << endl
              << "    @assert length(params) == " << symbol_table.param_nbr() << endl
@@ -1936,7 +1858,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "        staticG2TT!(T, y, x, params, T1_flag, T0_flag)" << endl
              << "    end" << endl
              << "    fill!(g2, 0.0)" << endl
-             << hessian_output.str()
+             << d_output[2].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -1946,7 +1868,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "    if T2_flag" << endl
              << "        staticG2TT!(T, y, x, params, T1_flag, T0_flag)" << endl
              << "    end" << endl
-             << third_derivatives_tt_output.str()
+             << tt_output[3].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -1964,7 +1886,7 @@ StaticModel::writeStaticModel(const string &basename,
              << "        staticG3TT!(T, y, x, params, T2_flag, T1_flag, T0_flag)" << endl
              << "    end" << endl
              << "    fill!(g3, 0.0)" << endl
-             << third_derivatives_output.str()
+             << d_output[3].str()
              << "    return nothing" << endl
              << "end" << endl << endl;
 
@@ -2069,7 +1991,6 @@ StaticModel::writeStaticCFile(const string &basename) const
          << "#include <uchar.h>" << endl // For MATLAB ≤ R2011a
          << R"(#include "mex.h")" << endl
          << endl
-         << "const int ntt = " << ntt << ";" << endl
          << "void static_resid_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T);" << endl
          << "void static_resid(const double *y, const double *x, int nb_row_x, const double *params, const double *T, double *residual);" << endl
          << "void static_g1_tt(const double *y, const double *x, int nb_row_x, const double *params, double *T);" << endl
@@ -2090,7 +2011,7 @@ StaticModel::writeStaticCFile(const string &basename) const
          << "  /* Gets number of rows of matrix x. */" << endl
          << "  int nb_row_x = mxGetM(prhs[1]);" << endl
          << endl
-         << "  double *T = (double *) malloc(sizeof(double)*ntt);"
+         << "  double *T = (double *) malloc(sizeof(double)*" << ntt << ");"
          << endl
          << "  if (nlhs >= 1)" << endl
          << "    {" << endl
@@ -2216,11 +2137,10 @@ StaticModel::writeStaticBlockMFSFile(const string &basename) const
 void
 StaticModel::writeOutput(ostream &output, bool block) const
 {
-  output << "M_.static_tmp_nbr = zeros(4,1); % Number of temporaries used for the static model" <<endl
-         << "M_.static_tmp_nbr(1) = " << temporary_terms_derivatives[0].size() << "; % Number of temporaries used for the evaluation of the residuals" << endl
-         << "M_.static_tmp_nbr(2) = " << temporary_terms_derivatives[1].size() << "; % Number of temporaries used for the evaluation of g1 (jacobian)" << endl
-         << "M_.static_tmp_nbr(3) = " << temporary_terms_derivatives[2].size() << "; % Number of temporaries used for the evaluation of g2 (hessian)" << endl
-         << "M_.static_tmp_nbr(4) = " << temporary_terms_derivatives[3].size() << "; % Number of temporaries used for the evaluation of g3 (third order derivatives)" << endl;
+  output << "M_.static_tmp_nbr = [";
+  for (size_t i = 0; i < temporary_terms_derivatives.size(); i++)
+    output << temporary_terms_derivatives[i].size() << "; ";
+  output << "];" << endl;
 
   if (!block)
     return;
@@ -2947,7 +2867,6 @@ StaticModel::writeJsonComputingPassOutput(ostream &output, bool writeDetails) co
     }
   jacobian_output << "]}";
 
-  int g2ncols = symbol_table.endo_nbr() * symbol_table.endo_nbr();
   // Write Hessian w.r. to endogenous only (only if 2nd order derivatives have been computed)
   temp_term_union_m_1 = temp_term_union;
   temp_term_union.insert(temporary_terms_derivatives[2].begin(), temporary_terms_derivatives[2].end());
@@ -2955,7 +2874,7 @@ StaticModel::writeJsonComputingPassOutput(ostream &output, bool writeDetails) co
   writeJsonTemporaryTerms(temp_term_union, temp_term_union_m_1, hessian_output, tef_terms, concat);
   hessian_output << R"(, "hessian": {)"
                  << R"(  "nrows": )" << equations.size()
-                 << R"(, "ncols": )" << g2ncols
+                 << R"(, "ncols": )" << hessianColsNbr
                  << R"(, "entries": [)";
   for (auto it = derivatives[2].begin();
        it != derivatives[2].end(); it++)