System Parameters

A operational description of Checker’s internal parameters, and operations on them. NOTE: here we focus primarily on the specifics of the calculations; for the meaning of the concepts, see Design.

State

  • q: of type (1 / kit).

  • index: of type tez.

  • protected_index: of type tez.

  • target: TODO: said dimensionless, but I think tez/kit? Hmmm. I have to re-check the units of measure.

  • drift_derivative

  • drift

  • outstanding_kit: approximation of the total amount of kit that would be currently required to close all burrows.

  • circulating_kit: approximation of the total amount of kit that is currently in circulation.

  • last_touched: the last time the parameters were touched.

And two additional indices, one used in the calculation of burrowing fees and one in the calculation of the imbalance adjustment:

  • burrow_fee_index

  • imbalance_index

Initialization

We currently initialize checker with the following parameters:

q = 1
index = 1xtz
protected_index = 1xtz
target = 1
drift = 0
drift_derivative = 0
outstanding_kit = 0kit
circulating_kit = 0kit
last_touched = now
burrow_fee_index = 1
imbalance_index  = 1

Price API

tz_minting     = max index protected_index (in tez)
tz_liquidation = min index protected_index (in tez)

To calculate the current prices in (tez/kit), we multiply with the current quantity:

minting_price     = q * tz_minting
liquidation_price = q * tz_liquidation

Note

The definition of tz_minting and tz_liquidation implies that at any given moment, tz_minting >= tz_liquidation > 0. Combined with fminting > fliquidation, we have that

tz_minting * fminting > tz_liquidation * fliquidation

which is useful in liquidation logic (see burrow-state-liquidations.md).

Adjustment index

The adjustment index, as required by burrowing logic, can be calculated from the system parameters as the product of the burrow fee index and the imbalance index:

adjustment_index = burrow_fee_index * imbalance_index

Touching

Touching the system parameters has the effect of updating all aforementioned fields, and calculating the burrowing fees that need to be accrued to the cfmm sub-contract. This is done under the assumption that we have available the current time now, the current index index_now (calculated by the medianizer), and the current price of kit in tez kit_in_tez_now (calculated by the cfmm sub-contract). In fact, the cfmm sub-contract gives us the one calculated at the end of the last block, to make manipulation a little harder. We update each field:

last_touched

Update the timestamp from the last time it was touched to now

new_last_touched = now

index

Update the index from the last time the parameters were touched to the current one

new_index = index_now

protected_index

Update the protected index, by multiplying it with a bounded factor:

new_protected_index = old_protected_index * clamp (current_index / protected_index, low, high)

where low and high depend on how much time has passed since the last time the parameters were touched, effectively limiting how fast protected_index can change:

low  = exp (-epsilon * (now - last_touched))
high = exp (+epsilon * (now - last_touched))

NOTE: exp (x) = 1 + x here; we expect the contract to be touched rather frequently, which keeps the exponent rather small, which makes this a good approximation of exp.

drift_derivative

For the calculation of the derivative of drift, drift_derivative, we only use the last-observed target (TODO: show how we get from the original formula with the logarithms to this?) We calculate as follows:

new_drift_derivative =
  -0.0005 / (secs_in_a_day ^ 2) , if                        target <= exp (-high_bracket)
  -0.0001 / (secs_in_a_day ^ 2) , if exp (-high_bracket) <  target <= exp (-low_bracket)
   0                            , if exp (-low_bracket)  <  target <  exp ( low_bracket)
   0.0001 / (secs_in_a_day ^ 2) , if exp ( low_bracket)  <= target <  exp ( high_bracket)
   0.0005 / (secs_in_a_day ^ 2) , if exp ( high_bracket) <= target

drift

For the calculation of the current drift, we use use the following formula:

new_drift = old_drift + (1/2) * (old_drift_derivative + new_drift_derivative) * (now - last_touched)

q

For the calculation of the current quantity q, we use the following formula:

new_q = old_q
      * exp (
          (old_drift + (1/6) * ((2 * old_drift_derivative) + new_drift_derivative) * (now - last_touched))
          * (now - last_touched)
        )

NOTE: exp (x) = 1 + x here; TODO: not sure if the exponent is small enough for this to be a good approximation.

target

new_target = new_q * (new_index / kit_in_tez_now)

burrow_fee_index

The burrow fee index is updated linearly on the number of seconds that have passed since the last time the parameters were touched.

new_burrow_fee_index = old_burrow_fee_index
                     * (1 + burrow_fee_rate * (now - last_touched) / seconds_in_a_year)

imbalance_index

The imbalance index is also updated linearly on the number of seconds that have passed since the last time the parameters were touched

new_imbalance_index = old_imbalance_index
                    * (1 + imbalance_rate * (now - last_touched) / seconds_in_a_year)

but imbalance_rate varies, depending on the difference between old_outstanding_kit and old_circulating_kit:

imbalance_rate =
  clamp
    ( imbalance_scaling_factor * (circulating - outstanding) / circulating,
      -imbalance_limit,
      +imbalance_limit
    )

or, equivalently:

imbalance_rate =
  min (imbalance_scaling_factor * (circulating - outstanding) / circulating, +imbalance_limit), if circulating >= outstanding
  max (imbalance_scaling_factor * (circulating - outstanding) / circulating, -imbalance_limit), if circulating < outstanding

And in the edge cases the imbalance_rate is calculated as follows:

  • if old_circulating_kit = 0 and old_outstanding_kit = 0 then imbalance_rate = 0.

  • if old_circulating_kit = 0 and old_outstanding_kit > 0 then imbalance_rate = -imbalance_limit. (the outstanding kit is infinitely greater than the circulating kit, so the rate is saturated).

Intermediate outstanding_kit

In order to compute the updates for the two remaining fields (outstanding_kit and circulating_kit), we first need to calculate the current amount of kit outstanding, taking into account the accrued burrowing fee, thus

outstanding_with_fees = old_outstanding_kit * (new_burrow_fee_index / old_burrow_fee_index)

Accrual to cfmm

The accrued burrowing fees are to be given to the cfmm sub-contract. The total amount we easily compute as

accrual_to_cfmm = outstanding_with_fees - old_outstanding

outstanding_kit

To obtain the updated outstanding_kit, we need to account for both the accrued burrowing fees, and the imbalance adjustment

new_outstanding_kit = old_outstanding_kit
                    * (new_burrow_fee_index / old_burrow_fee_index)
                    * (new_imbalance_index / old_imbalance_index)

or equivalently

new_outstanding_kit = outstanding_with_fees * (new_imbalance_index / old_imbalance_index)

circulating_kit

Finally, to obtain the up-to-date circulating_kit, we just need to record the new kit in circulation, that is, accrual_to_cfmm:

new_circulating_kit = old_circulating_kit + accrual_to_cfmm

NOTE: If the current timestamp is identical to that stored in the parameters, we do not perform any of the above.

Misc

  • seconds_in_a_year = 31556952 (= (365 + 1/4 - 1/100 + 1/400) days * 24 * 60 * 60)

  • seconds_in_a_day  = 86400 (= 24 * 60 * 60)

  • low_bracket  = 0.005

  • high_bracket = 0.05

  • imbalance_scaling_factor = 0.75

  • imbalance_limit = 0.05