Country-specific scoring
- Example: different logic for DE and PL
- The
sourceis the join key - Going further: a different strategy per country
- Tips
Yes - you can score one country differently from another, using the data your own registry driver returns. Two facts make this work:
- The
datayou put inLookupResult::found(data: [...])flows intoCheckResult::$raw(viaRegistryCheck), tagged with the source you gave that check. - A
RiskRulereceives aRiskContextwith both thecounterparty(so the country) and the fullreport(so every result, including yourrawdata).
LookupResult.data ─► RegistryCheck ─► CheckResult { source, raw = data, proofId }
│
▼
RiskRule->evaluate(RiskContext)
(counterparty->country + report.fromSource(...))
So a rule can branch on the country and read the country-specific payload.
Example: different logic for DE and PL
use Gawrys\Counterparty\Risk\{RiskRule, RiskContext, RiskSignal, Evidence};
// Only applies to DE, reads the German registry payload.
final class GermanInsolvencyRule implements RiskRule
{
public function evaluate(RiskContext $ctx): iterable
{
if ($ctx->counterparty->country !== 'DE') {
return; // not applicable elsewhere
}
foreach ($ctx->report->fromSource('de.handelsregister') as $r) {
if (($r->raw['status'] ?? null) === 'INSOLVENT') {
yield new RiskSignal('de.insolvent', 0.9, adverse: true,
evidence: Evidence::grounded(
'Insolvency flagged in the Handelsregister.',
$r->proofId ?? 'https://handelsregister.de/',
0.95,
));
}
}
}
}
// Only applies to PL, uses the White List / KRS.
final class PolishVatRule implements RiskRule
{
public function evaluate(RiskContext $ctx): iterable
{
if ($ctx->counterparty->country !== 'PL') {
return;
}
// ... read Source::WHITE_LIST / Source::KRS results ...
}
}
Both live in the same strategy; each guards on country:
$strategy = new RuleBasedRiskStrategy([
new GermanInsolvencyRule(),
new PolishVatRule(),
new \Gawrys\Counterparty\Risk\Rule\SanctionsHitRule(), // applies everywhere
]);
The source is the join key
When you register a RegistryCheck for your country, you pass a source string (e.g.
'de.handelsregister'). That is exactly what the rule queries with
$ctx->report->fromSource('de.handelsregister'). Keep a small set of constants for your
sources, the way the core does in Report\Source.
new RegistryCheck(
$registries,
RegistryCapability::LegalEntityData,
$clock,
source: 'de.handelsregister',
name: 'DE Handelsregister',
);
Going further: a different strategy per country
Country-guarded rules cover most needs. If two countries need fundamentally different models (weights, thresholds, even an ML scorecard), select the whole strategy up front:
$strategy = match ($counterparty->country) {
'DE' => $germanStrategy, // RuleBasedRiskStrategy with DE rules + threshold
'PL' => $polishStrategy,
default => RuleBasedRiskStrategy::withDefaultRules(),
};
$verifier = new Verifier($checks, $strategy, $clock);
See Custom strategy for replacing the engine entirely.
Tips
- Keep country logic in rules, not in checks - checks gather facts, rules interpret them.
- Put structured, rule-friendly fields into
LookupResult::data(e.g.status,incorporatedAt,pkd), not just a raw blob. - Prefer grounded
Evidence(with the registry’s URL / proof id) for adverse signals so the outcome is auditable.