MPA and Credit Usage
DEXBot2 supports native BitShares debt workflows through the bot-level debtPolicy config block. Each lending item declares its own collateral asset, and the runtime groups items by collateral to compute independent distributions. For the related AMA/grid side, see Market Adapter.
Contents
- Configuration Format
- Credit-Only Mode
- Collateral Distribution
- Runtime Timing
- MPA Maintenance
- Credit Offer Maintenance
- LP-Backed Credit Collateral
- State Files
- Operational Notes
- Related Files
Which section do I need?
| If you want to… | Read this | Key file / field |
|---|---|---|
| Configure a bot to borrow MPAs or credit offers | Configuration Format | debtPolicy.lending in bots.json |
| Understand how collateral is split across lending items | Collateral Distribution | outputWeight |
| Change how often the credit watchdog runs | Runtime Timing | TIMING in constants.ts |
| Know what happens when MPA CR drops below minimum | MPA Maintenance | minCollateralRatio |
| Know how credit deals are renewed and repaid | Credit Offer Maintenance | autoReborrow / autoRepay |
| Cap the size of a single borrow, or split oversized deals | Credit Offer Maintenance | maxBorrowAmountPerOperation |
| Use LP shares as credit-offer collateral | LP-Backed Credit Collateral | automatic valuation |
| Diagnose pending reborrow or renewal issues | State Files | profiles/credit_runtime/<botKey>.json |
| Safe operating practices | Operational Notes | — |
Configuration Format
Add debtPolicy to a bot entry in profiles/bots.json:
{
"name": "credit-bot-1",
"preferredAccount": "my-account",
"active": true,
"creditOnly": true,
"debtPolicy": {
"maxCollateralAmount": "80%",
"lending": [
{
"asset": "HONEST.USD",
"collateralAsset": "BTS",
"type": "mpa",
"outputWeight": 1,
"maxBorrowAmount": 1000,
"maxCollateralAmount": 5000,
"minCollateralRatio": 2.0,
"maxCollateralRatio": 2.5,
"targetCollateralRatio": 2.2
},
{
"asset": "HONEST.CNY",
"collateralAsset": "BTS",
"type": "creditOffer",
"outputWeight": 1,
"maxBorrowAmount": 1000,
"maxCollateralRatio": 2.5,
"maxFeeRatePerDay": 0.05,
"autoReborrow": true,
"autoRepay": 2
}
]
}
}Field Reference
All config fields (click to expand)
Required Fields:
| Field | Type | Description |
|---|---|---|
lending |
array |
Non-empty array of lending items. Each item maps a debt asset to a debt type and collateral asset. |
Lending Item Fields (every item must have):
| Field | Type | Required | Description |
|---|---|---|---|
asset |
string |
Yes | Debt asset symbol or ID (e.g. "HONEST.USD"). |
collateralAsset |
string |
Yes | Collateral asset (e.g. "BTS"). Multiple items may share the same collateral asset. |
type |
string |
Yes | "mpa" (BitShares MPA call order) or "creditOffer" (credit offer deal). |
Shared Optional Fields (both "mpa" and "creditOffer"):
| Field | Type | Required | Description |
|---|---|---|---|
outputWeight |
number |
No | Output weight for this asset. Controls the proportion of debt value across lending items (not collateral). Defaults to 1. See Collateral Distribution below. |
maxBorrowAmount |
number |
No | Fixed total debt ceiling. Must be a positive number (not a percentage). |
maxBorrowAmountPerOperation |
number |
No | Per-operation borrow cap. Any single credit-offer accept operation whose borrow amount exceeds this is rejected. For MPA, the planner clamps each debtDelta by this cap on top of maxBorrowAmount. When set, oversized credit deals are split into equal pieces via repay+reborrow cycles during maintenance (see Credit Offer Maintenance). Must be a positive number. |
maxCollateralAmount |
number | percentage string |
No | Total collateral ceiling. Use a number for an absolute collateral amount, e.g. 5000, or a percentage string of total available collateral, e.g. "80%". |
minCollateralIncreaseThreshold |
number | percentage string |
No | Minimum unused collateral allocation before increasing debt. Use a number for an absolute collateral amount, e.g. 25, or a percentage string of assigned collateral budget, e.g. "5%". 0 means no minimum. |
maxCollateralRatio |
number |
No* | Behavior differs by type: MPA — hard CR ceiling above which debt is increased first; creditOffer — maximum effective ratio when accepting offers. Required for creditOffer. |
MPA-Specific Fields:
| Field | Type | Required | Description |
|---|---|---|---|
targetCollateralRatio |
number |
No | Preferred operating CR. If omitted, midpoint of min/max is used. |
minCollateralRatio |
number |
No | Hard minimum CR floor. Below this, debt is reduced first. |
debtOnly |
boolean |
No | If true, the bot only adjusts debt to manage the collateral ratio — collateral is never added or withdrawn. Combined with minCollateralRatio/maxCollateralRatio, this keeps the position size constant while maintaining CR bounds. |
Credit-Offer-Specific Fields:
| Field | Type | Required | Description |
|---|---|---|---|
maxFeeRatePerDay |
number |
No | Maximum acceptable daily fee rate. Defaults to 1/2900 (~0.034%/day). |
autoReborrow |
boolean |
No | If true, the bot reborrows from the same offer after repayment. |
autoRepay |
number |
No | On-chain auto-repay mode: 0 (off), 1 (full only), 2 (partial allowed). |
allowedOfferIds |
string[] |
No | Whitelist of credit offer object IDs (1.21.x) the bot may accept. |
disallowedDealIds |
string[] |
No | Denylist of credit deal object IDs (1.22.x) the bot must not reborrow from. Repay is unaffected — the bot can still repay deals in this list. |
renewOnly |
boolean |
No | If true, the bot only reborrows existing deals — standalone credit borrows are refused. Default false. |
minDurationSeconds |
number |
No | Minimum acceptable offer duration in seconds. Offers with duration_seconds below this value are skipped. |
Global Fields:
| Field | Type | Description |
|---|---|---|
maxCollateralAmount |
number | percentage string |
Global collateral cap across all lending items. Use a number for an absolute collateral amount, e.g. 10000, or a percentage string of total available collateral, e.g. "80%". |
There is no separate enable switch. If debtPolicy.lending is present, non-empty, and every item has a valid collateralAsset, the credit runtime loads for that bot.
Credit-Only Mode
Set creditOnly: true on a bot entry to run only the credit runtime — no grid trading, order management, or fill processing.
{ "name": "Credit", "active": true, "creditOnly": true,
"preferredAccount": "my-account", "debtPolicy": { "lending": [...] } }No trading fields (assetA, assetB, startPrice, incrementPercent, activeOrders, reserveOrders, botFunds) are needed.
dexbot start credit # Background daemon — runs only the credit worker
dexbot start <bot> # Start a named bot directlyCollateral Increase Thresholds
minCollateralIncreaseThreshold is evaluated in collateral-asset units against the unused assigned collateral for that lending item:
25means at least 25 units of the collateral asset, such as25 BTS."5%"means at least 5% of that item’s assigned collateral budget.0means no minimum; any positive unused assigned collateral may trigger an increase.- Omitted on credit-offer items leaves proactive credit increases disabled for backward compatibility.
Collateral Distribution
The runtime calculates required collateral for each lending item backwards from the desired debt output ratio. The outputWeight field controls the proportion of debt value (not collateral) each item receives.
Formulas
MPA weight_i = outputWeight_i * feedPrice_i * targetCR_i
Credit weight_i = (outputWeight_i * maxCR_i) / conversionRate_i
C_total = min(availableCollateral, globalMaxCollateral)
C_i = C_total * weight_i / sum(all weights)
- MPA:
feedPrice_iis the current settlement feed price (collateral per debt asset), discovered from the chain and cached per position. - Credit:
conversionRate_iis the offer'sacceptable_collateralprice (debt asset per collateral unit), discovered from existing deals orallowedOfferIds. - Fallback chain (every pair resolves a rate, so every CR is calculable): offer map → LP pool valuation (
pool-derived) → universal DEX price (market-direct, elsemarket-bridge:<asset>via bridge hops, default BTS). Only when all three fail does the group keep its existing budget with a warning andweight = outputWeight * targetCR. - All offer-price orientation, conversion-rate, and CR math lives in
modules/credit_pricing.ts, shared by the runtime anddexbot credit. outputWeightis the user's output proportion. Equal weights produce equal economic debt value across all lending items, regardless of price or CR differences.
Examples
Two assets, equal weight — collateral split 50:50.
Two assets, 80% output on USD — USD receives a proportionally smaller share of the configured collateral pool, CNY receives the remaining larger share.
Three assets, equal weight — collateral split 1/3 : 1/3 : 1/3.
Runtime Timing
Credit and MPA maintenance are separated from periodic grid checks. DEXBot2 starts a dedicated credit watchdog interval during bot startup.
Timing defaults live in modules/constants.ts:
{
"TIMING": {
"CREDIT_DEAL_CHECK_INTERVAL_MIN": 60,
"CREDIT_DEAL_EXPIRY_THRESHOLD_HOURS": 12,
"CREDIT_DEAL_SPLIT_MAX_PIECES": 48,
"BLOCKCHAIN_SETTLE_DELAY_MS": 6000
}
}CREDIT_DEAL_CHECK_INTERVAL_MIN: how often the credit watchdog runs. Set to0or negative to disable.CREDIT_DEAL_EXPIRY_THRESHOLD_HOURS: how far beforelatest_repay_timethe bot proactively repays and reborrows.CREDIT_DEAL_SPLIT_MAX_PIECES: hard cap on pieces per_splitOversizedCreditDealscycle (default 48; ~4.8min at 6s/piece). Prevents one maintenance run from exceeding the watchdog interval.BLOCKCHAIN_SETTLE_DELAY_MS: pause between split pieces (default 6000ms). Resolved from theTIMINGconstant — per-botbots.jsonoverrides for this field are not honoured for split pacing.
MPA Maintenance
For each type: "mpa" lending item:
- If CR is below
minCollateralRatio, reduce debt first, then add collateral if needed. - If CR is above
maxCollateralRatio, increase debt first, then withdraw collateral if allowed. - Debt increases are calculated from the current feed price and current call-order collateral, capped by the total outstanding debt ceiling in
maxBorrowAmount. minCollateralIncreaseThresholdsuppresses dust-sized increases when unused assigned collateral is below the configured absolute or percentage threshold.- If the debt-first leg fails (e.g. insufficient free MPA to repay), the runtime attempts a collateral-only fallback.
- If
targetCollateralRatiois not set, the midpoint of the min/max band is used. - After any successful CR adjustment, the bot requests a grid reset so order sizing reflects the new capital base.
maxBorrowAmountonly prevents additional debt above the configured total; it does not block debt reduction. Must be a fixed positive number (no percentages).
Credit Offer Maintenance
For each type: "creditOffer" lending item, the runtime:
- Phase 0 — Split oversized deals: if
maxBorrowAmountPerOperationis set, scans existing credit deals and splits any whose debt exceeds the per-op cap into equal pieces via repay+reborrow cycles (see Oversized Credit Deal Splitter). This keeps each individual deal below the per-op cap and makes future renewals easier with less liquidity per operation. - Discovers active credit deals on-chain.
- Validates deals against the per-item policy (
maxCollateralRatio,maxFeeRatePerDay,allowedOfferIds,disallowedDealIds, etc.). - Gates increases on unused assigned collateral. If the collateral shortfall is at least
minCollateralIncreaseThreshold, it accepts an additional credit deal from the cheapest acceptable offer; the selected offer's price derives the borrow amount, capped bymaxBorrowAmountand, when set,maxBorrowAmountPerOperation. A borrow-cap-capped increase is skipped if the actual collateral used would fall belowminCollateralIncreaseThreshold. - Proactively repays deals nearing expiration (within
CREDIT_DEAL_EXPIRY_THRESHOLD_HOURS) and reborrows whenautoReborrowis enabled. - Ensures
auto_repayon-chain matches the policy'sautoRepaysetting, updating local state after each successful broadcast.
Oversized Credit Deal Splitter
When maxBorrowAmountPerOperation is set, each credit-maintenance cycle runs _splitOversizedCreditDeals as Phase 0. The splitter:
- Discovers deals whose
debtAmountexceedsmaxBorrowAmountPerOperation. - Splits each oversized deal into
ceil(debt / maxPerOp)equal pieces via atomic repay+reborrow transactions. Total debt across the new deals is preserved; only deal granularity changes. - Skips a deal if any piece would fall below the offer's
min_deal_amount(offers are cached with a 10-minute TTL —TIMING.OFFER_CACHE_TTL_MS— so on-chainmin_deal_amountchanges are picked up on the next re-fetch). - Pauses
BLOCKCHAIN_SETTLE_DELAY_MSbetween pieces; aborts on shutdown. - Stops once
CREDIT_DEAL_SPLIT_MAX_PIECESpieces have been emitted in the current cycle. Remaining oversized deals are deferred to the next maintenance cycle. - Uses an in-process
_splitInFlightguard sorunMaintenanceandrunCreditWatchdogcannot start overlapping splits.
The split pieces are normal credit deals — they appear in profiles/credit_runtime/<botKey>.json alongside other deals and are subject to the usual renewal, auto_repay, and collateral-switching flows.
Amount Cap Semantics
| Policy | Field | Scope |
|---|---|---|
| MPA | maxBorrowAmount |
Total debt ceiling — call order debt cannot exceed this. |
| MPA | maxBorrowAmountPerOperation |
Per-op borrow cap — clamps each debtDelta increment during CR-band adjustments. Ignored on debt-reduction moves. |
| MPA | maxCollateralAmount |
Total collateral ceiling — call order collateral cannot exceed this. Withdrawals still allowed. |
| Credit | maxBorrowAmount |
Total debt ceiling — total credit debt for the asset cannot exceed this. |
| Credit | maxBorrowAmountPerOperation |
Per-op borrow cap — rejects any single credit_offer_accept whose borrow amount exceeds this. Oversized existing deals are split during maintenance (see Oversized Credit Deal Splitter). |
| Credit | maxCollateralAmount |
Total collateral ceiling — total credit collateral for the asset cannot exceed this. |
maxBorrowAmount is always a fixed number (no percentages). maxCollateralAmount may be a fixed number or a percentage.
Credit Deal Renewal
When renewOnly is true, the bot refuses standalone credit borrows and only renews existing deals via repay+reborrow. This is useful when you want the bot to maintain existing positions but not open new ones.
When a deal's latest_repay_time is within CREDIT_DEAL_EXPIRY_THRESHOLD_HOURS:
- Repay the deal.
- Reborrow from the same offer when
autoReborrowis enabled, using the fullassignedCollateralBudget. - Preserve configured
autoRepayon the new credit-offer accept operation.
If inline reborrow cannot be built safely, the runtime stores a deferred reborrow request in profiles/credit_runtime/<botKey>.json and retries later.
Collateral Switching on Renewal
You can switch a credit deal's collateral to a different asset on its next renewal by changing lendingItem.collateralAsset to the new asset in bots.json. The runtime detects existing deals whose collateral no longer matches the policy and migrates them during proactive expiry repay+reborrow. Requirements:
- The new asset must be listed in the credit offer's
acceptable_collateral. The runtime rejects mismatched collateral with a specific error message. - The bot must hold enough of the new collateral asset before the deal is repaid. The offer's minimum required collateral is computed from the borrow amount and the new collateral price — the old deal's collateral amount is not carried forward.
- Only applies to
type: "creditOffer"items — MPA collateral is fixed by the call order asset. - Deferred reborrow requests from before the switch may fail; drop stale pending reborrows by clearing
profiles/credit_runtime/<botKey>.jsonor letting them expire naturally. - If the switch produces no active reborrow (e.g., insufficient balance for the new collateral), the deal is repaid and the reborrow is deferred to the pending queue for later retry.
auto_repay Enforcement
On each maintenance cycle, the runtime compares each deal's on-chain auto_repay against the policy's autoRepay value. If they differ, a credit_deal_update operation is broadcast. After a successful update, the local deal state is updated to prevent redundant broadcasts on the next cycle.
BitShares auto-repay modes (click to expand)
BitShares core 7.0.2 defines three auto-repay modes:
| Value | Mode | Behavior at latest_repay_time |
|---|---|---|
0 |
no_auto_repayment |
No auto-repay. Deal expires; collateral is liquidated to the offer owner. |
1 |
only_full_repayment |
Full repay if borrower balance >= debt + fee; otherwise deal expires. |
2 |
allow_partial_repayment |
Repay as much as possible with available balance; any remaining debt triggers expiry with proportional collateral liquidation. |
Important Distinction
autoReborrowis DEXBot2 behavior — the bot re-accepts the same offer after a repay.autoRepayis BitShares chain behavior — the chain attempts automatic repayment at deal expiry.
LP-Backed Credit Collateral
Credit offers may accept liquidity-pool share assets as collateral. Before accepting an offer, DEXBot2:
- Resolves the LP pool for the share asset.
- Reads pool balances and share supply.
- Computes the collateral value from the underlying reserves.
- Converts that value into the debt asset denomination.
- Rejects the borrow if the effective ratio exceeds the lending item's
maxCollateralRatio.
If pool lookup, supply lookup, or valuation cannot be resolved, the runtime fails closed and does not sign the borrow.
State Files
The runtime persists one state file per bot:
profiles/credit_runtime/<botKey>.json
The file tracks discovered chain state and pending work, including:
positions— per-position state map keyed asdebtAssetId:collateralAssetId- Active MPA call-order state and credit deal IDs per position
assignedCollateralBudgetper position- Pending reborrow requests (including deferred split pieces when an oversized-deal cycle hits
CREDIT_DEAL_SPLIT_MAX_PIECES) - Last repay timestamp and grid reset request
- Debt snapshot across all assets
Treat this file as runtime state, not primary configuration. The source of truth for enabled policy is profiles/bots.json.
Operational Notes
- Keep
debtPolicynarrow. Only list assets and offers the bot is allowed to use. - Use conservative CR bands.
minCollateralRatiois a hard safety floor, not a target. - Keep
maxFeeRatePerDayexplicit for credit offers. - Credit-offer collateral ratio and MPA call-order CR are validated in separate paths.
- After editing
profiles/bots.json, restart the bot so the runtime picks up the new policy. - Review
profiles/credit_runtime/<botKey>.jsonwhen diagnosing pending reborrow or renewal behavior.
Related Files
Source files and tests (click to expand)
modules/credit_runtime.ts: debt workflow executor (Phase 0 oversized-deal splitter lives here)modules/credit_pricing.ts: canonical credit-pricing math (offer orientation, conversion rates, CR, fees) shared by runtime and analyzermodules/cr_planner.ts: MPA debt-first planner; clampsdebtDeltabymaxBorrowAmountPerOperationmodules/cr_planner.ts:DebtFirstCrPlanOptions— planner options carryingmaxBorrowAmountPerOperation; lending-item shapes are validated inline inbot_settings.tsmodules/dexbot_class.ts: runtime startup and watchdog lifecyclemodules/bot_settings.ts:debtPolicyvalidationmarket_adapter/README.md: AMA pricing, grid triggers, and dynamic-weight runtimemodules/credential_policy.ts: signing constraints for credit and call-order operationstests/test_credit_runtime.ts: credit runtime behavior coverage (including 3 oversized-deal splitter tests)tests/test_multi_asset_distribution.ts: collateral distribution and multi-asset state coverage