Browse the docs
Example 10 of 13
Upstream oil: PSC and reserve-based loan
Based on Oil Exercise.xlsx + Oil Project with Reserve Based Loan.xlsm
Two workbooks from the upstream folder combined into one model. The first works through production-sharing terms: cost recovery, profit oil on a sliding scale or an R-factor, royalty, milestone bonuses and a tax holiday. The second finances a Nigerian field with a reserve-based loan whose borrowing base is the present value of remaining reserves. Design example: written in the full v0.5 language. The compiler builds the working core today, so this one doesn't compile yet. Source: examples/design/oil/.
- Library
- A. Featured Models / D. Upstream Oil (33 files): Production Payment, Nigeria Oil Examples
- Fiscal terms
- 53 MMbbl reserves; profit share 55% → 10% on cumulative production, or 50% → 15% on the R-factor; royalty 2% → 14%; 25% tax after a 5-year holiday
- Bonuses
- Signing 2, commercial declaration 3, 25 MMbbl produced 5, 50 MMbbl produced 7
- Field
- Oil at USD 44/bbl with a hedge at 45, fixed cost 250 a year, variable USD 10/bbl (workbook units)
- Debt
- 45% debt to capital, LIBOR 2% + 5%, 9-year tenor, PLCR 1.5x, LLCR 1.35x, reserves discounted at 8%
- Rebuild shown
- 5 files, 116 lines
Where each sheet goes
| Workbook sheets | Rufineer |
|---|---|
| Assumptions, Production Curves | inputs.rfn and the Production, Sliding scale, R-factor and Royalty sheets |
| Cost Recovery Account | a balance with .available in psc.rfn |
| Profit Sharing, Sliding Scale, R Factor, Royalty | bands in psc.rfn |
| Bonuses | three event lines |
| IncomeTax | psc.rfn |
| Operating Analysis (hedged and unhedged) | field.rfn with hedge |
| Borrowing Base, Financing Inputs | rbl.rfn with pv_ahead and | sampled |
Source 5 files shown, 116 lines
model "Upstream Oil: PSC and RBL" {
currency USD
currency NGN
unit kUSD = 1_000 USD
unit MMbbl = 1_000_000 bbl
data "oil_inputs.xlsx"
}
timeline {
start 2020-01-01
development 1m
construction 3m
operations 15y
step month until operations.start, then year
}
fx NGN/USD = ppp(450, local: inputs.macro.ngn_cpi, foreign: inputs.macro.us_cpi)table production: kbbl/y // Operating year | Production
table sliding_scale: % // Cumulative production | Contractor share: 0, 25, 50, 100 MMbbl → 55, 30, 20, 10%
table r_factor_scale: % // R-factor | Contractor share: 0, 1, 1.5, 2, 2.25, 2.5 → 50, 45, 40, 30, 20, 15%
table royalty_scale: % // R-factor | Royalty: 0, 0.5, 0.8, 1.1, 1.5, 2 → 2, 5, 7, 10, 12, 14%
table macro { rows year; columns ngn_cpi: %/y "Naira inflation", us_cpi: %/y "USD inflation" }
inputs "Field" {
development = 2_000 kUSD
capex = 30_000 kUSD
oil_price = 44 USD/bbl
hedge_price = 45 USD/bbl
hedge_share = 0 %
fixed_opex = 250 kUSD/y
variable_opex = 10 USD/bbl
}
inputs "Fiscal terms" {
sharing = option r_factor | sliding_scale
cost_oil_cap = 100 % // of revenue after royalty
signing_bonus = 2_000 kUSD
declaration = 3_000 kUSD
bonus_25 = 5_000 kUSD
bonus_50 = 7_000 kUSD
declaration_date = 2021-01-01
tax_holiday = 5y
tax_rate = 25 %
}
inputs "Reserve-based loan" {
max_gearing = 45 %
libor = 2 %/y
margin = 5 %/y
tenor = 9y
reserve_discount = 8 %/y
target_plcr = 1.50 x
target_llcr = 1.35 x
repayment = option reserve_based | equal
}module field "Field" {
production: kbbl = inputs.production[operations.year] | during operations
event first_oil = first(production > 0 kbbl)
sale = hedge(volume: production, hedged: inputs.hedge_share,
strike: inputs.hedge_price, market: inputs.oil_price)
revenue: kUSD = sale.hedged_revenue + sale.market_revenue
fixed: kUSD = inputs.fixed_opex | during operations
opex: kUSD = fixed + production * inputs.variable_opex
build: kUSD = spread(inputs.capex, over: construction, profile: even)
development: kUSD = inputs.development | at development.start
capex: kUSD = build + development
}module psc "Production sharing" {
royalty: kUSD = field.revenue * bands(prev(r_factor), inputs.royalty_scale)
/// 1. Cost oil recovers spending, up to the cap
balance cost_pool: kUSD {
+ field.capex + field.opex
- cost_oil
}
cost_oil: kUSD = min(cost_pool.available, inputs.cost_oil_cap * (field.revenue - royalty))
/// 2. Profit oil, shared on last year's R-factor or on cumulative production
profit_oil: kUSD = field.revenue - royalty - cost_oil
r_factor = cumulative(contractor_take) / cumulative(field.capex + field.opex)
share = match inputs.sharing {
r_factor => bands(prev(r_factor), inputs.r_factor_scale)
sliding_scale => bands(cumulative(field.production), inputs.sliding_scale)
}
contractor_take: kUSD = cost_oil + profit_oil * share
/// 3. Bonuses on milestones
event bonus_25 = first(cumulative(field.production) >= 25 MMbbl)
event bonus_50 = first(cumulative(field.production) >= 50 MMbbl)
signing: kUSD = inputs.signing_bonus | at timeline.start
declaration: kUSD = inputs.declaration | at inputs.declaration_date
milestones: kUSD = inputs.bonus_25 | at bonus_25
milestones2: kUSD = inputs.bonus_50 | at bonus_50
bonuses: kUSD = signing + declaration + milestones + milestones2
/// 4. Income tax after the holiday
period holiday = field.first_oil.date for inputs.tax_holiday
tax: kUSD = max(0, contractor_take - field.opex
- depreciation(additions: field.capex, method: unit_of_production,
production: field.production).charge)
* inputs.tax_rate | during not holiday
government_take: kUSD = royalty + profit_oil * (1 - share) + bonuses + tax
contractor_cash: kUSD = contractor_take - field.capex - field.opex - bonuses - tax
contractor_irr = xirr(contractor_cash)
}module rbl "Reserve-based loan" {
period tenor = operations.start for inputs.tenor
all_in: %/y = inputs.libor + inputs.margin
/// Borrowing base: PV of remaining net cash at 8%, cut back to the PLCR, reset twice a year
borrowing_base: kUSD = pv_ahead(psc.contractor_cash, rate: inputs.reserve_discount)
/ inputs.target_plcr | sampled every 6m
balance loan: kUSD {
+ inputs.max_gearing * field.capex
- repayment
}
required: kUSD = max(0, loan.opening - borrowing_base)
scheduled: kUSD = loan.opening / periods_left(tenor)
repayment: kUSD = match inputs.repayment { reserve_based => max(required, scheduled),
equal => scheduled } | during tenor
interest: kUSD = loan.opening * all_in
plcr: x = pv_ahead(psc.contractor_cash, rate: all_in) / loan.opening
llcr: x = pv_ahead(psc.contractor_cash | during tenor, rate: all_in) / loan.opening
}
check "LLCR above target" rbl.llcr >= inputs.target_llcr | during rbl.tenor
check "Cost pool recovered" psc.cost_pool.closing == 0 at operations.end tolerance 1 kUSDWhat this model needed from the language
- Recovering up to a cap. The cost-recovery account is a balance that pays out
min(available, cap × revenue). The same pattern covers tax losses and deferred fees. - Sliding scales and R-factors.
bandsreads a threshold table from the data workbook, so changing the fiscal terms changes no source. - Milestones. Bonuses at 25 and 50 MMbbl are events on cumulative production.
- A borrowing base.
pv_aheadgives the PV of remaining reserves in every period, and| sampled every 6mfixes it between redeterminations. - Unit-of-production depreciation and
hedgefrom the standard library.