Central banks now redeem the national money foreign banks hold, sell reserves to defend a weak rate, and cover part of strategic imports from reserves. Five repeatable technologies raise each commodity's output per unit of energy. Hostile troops on a road or sea lane sever trade, power and unit supply. Middlemen buy food to resell to neighbours at a markup, and starving regions buy before those topping up their buffers, which clears the landlocked famines. The economic map's trade graph gains a colour legend.
93 lines
3.8 KiB
JavaScript
93 lines
3.8 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import { smallState, cityOf } from "./framework/helpers.js";
|
|
import { MONEY } from "../shared/data.js";
|
|
|
|
// Gives both central banks a symmetric reserve of each other's currency and the
|
|
// matching issued-currency debt, so the FX desk has something to work.
|
|
function bookReserves(state, amount = 1000) {
|
|
const a = state.currencyOf(0).code;
|
|
const b = state.currencyOf(1).code;
|
|
state._addReserve(1, a, amount);
|
|
state._addReserve(0, b, amount);
|
|
state._addCentralBankDebt(0, a, amount);
|
|
state._addCentralBankDebt(1, b, amount);
|
|
return { a, b };
|
|
}
|
|
|
|
export class CentralBankFxTest extends TestCase {
|
|
test_redeeming_own_currency_retires_it_from_the_foreign_bank() {
|
|
const state = smallState();
|
|
const { a, b } = bookReserves(state, 1000);
|
|
const redeemed = state._redeemOwnCurrency(0, 500);
|
|
this.assertApprox(redeemed, 500);
|
|
// Britain hands back the francs it held; France hands back the pounds.
|
|
this.assertApprox(state.getCentralBankReserves(1).get(a), 500);
|
|
this.assertApprox(state.getCentralBankReserves(0).get(b), 500);
|
|
// Both issued-currency liabilities shrink with the money retired.
|
|
this.assertApprox(state.getCentralBankDebt(0).get(a), 500);
|
|
this.assertApprox(state.getCentralBankDebt(1).get(b), 500);
|
|
// The bid for francs and the offer of pounds register as currency demand.
|
|
this.assertGreater(state._currencyDemand.get(0) || 0, 0);
|
|
this.assertLess(state._currencyDemand.get(1) || 0, 0);
|
|
}
|
|
|
|
test_a_weak_rate_sells_more_reserves_than_a_strong_one() {
|
|
const strong = smallState();
|
|
bookReserves(strong, 1_000_000);
|
|
strong.setCurrencyValue(0, 1);
|
|
const ordinary = strong._settleCentralBankFx(0);
|
|
|
|
const weak = smallState();
|
|
bookReserves(weak, 1_000_000);
|
|
weak.setCurrencyValue(0, 0.9);
|
|
const defended = weak._settleCentralBankFx(0);
|
|
|
|
this.assertGreater(ordinary, 0, "the bank redeems even when the rate is calm");
|
|
this.assertGreater(defended, ordinary, "a weak rate is defended harder");
|
|
}
|
|
|
|
test_the_bank_leaves_its_reserves_alone_without_money_held_abroad() {
|
|
const state = smallState();
|
|
const b = state.currencyOf(1).code;
|
|
state._addReserve(0, b, 1_000_000);
|
|
this.assertApprox(state._settleCentralBankFx(0), 0);
|
|
}
|
|
|
|
test_the_import_subsidy_draws_on_its_own_currency_reserves() {
|
|
const state = smallState();
|
|
const buyer = cityOf(state, 0);
|
|
const a = state.currencyOf(0).code;
|
|
const b = state.currencyOf(1).code;
|
|
state._addReserve(0, b, 1_000_000);
|
|
const before = state.getRegionCash(buyer).get(a) || 0;
|
|
const acquired = state._regionAcquireCurrency(buyer, 1, 1000, null, true, 400);
|
|
this.assertApprox(acquired.subsidised, 400, 1e-6, "the bank covered its share");
|
|
this.assertApprox(acquired.amount, 1000, 1e-6, "the seller is paid in full");
|
|
this.assertApprox(
|
|
state.getCentralBankReserves(0).get(b), 999_600, 1e-6,
|
|
"the subsidy came out of reserves"
|
|
);
|
|
const after = state.getRegionCash(buyer).get(a) || 0;
|
|
this.assertApprox(before - after, 600, 1e-6, "the buyer paid only the rest");
|
|
}
|
|
|
|
test_the_import_subsidy_budget_is_capped_for_the_day() {
|
|
const state = smallState();
|
|
const b = state.currencyOf(1).code;
|
|
state._addReserve(0, b, 1_000_000);
|
|
state._fxSubsidySpent = new Map();
|
|
const budget = 1_000_000 * MONEY.fx.importSubsidyPerDay;
|
|
const first = state._centralBankImportSubsidy(0, "food", 1_000_000);
|
|
this.assertApprox(first, budget, 1e-6);
|
|
state._recordImportSubsidy(0, first);
|
|
this.assertApprox(state._centralBankImportSubsidy(0, "food", 1_000_000), 0);
|
|
}
|
|
|
|
test_no_subsidy_for_a_resource_the_bank_does_not_back() {
|
|
const state = smallState();
|
|
const b = state.currencyOf(1).code;
|
|
state._addReserve(0, b, 1_000_000);
|
|
this.assertEqual(state._centralBankImportSubsidy(0, "luxury", 1_000_000), 0);
|
|
}
|
|
}
|