diff --git a/NAMESPACE b/NAMESPACE
index 9cce71e8cd4085adfcf0fbac57a4cceaae829314..13acd866a62846d30a130005ce5acb64ed89d50a 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -54,6 +54,8 @@ export(ProfitParticipation)
 export(applyHook)
 export(contractGrid)
 export(contractGridPremium)
+export(costs.baseAlpha)
+export(costsDisplayTable)
 export(deathBenefit.annuityDecreasing)
 export(deathBenefit.linearDecreasing)
 export(exportInsuranceContract.xlsx)
diff --git a/R/InsuranceContract.R b/R/InsuranceContract.R
index 4739898e77d13ad3cdffaa619a9a65050a763515..7922b14d674d3f3cb5b3be26e3c9a415a50bb357 100644
--- a/R/InsuranceContract.R
+++ b/R/InsuranceContract.R
@@ -909,7 +909,7 @@ InsuranceContract = R6Class(
             #### #
             # COSTS PARAMETERS: can be a function => evaluate it to get the real costs
             #### #
-            self$Parameters$Costs = private$evaluateCosts(self$Parameters$Costs)
+            self$Parameters$Costs = private$evaluateCosts()
 
             #### #
             # AGES for multiple joint lives:
@@ -953,8 +953,8 @@ InsuranceContract = R6Class(
             invisible(self)
         },
 
-        evaluateCosts = function(costs) {
-            self$tarif$getCostValues(costs, params = self$Parameters)
+        evaluateCosts = function() {
+            self$tarif$getCostValues(params = self$Parameters)
         },
 
         determineInternalValues = function(...) {
diff --git a/R/InsuranceParameters.R b/R/InsuranceParameters.R
index ef2d07a3bf3a491d1e1c475f46e2a37f6abdf57d..5962a9c1bb94cf2d47d336c77090d5f8921c7691 100644
--- a/R/InsuranceParameters.R
+++ b/R/InsuranceParameters.R
@@ -142,6 +142,41 @@ initializeCosts = function(costs, alpha, Zillmer, beta, gamma, gamma.paidUp, gam
     costs
 }
 
+#' Helper function to define base costs with base alpha, but otherwise unchanged costs
+#'
+#' Returns a function that sets base alpha (and Zillmer) costs to the given value,
+#' but otherwise uses the full costs defined by the Costs parameter.
+#'
+#' This function can be set as minCosts parameter for a tariff and makes sure
+#' that only alpha costs are modified / waived, but no other costs.
+#'
+#' @param alpha The minimum alpha / Zillmer cost that cannot be waived
+#'
+#' @export
+costs.baseAlpha = function(alpha) {
+  function(params, values, costs) {
+    costs = setCost(costs, "alpha", "SumPremiums", "once", alpha)
+    if (costs["Zillmer", "SumPremiums", "once"] != 0) {
+      costs = setCost(costs, "Zillmer", "SumPremiums", "once", alpha)
+    }
+    costs
+  }
+}
+
+
+#' Helper function to display all cost definitions in a concise table
+#'
+#' Returns a data.frame with columns
+#'
+#' @export
+costsDisplayTable = function(costs) {
+  costtable = as.data.frame.table(setInsuranceValuesLabels(costs) )
+  colnames(costtable) = c("Kostenart", "Basis", "Periode", "Kostensatz");
+  costtable[costtable[,"Kostensatz"] != 0.0000,]
+}
+
+
+
 
 #' Data structure (filled only with NULL) for insurance contract class member values.
 #' @export
@@ -186,7 +221,7 @@ InsuranceContract.Values = list(
 #'     \item \code{$ActuarialBases} ... Actuarial bases for the contract
 #'               calculation (mortality/invalidity table, guaranteed interest,
 #'               surrender penalty, etc.)
-#'     \item \code{$Costs} ... Expenses charged to the contract (see [initializeCosts()])
+#'     \item \code{$Costs}, \code{$minCosts} ... Expenses charged to the contract (see [initializeCosts()])
 #'     \item \code{$Loadings} ... Loadings, rebates and other charges of the
 #'               tariff / contract (tax, unit costs, surcharge for no medial exam, premium/benefit frequency loading)
 #'     \item \code{$Features} ... Peculiarities of the tariff (to enable
@@ -276,6 +311,12 @@ InsuranceContract.Values = list(
 #'     \item{\code{$deathBenefit}}{The yearly relative death benefit (relative
 #'               to the initial sum insured); Can be set to a \code{function(len,
 #'               params, values)}, e.g. \code{deathBenefit = deathBenefit.linearDecreasing}}
+#'
+#'    \item{\code{$costWaiver}}{The fraction of the costs that are waived (only
+#'               those cost components that are defined to be waivable, i.e. by
+#'               defining a corresponding \code{$minCosts}). Linearly interpolates
+#'               between \code{$Costs} and \code{$minCosts}, if the latter is set.
+#'               Otherwise is has no effect.}
 #' }
 #'
 #' ## Elements of sublist \code{InsuranceContract.ParameterDefault$ContractState}
@@ -343,6 +384,17 @@ InsuranceContract.Values = list(
 #'
 #' Definition of contractual costs charged to the contract. See [initializeCosts()].
 #'
+#' \describe{
+#'     \item{\code{$Costs}}{The full cost defined for the contract / tariff,
+#'               usually set with [initializeCosts()] and [setCost()]}
+#'     \item{\code{$minCosts}}{The minimum costs defined for the contract / tariff
+#'               that cannot be waived. Either an explicit cost definition structure
+#'               generated by [initializeCosts()] and [setCost()], or a
+#'               `function(params, values, costs)`, where the full costs are passed
+#'               as third parameter, so the function can modify only those cost parts
+#'               that can be waived at all. }
+#' }
+#'
 #' ## Elements of sublist \code{InsuranceContract.ParameterDefault$Loadings}
 #'
 #' \describe{
@@ -466,7 +518,9 @@ InsuranceContract.ParameterDefaults = list(
         premiumRefund = 0,                      # Proportion of premiums refunded on death (including additional risk, e.g. 1.10 = 110% of paid premiums)
         premiumIncrease = 1,                    # The yearly growth factor of the premium, i.e. 1.05 means +5% increase each year; a Vector describes the premiums for all years
         annuityIncrease = 1,                    # The yearly growth factor of the annuity payments, i.e. 1.05 means +5% incrase each year; a vector describes the annuity unit payments for all years
-        deathBenefit = 1                        # The yearly relative death benefit (relative to the initial sum insured); Can be set to a function(len, params, values), e.g. deathBenefit = deathBenefit.linearDecreasing
+        deathBenefit = 1,                        # The yearly relative death benefit (relative to the initial sum insured); Can be set to a function(len, params, values), e.g. deathBenefit = deathBenefit.linearDecreasing
+
+        costWaiver = 0                          # The cost waiver (up to minCosts, 0=no cost waiver, 1=full cost waiver down to minCosts)
     ),
     ContractState = list(
         premiumWaiver = FALSE,                  # contract is paid-up
@@ -488,6 +542,7 @@ InsuranceContract.ParameterDefaults = list(
         benefitFrequencyOrder = 0
     ),
     Costs = initializeCosts(),
+    minCosts = NULL,               # Base costs, which cannot be waived
     Loadings = list( # Loadings can also be function(sumInsured, premiums)
         ongoingAlphaGrossPremium = 0,           # Acquisition cost that increase the gross premium
         tax = 0.04,                             # insurance tax, factor on each premium paid
@@ -567,7 +622,7 @@ InsuranceContract.ParameterStructure$Loadings["premiumFrequencyLoading"] = list(
 #'                  initial parameters provided in \code{params}.
 #'
 #' @export
-InsuranceContract.ParametersFill = function(params = InsuranceContract.ParameterStructure, costs = NULL, ...) {
+InsuranceContract.ParametersFill = function(params = InsuranceContract.ParameterStructure, costs = NULL, minCosts = NULL, ...) {
     # params = InsuranceContract.ParameterStructure;
     params$ContractData = fillFields(params$ContractData, list(...));
     params$ContractState = fillFields(params$ContractState, list(...));
@@ -580,6 +635,7 @@ InsuranceContract.ParametersFill = function(params = InsuranceContract.Parameter
     # Costs are a special case, because they are an array rather than a list:
     # TODO: Find a way to partially override
     if (!missing(costs)) params$Costs = costs;
+    if (!missing(minCosts)) params$minCosts = minCosts;
     params
 }
 
@@ -618,6 +674,9 @@ InsuranceContract.ParametersFallback = function(params, fallback, ppParameters =
             params$Costs = fallback$Costs;
         }
     }
+    if (is.null(params$minCosts)) {
+      params$minCosts = fallback$minCosts;
+    }
     params
 }
 
diff --git a/R/InsuranceTarif.R b/R/InsuranceTarif.R
index 6dfc930a5be50ff8398c48d1f5fe9c17b410c388..b0fc94c798a35e26c06f8b54204baaab5c35e500 100644
--- a/R/InsuranceTarif.R
+++ b/R/InsuranceTarif.R
@@ -351,11 +351,21 @@ InsuranceTarif = R6Class(
     #' an array of the required dimensions. This function makes sures that the
     #' latter function is actually evaluated.
     #'
-    #' @param costs The cost parameter passed to the tarif definition or the
-    #' contract (either an array of the form returned by [initializeCosts()] or
-    #' a function(params, values) returning such an array)
-    getCostValues = function(costs, params) {
-        valueOrFunction(costs, params = params, values = NULL)
+    #' @param params The parameters of the contract / tariff
+    getCostValues = function(params) {
+      costs = valueOrFunction(params$Costs, params = params, values = NULL)
+      baseCost = valueOrFunction(params$minCosts, params = params, values = NULL, costs = costs)
+      if (!is.null(baseCost)) {
+        costWaiver = valueOrFunction(params$ContractData$costWaiver, params = params, values = NULL, costs = costs, minCosts = baseCost)
+        if (is.numeric(costWaiver)) {
+          costs = costs * (1 - costWaiver) + baseCost * costWaiver
+        } else if (is.boolean(costWaiver)) {
+          if (isTRUE(costWaiver)) {
+            costs = baseCost
+          }
+        }
+      }
+      costs
     },
 
     #' @description Returns the unit premium cash flow for the whole contract period.
diff --git a/R/exportInsuranceContract_xlsx.R b/R/exportInsuranceContract_xlsx.R
index 3063f2b334b4ce2425da25814b06fe3141443255..cc3d689deb644d895cec10b77d009c3b383cdf6b 100644
--- a/R/exportInsuranceContract_xlsx.R
+++ b/R/exportInsuranceContract_xlsx.R
@@ -295,10 +295,10 @@ costValuesAsDF = function(costValues) {
 exportLoadingsTable = function(wb, sheet, contract, crow, ccol, styles = styles, seprows = 3, tariffs.handled = c()) {
   tarifname = contract$tarif$tarif
   if (!(tarifname %in% tariffs.handled)) {
-    # TODO: Detect cost structures overridden at contract-level! => Currently only the default tariff costs are printed!
-    costtable = as.data.frame.table(setInsuranceValuesLabels(contract$Parameters$Costs) )
-    colnames(costtable) = c("Kostenart", "Basis", "Periode", "Kostensatz");
-    costtable = costtable[costtable[,"Kostensatz"] != 0.0000,]
+    costtable = costsDisplayTable(contract$Parameters$Costs)
+    # costtable = as.data.frame.table(setInsuranceValuesLabels(contract$Parameters$Costs) )
+    # colnames(costtable) = c("Kostenart", "Basis", "Periode", "Kostensatz");
+    # costtable = costtable[costtable[,"Kostensatz"] != 0.0000,]
     cap = sprintf("Kosten (Tarif %s)", tarifname)
     writeValuesTable(wb, sheet, costtable, crow = crow, ccol = 1, tableName = tableName("Kosten_", tarifname), styles = styles, caption = cap);
     # writeDataTable(wb, sheet, costtable, startCol = 1, startRow = crow + 1, colNames = TRUE, rowNames = FALSE,
diff --git a/man/InsuranceContract.ParameterDefaults.Rd b/man/InsuranceContract.ParameterDefaults.Rd
index 6c719aba7612ccfc68bd9e195c997cfc8ddfc9b3..118bde820c9f02aae8b73536c9659dd8a5ada6f6 100644
--- a/man/InsuranceContract.ParameterDefaults.Rd
+++ b/man/InsuranceContract.ParameterDefaults.Rd
@@ -18,7 +18,7 @@ penalty already applied, alpha costs already (partially) refunded)
 \item \code{$ActuarialBases} ... Actuarial bases for the contract
 calculation (mortality/invalidity table, guaranteed interest,
 surrender penalty, etc.)
-\item \code{$Costs} ... Expenses charged to the contract (see \code{\link[=initializeCosts]{initializeCosts()}})
+\item \code{$Costs}, \code{$minCosts} ... Expenses charged to the contract (see \code{\link[=initializeCosts]{initializeCosts()}})
 \item \code{$Loadings} ... Loadings, rebates and other charges of the
 tariff / contract (tax, unit costs, surcharge for no medial exam, premium/benefit frequency loading)
 \item \code{$Features} ... Peculiarities of the tariff (to enable
@@ -107,6 +107,12 @@ describes the annuity unit payments for all years}
 \item{\code{$deathBenefit}}{The yearly relative death benefit (relative
 to the initial sum insured); Can be set to a \code{function(len,
               params, values)}, e.g. \code{deathBenefit = deathBenefit.linearDecreasing}}
+
+\item{\code{$costWaiver}}{The fraction of the costs that are waived (only
+those cost components that are defined to be waivable, i.e. by
+defining a corresponding \code{$minCosts}). Linearly interpolates
+between \code{$Costs} and \code{$minCosts}, if the latter is set.
+Otherwise is has no effect.}
 }
 }
 
@@ -175,6 +181,17 @@ used => then leave this at 0)}
 \subsection{Elements of sublist \code{InsuranceContract.ParameterDefault$Costs}}{
 
 Definition of contractual costs charged to the contract. See \code{\link[=initializeCosts]{initializeCosts()}}.
+
+\describe{
+\item{\code{$Costs}}{The full cost defined for the contract / tariff,
+usually set with \code{\link[=initializeCosts]{initializeCosts()}} and \code{\link[=setCost]{setCost()}}}
+\item{\code{$minCosts}}{The minimum costs defined for the contract / tariff
+that cannot be waived. Either an explicit cost definition structure
+generated by \code{\link[=initializeCosts]{initializeCosts()}} and \code{\link[=setCost]{setCost()}}, or a
+\verb{function(params, values, costs)}, where the full costs are passed
+as third parameter, so the function can modify only those cost parts
+that can be waived at all. }
+}
 }
 
 \subsection{Elements of sublist \code{InsuranceContract.ParameterDefault$Loadings}}{
diff --git a/man/InsuranceContract.ParameterStructure.Rd b/man/InsuranceContract.ParameterStructure.Rd
index 60d0e8669e77dffaf3718a0abed5accd7e34b899..e02511618bc07ea13ac930e92c1a763a8fa23343 100644
--- a/man/InsuranceContract.ParameterStructure.Rd
+++ b/man/InsuranceContract.ParameterStructure.Rd
@@ -5,7 +5,7 @@
 \alias{InsuranceContract.ParameterStructure}
 \title{Full insurance contract parameter structure.}
 \format{
-An object of class \code{list} of length 8.
+An object of class \code{list} of length 9.
 }
 \usage{
 InsuranceContract.ParameterStructure
diff --git a/man/InsuranceContract.ParametersFill.Rd b/man/InsuranceContract.ParametersFill.Rd
index 2f88767baf85cfacebcbaecd5d0bee56926de1f0..e3fcb09ced3ba2b5012263c6ec613db4af71ba4e 100644
--- a/man/InsuranceContract.ParametersFill.Rd
+++ b/man/InsuranceContract.ParametersFill.Rd
@@ -7,6 +7,7 @@
 InsuranceContract.ParametersFill(
   params = InsuranceContract.ParameterStructure,
   costs = NULL,
+  minCosts = NULL,
   ...
 )
 }
diff --git a/man/InsuranceTarif.Rd b/man/InsuranceTarif.Rd
index 7798d27cb79a8e11db5fac0ab28092bc15a63b87..4dbdb556af1ff816384ed2a99abd9603f35e84c3 100644
--- a/man/InsuranceTarif.Rd
+++ b/man/InsuranceTarif.Rd
@@ -408,19 +408,13 @@ Not to be called directly, but implicitly by the \link{InsuranceContract} object
 Obtain the cost structure from the cost parameter and the
 given paremeter set
 \subsection{Usage}{
-\if{html}{\out{<div class="r">}}\preformatted{InsuranceTarif$getCostValues(costs, params)}\if{html}{\out{</div>}}
+\if{html}{\out{<div class="r">}}\preformatted{InsuranceTarif$getCostValues(params)}\if{html}{\out{</div>}}
 }
 
 \subsection{Arguments}{
 \if{html}{\out{<div class="arguments">}}
 \describe{
-\item{\code{costs}}{The cost parameter passed to the tarif definition or the
-contract (either an array of the form returned by \code{\link[=initializeCosts]{initializeCosts()}} or
-a function(params, values) returning such an array)}
-
-\item{\code{params}}{Contract-specific, full set of parameters of the contract
-(merged parameters of the defaults, the tariff, the profit participation
-scheme and the contract)}
+\item{\code{params}}{The parameters of the contract / tariff}
 }
 \if{html}{\out{</div>}}
 }
diff --git a/man/costs.baseAlpha.Rd b/man/costs.baseAlpha.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..63cca08a9df78b88be1792bfd1e2cf157777f3a8
--- /dev/null
+++ b/man/costs.baseAlpha.Rd
@@ -0,0 +1,19 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/InsuranceParameters.R
+\name{costs.baseAlpha}
+\alias{costs.baseAlpha}
+\title{Helper function to define base costs with base alpha, but otherwise unchanged costs}
+\usage{
+costs.baseAlpha(alpha)
+}
+\arguments{
+\item{alpha}{The minimum alpha / Zillmer cost that cannot be waived}
+}
+\description{
+Returns a function that sets base alpha (and Zillmer) costs to the given value,
+but otherwise uses the full costs defined by the Costs parameter.
+}
+\details{
+This function can be set as minCosts parameter for a tariff and makes sure
+that only alpha costs are modified / waived, but no other costs.
+}
diff --git a/man/costsDisplayTable.Rd b/man/costsDisplayTable.Rd
new file mode 100644
index 0000000000000000000000000000000000000000..784a6be7d4030c5bbcf0737f17b5a976f1b79707
--- /dev/null
+++ b/man/costsDisplayTable.Rd
@@ -0,0 +1,11 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/InsuranceParameters.R
+\name{costsDisplayTable}
+\alias{costsDisplayTable}
+\title{Helper function to display all cost definitions in a concise table}
+\usage{
+costsDisplayTable(costs)
+}
+\description{
+Returns a data.frame with columns
+}