taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

commit a242f682257c7bfbf88c62dfbe4a64cfc54c6dde
parent 4312cc94b4d0d31f6e68e1ae354910ed1e90fada
Author: Florian Dold <dold@taler.net>
Date:   Sun, 23 Aug 2026 12:12:49 +0200

TOPS: correct AML forms and reporting

Diffstat:
Mdeployments/tops.rst | 248+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
1 file changed, 154 insertions(+), 94 deletions(-)

diff --git a/deployments/tops.rst b/deployments/tops.rst @@ -4,8 +4,8 @@ Taler Operations Deployment Definitions / Glossary ---------------------- -* GwG: German "Geldwäschegesetz", Swiss law regarding anti-money laundering -* VQF: Verein für Qualitätssicherung im Finanzwesen, self-regulatory +* GwG: Swiss "Geldwäschereigesetz", the Swiss Anti-Money Laundering Act +* VQF: Verein zur Qualitätssicherung von Finanzdienstleistungen, self-regulatory organization that Taler Operations AG is a member of and thus needs to stick to their rules * TmeR: German "Transaktion mit erhöhtem Risiko", i.e. @@ -252,10 +252,10 @@ Properties are registered at the GNU Taler Account Properties `GNU Taler Account * ``INVESTIGATION_STATE`` - * The MROS reporting state for the account. + * The special-clarification and MROS reporting state for the account. * Values: - * ``NONE`` / undefined: No MROS reporting for that account + * ``NONE`` / undefined: No pending special clarification and no MROS report * ``INVESTIGATION_PENDING``: Pending investigation. The AML officer should submit ``vqf_902_14`` to conclude investigation. Usually the property would be set by the sanction list tool or some AML program that detects @@ -263,9 +263,12 @@ Properties are registered at the GNU Taler Account Properties `GNU Taler Account monitoring (see ``tops-0001.sql`` for an example). The ``vqf_902_14`` form could also be used to start an investigation (by setting ``INCRISK_RESULT`` to ``OTHER``). - * ``INVESTIGATION_COMPLETED_WITHOUT_SUSPICION``: Completed according to Art. 6 GwG - * ``REPORTED_SUSPICION_SIMPLE``: Reported under Art. 305 StGB (German "einfacher Verdacht", simple suspicion) - * ``REPORTED_SUSPICION_SUBSTANTIATED``: Reported under Art. 9 GwG (German "begründeter Verdacht", substantiated suspicion) + * ``INVESTIGATION_COMPLETED_WITHOUT_SUSPICION``: Special clarification + completed without a suspicion that leads to an MROS report + * ``REPORTED_SUSPICION_SIMPLE``: A report was filed under Art. 305ter Abs. 2 + StGB (German "einfacher Verdacht", simple suspicion) + * ``REPORTED_SUSPICION_SUBSTANTIATED``: A report was filed under Art. 9 GwG + (German "begründeter Verdacht", substantiated suspicion) * ``INVESTIGATION_TRIGGER :: Text`` @@ -303,7 +306,7 @@ Account opening/closing: PEP/Risk classification: * ``INCR_HIGH_RISK_CUSTOMER`` / ``DECR_HIGH_RISK_CUSTOMER`` -* ``INCR_HIGH_RISK_COUNTRY`` / ``INCR_HIGH_RISK_COUNTRY`` +* ``INCR_HIGH_RISK_COUNTRY`` / ``DECR_HIGH_RISK_COUNTRY`` * ``INCR_PEP`` / ``DECR_PEP`` * ``INCR_PEP_FOREIGN`` / ``DECR_PEP_FOREIGN`` * ``INCR_PEP_DOMESTIC`` / ``DECR_PEP_DOMESTIC`` @@ -719,7 +722,10 @@ Initial collection of basic attributes about customer during onboarding. ESTABLISHER_LIST[].NATIONALITY :: Text ESTABLISHER_LIST[].PERSONAL_IDENTIFICATION_DOCUMENT_COPY :: File ESTABLISHER_LIST[].SIGNING_AUTHORITY_TYPE :: 'SINGLE' | 'COLLECTIVE_TWO' | 'OTHER' - ESTABLISHER_LIST[].SIGNING_AUTHORITY_EVIDENCE_TYPE :: 'CR' | 'MANDATE' | 'OTHER' + when (ESTABLISHER_LIST[].SIGNING_AUTHORITY_TYPE = 'OTHER') { + ESTABLISHER_LIST[].SIGNING_AUTHORITY_TYPE_OTHER :: Text + } + ESTABLISHER_LIST[].SIGNING_AUTHORITY_EVIDENCE :: 'CR' | 'MANDATE' | 'OTHER' ESTABLISHER_LIST[].SIGNING_AUTHORITY_EVIDENCE_DOCUMENT_COPY :: File when (ESTABLISHER_LIST[].SIGNING_AUTHORITY_EVIDENCE = 'OTHER') { ESTABLISHER_LIST[].SIGNING_AUTHORITY_EVIDENCE_OTHER :: Text @@ -727,7 +733,7 @@ Initial collection of basic attributes about customer during onboarding. } CORRESPONDENCE_LANGUAGE :: 'en' | 'de' | 'fr' | 'it' CUSTOMER_TYPE_VQF :: ( - 'NATURAL' | 'OPERATIONAL' | 'FOUNDATION' | + 'NATURAL_PERSON' | 'OPERATIONAL' | 'FOUNDATION' | 'TRUST' | 'LIFE_INSURANCE' | 'OTHER') * ``SIGNATURE`` @@ -754,7 +760,7 @@ Initial collection of basic attributes about customer during onboarding. * **Type:** Single Choice * **Choices:**: - * ``NATURAL`` + * ``NATURAL_PERSON`` * **Label DE**: Die Vertragspartei ist eine natürliche Person und es bestehen keine Zweifel, dass diese selber an den Vermögenswerten wirtschaftlich @@ -939,10 +945,10 @@ Initial collection of basic attributes about customer during onboarding. * ``Identifizierungsformular (Kundenbasisdaten)`` **Measure after submission by customer:** -Depending on ``CUSTOMER_TYPE``, the customer is asked to fill out another +Depending on ``CUSTOMER_TYPE_VQF``, the customer is asked to fill out another form: -* ``NATURAL``: No other form to fill out. A TAN letter will be directly +* ``NATURAL_PERSON``: No other form to fill out. A TAN letter will be directly sent to the customer. * ``OPERATIONAL``: Form ``vqf_902_11_customer`` * ``FOUNDATION``: Form ``vqf_902_12`` @@ -984,7 +990,7 @@ vqf_902_1_officer ACCEPTANCE_FURTHER_INFO :: Optional[Text] EMBARGO_TERRORISM_CHECK_RESULT :: 'LISTED' | 'NOT_LISTED' EMBARGO_TERRORISM_CHECK_DATE :: Date - when EMBARGO_TERRORISM_INFO = 'LISTED' { + when EMBARGO_TERRORISM_CHECK_RESULT = 'LISTED' { EMBARGO_TERRORISM_INFO :: Text } SUPPLEMENTAL_FILES_LIST[].FILE :: File @@ -1305,14 +1311,23 @@ to document the risk profile of a customer. * **Type:** Multi-line text * **Label DE:** Begründung für abweichende Risikobewertung -* ``HIGH_RISK`` +* ``RISK_CLASSIFICATION_LEVEL`` - * **Type:** Checkbox (yes/no) + * **Type:** Single choice * **Label DE:** [Risikoklassifizierung] Geschäftsbeziehung mit erhöhtem Risiko + * **Choices:** + + * ``HIGH_RISK`` + + * **Label DE:** Ja + + * ``NO_HIGH_RISK`` + + * **Label DE:** Nein * ``HIGH_RISK_ACCEPTANCE_DATE`` - * **Type:** Checkbox (yes/no) + * **Type:** Date * **Label DE:** Die Zustimmung einer vorgesetzten Person / Stelle oder der Geschäftsführung zur Aufnahme einer Geschäftsbeziehung mit erhöhtem Risiko wurde eingeholt @@ -1760,7 +1775,7 @@ entity or partnership. * ``TITLE_VQF_902_11_CUSTOMER`` * **DE:** - Feststellung des Kontrollinhabers an nicht + Feststellung des Kontrollinhabers an nicht börsenkotierten, operativ tätigen juristischen Personen und Personengesellschaften (K) @@ -1882,7 +1897,7 @@ entity or partnership. * ``TITLE_VQF_902_11_OFFICER`` * **DE:** - Feststellung des Kontrollinhabers an nicht + Feststellung des Kontrollinhabers an nicht börsenkotierten, operativ tätigen juristischen Personen und Personengesellschaften (K) @@ -1930,15 +1945,15 @@ out by at the initiative of the AML officer or in response to an alert. CUSTOMER_NAME :: String INCRISK_REASON :: Text INCRISK_MEANS :: 'GATHERING' | 'CONSULTATION' | 'ENQUIRIES' | 'OTHER' - when INCRISK_MEANS_OTHER = 'OTHER' { + when INCRISK_MEANS = 'OTHER' { INCRISK_MEANS_OTHER :: Text } INCRISK_SUMMARY :: Text INCRISK_DOCUMENTS :: Text INCRISK_RESULT :: ( - 'NO_SUSPICION' | 'SUBSTANTIATED_SUSPICION' | + 'NO_SUSPICION' | 'REASONABLE_SUSPICION' | 'SIMPLE_SUSPICION' | 'OTHER') - if INCRISK_REASON = 'OTHER' { + when INCRISK_RESULT = 'OTHER' { INCRISK_RESULT_OTHER :: Text } @@ -2022,7 +2037,7 @@ out by at the initiative of the AML officer or in response to an alert. * **Type:** Free-form, multi-line text * **When:** ``INCRISK_RESULT = 'OTHER'`` - * **Label DE:** Erklärung zu anderem Verdacht + * **Label DE:** Erklärung zu anderem Ergebnis vqf_902_15 @@ -2186,7 +2201,7 @@ Properties: if (INCRISK_RESULT == "SIMPLE_SUSPICION") { newProps.INVESTIGATION_STATE = "REPORTED_SUSPICION_SIMPLE"; - } else if (INCRISK_RESULT == "SUBSTANTIATED_SUSPICION") { + } else if (INCRISK_RESULT == "REASONABLE_SUSPICION") { newProps.INVESTIGATION_STATE = "REPORTED_SUSPICION_SUBSTANTIATED"; } else if (INCRISK_RESULT == "NO_SUSPICION") { newProps.INVESTIGATION_STATE = "INVESTIGATION_COMPLETED_WITHOUT_SUSPICION"; @@ -2322,103 +2337,146 @@ to answer with statistics derived via events: TBD. -Based on this, we have the following statistics: - -* Number of open accounts on January 1st (self-declaration 3.1.1) - - * Implemementation: ``evtcount(INCR_ACCOUNT_OPEN, start=0, end=jan_first_20xx) - evtcount(DECR_ACCOUNT_OPEN, start=0, end=jan_first_20xx)`` - -* Number of newly opened accounts between 01.01.20XX and 31.12.20XX (self-declaration 3.1.2.) - - * Implemementation: ``evtcount(INCR_ACCOUNT_OPEN, start=jan_first_20xx, end=dec_last_20xx)`` - -* Number of AML files managed during the year 20XX (self-declaration 3.1.3.) - - * All accounts ever opened except the ones that were closed *before* 20xx - * Implemementation: ``evtcount(INCR_ACCOUNT_OPEN, start=0, end=dec_last_20xx) - evtcount(DECR_ACCOUNT_OPEN, start=0, end=jan_first_20xx)`` - -* Number of AML files closed between 01.01.20XX and 31.12.20XX (self-declaration 3.1.4) +All annual statistics use the half-open reporting period +``year_20xx = [year_start, next_year_start)``. Events at ``year_start`` belong +to the new reporting year; events at ``next_year_start`` do not. The following +notation is used: - * Implemementation: ``evtcount(DECR_ACCOUNT_OPEN, start=jan_first_20xx, end=dec_last_20xx)`` +* ``accounts_at(condition, t)`` is the set of AML accounts for which + ``condition`` is true immediately before ``t``. +* ``accounts_during(condition, range)`` is the set of AML accounts for which + ``condition`` is true for any non-empty interval within ``range``. +* ``accounts_with_event(event, range)`` is the set of AML accounts that emitted + ``event`` in ``range``. Each account occurs at most once in the set. +* ``evtcount(event, range)`` counts occurrences of ``event``. This is used for + reports, where separate filings by the same customer must be counted + separately. -* Were there business relationships in the year 20XX with high risk? (self-declaration 4.1) +The set operations must be evaluated from each account's ordered property and +event history. In particular, two properties must be true at the same time to +satisfy a conjunction. Define these sets for the selected reporting year: - * Implementation: ``evtcount(INCR_HIGH_RISK_CUSTOMER, start=0, end=dec_last_20xx) - evtcount(DECR_HIGH_RISK_CUSTOMER, start=0, end=dec_last_20xx) > 0`` - -* Of those, how many were with PEPs? (self-declaration 4.2.) - - * Implementation: ``evtcount(INCR_PEP, start=0, end=dec_last_20xx) - evtcount(DECR_PEP, start=0, end=dec_last_20xx)`` - -* Of those PEPs, how many were with *foreign* PEPs? (self-declaration 4.3.) - - * Implementation: ``evtcount(INCR_PEP_FOREIGN, start=0, end=dec_last_20xx) - evtcount(DECR_PEP_FOREIGN, start=0, end=dec_last_20xx)`` - -* Number of other additional (other than PEPs and foreign PEPs) high-risk business relationships in 20XX (self-declaration 4.4.) - - * Implementation: Difference between 4.5. and 4.2 - -* Number of high-risk business relationship n total in 20xx (self-declaration 4.5.) - - * Implementation: ``evtcount(INCR_HIGH_RISK_CUSTOMER, start=0, end=dec_last_20xx) - evtcount(DECR_HIGH_RISK_CUSTOMER, start=0, end=dec_last_20xx)`` - -* Number of reports (substantiated suspicion) to MROS during 20xx (self-declaration 5.1) - - * Implementation: ``evtcount(REPORTED_SUSPICION_SUBSTANTIATED, range=year_20xx)`` - -* Number of reports (simple suspicion) to MROS during 20xx (self-declaration 5.2) - - * Implementation: ``evtcount(REPORTED_SUSPICION_SIMPLE, range=year_20xx)`` - -* Total number of reports to MROS during 20xx (self-declaration 5.3) +.. code:: none - * Implementation: ``evtcount(REPORTED_SUSPICION_SIMPLE, range=year_20xx) + evtcount(REPORTED_SUSPICION_SUBSTANTIATED, range=year_20xx)`` + open_start = accounts_at(ACCOUNT_OPEN, year_start) + open_end = accounts_at(ACCOUNT_OPEN, next_year_start) + open_during = accounts_during(ACCOUNT_OPEN, year_20xx) + opened = accounts_with_event(INCR_ACCOUNT_OPEN, year_20xx) + closed = accounts_with_event(DECR_ACCOUNT_OPEN, year_20xx) + + is_pep = PEP_DOMESTIC or PEP_FOREIGN or PEP_INTERNATIONAL_ORGANIZATION + high_risk = accounts_during( + ACCOUNT_OPEN and HIGH_RISK_CUSTOMER, year_20xx) + high_risk_pep = accounts_during( + ACCOUNT_OPEN and HIGH_RISK_CUSTOMER and is_pep, year_20xx) + high_risk_foreign_pep = accounts_during( + ACCOUNT_OPEN and HIGH_RISK_CUSTOMER and PEP_FOREIGN, year_20xx) + high_risk_country = accounts_during( + ACCOUNT_OPEN and HIGH_RISK_COUNTRY, year_20xx) + +Based on these sets, the VQF self-declaration statistics are: + +* Number of open AML files on January 1st (self-declaration 3.1.1): + ``size(open_start)``. +* Number of newly opened AML files during the year (self-declaration 3.1.2): + ``size(opened)``. +* Number of AML files managed at any time during the year (self-declaration + 3.1.3): ``size(open_during)``. +* Number of AML files closed during the year (self-declaration 3.1.4): + ``size(closed)``. +* Number of open AML files at year end (self-declaration 3.1.5): + ``size(open_end)``. +* Whether any high-risk business relationship was managed during the year + (self-declaration 4.1): ``size(high_risk) > 0``. +* Number of high-risk business relationships involving PEPs + (self-declaration 4.2): ``size(high_risk_pep)``. +* Number of those high-risk PEP relationships involving foreign PEPs + (self-declaration 4.3): ``size(high_risk_foreign_pep)``. +* Number of other high-risk business relationships, excluding those involving + PEPs (self-declaration 4.4): ``size(high_risk - high_risk_pep)``. +* Total number of high-risk business relationships (self-declaration 4.5): + ``size(high_risk)``. +* Whether any relationship involved a contractual party or beneficial owner + associated with a high-risk country (self-declaration 4.6): + ``size(high_risk_country) > 0``. +* Number of mandatory reports filed with MROS (self-declaration 5.1): + ``evtcount(MROS_REPORTED_SUSPICION_SUBSTANTIATED, year_20xx)``. +* Number of reports filed under the reporting right (self-declaration 5.2): + ``evtcount(MROS_REPORTED_SUSPICION_SIMPLE, year_20xx)``. +* Total MROS reports filed and notified to VQF (self-declaration 5.3): the sum + of the two event counts for 5.1 and 5.2. Only actual filing events may be + included. + +For example, an account that is open at the start of the year and closes in +June is included in 3.1.1, 3.1.3, and 3.1.4, but not 3.1.5. A relationship +that is high-risk in March and downgraded in April remains included in 4.1 and +4.5 for that reporting year. A PEP relationship is included in 4.2 only if it +was simultaneously open and classified as high-risk. A temporary high-risk- +country association is sufficient for 4.6. Two distinct MROS filing events +for the same account are counted as two reports under 5.1 through 5.3. Event Reporting (TOPS) ^^^^^^^^^^^^^^^^^^^^^^ -The following event-based statistics are custom-defined by us and shown in the AML officer dashboard. +The following statistics are custom-defined by us and shown in the AML officer +dashboard. ``reporting_period`` is an explicitly selected half-open calendar +period; it must not depend on the current date or an ambiguous ``last_year`` +shortcut. -* Number of accounts that are opened: +* Number of accounts that are currently open: - * Implementation: ``evtcount(INCR_ACCOUNT_OPEN) - evtcount(DECR_ACCOUNT_OPEN)`` + * Implementation: ``size(accounts_at(ACCOUNT_OPEN, now))`` -* Number of new GwG files in the last year. +* Number of new GwG files in the selected reporting period: - * Implementation: ``evtcount(INCR_ACCOUNT_OPEN, range=last_year)`` + * Implementation: + ``size(accounts_with_event(INCR_ACCOUNT_OPEN, reporting_period))`` -* Number of GwG files closed in the last year +* Number of GwG files closed in the selected reporting period: - * Implementation: ``evtcount(DECR_ACCOUNT_OPEN), range=last_year)`` + * Implementation: + ``size(accounts_with_event(DECR_ACCOUNT_OPEN, reporting_period))`` * Note: we only close GwG files after 1 year of inactivity, so implementation not exactly pressing ... -* Number of GwG files of high-risk customers +* Number of currently open GwG files of high-risk customers: + + * Implementation: + ``size(accounts_at(ACCOUNT_OPEN and HIGH_RISK_CUSTOMER, now))`` - * Implementation: ``evtcount(INCR_HIGH_RISK) - evtcount(INCR_HIGH_RISK)`` +* Number of currently open high-risk GwG files involving PEPs: -* Number of GwG files managed with "increased risk" due to PEP status + * Implementation: ``size(accounts_at(ACCOUNT_OPEN and HIGH_RISK_CUSTOMER and + (PEP_DOMESTIC or PEP_FOREIGN or PEP_INTERNATIONAL_ORGANIZATION), now))`` - * Implementation: ``evtcount(INCR_PEP) - evtcount(DECR_PEP)`` +* Number of MROS reports based on Art. 9 Abs. 1 GwG in the selected period: -* Number of MROS reports based on Art 9 Abs. 1 GwG (per year) + * Implementation: + ``evtcount(MROS_REPORTED_SUSPICION_SUBSTANTIATED, reporting_period)`` - * Implementation: ``evtcount(MROS_REPORTED_SUSPICION_SUBSTANTIATED, range=last_year)`` +* Number of MROS reports based on Art. 305ter Abs. 2 StGB in the selected + period: -* Number of MROS reports based on Art 305ter Abs. 2 StGB (per year) + * Implementation: + ``evtcount(MROS_REPORTED_SUSPICION_SIMPLE, reporting_period)`` - * Implementation: ``evtcount(MROS_REPORTED_SUSPICION_SIMPLE, range=last_year)`` +* Number of special clarifications concluded in the selected reporting period: -* Number of customers involved in proceedings for which Art 6 GwG did apply + * Implementation: + ``size(accounts_with_event(INCR_INVESTIGATION_CONCLUDED, reporting_period))`` - * Implementation: ``evtcount(INCR_INVESTIGATION, range=last_year)`` +Transactions with Increased Risk (TmeR) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Suspicious Transaction Reporting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +A TmeR ("Transaktion mit erhöhtem Risiko") is a transaction-monitoring +classification. It triggers special clarification but is not by itself a +suspicion, a decision to report, or evidence that a report was filed with MROS. +The special clarification records its result; a separate reporting decision +and an actual filing are required before an MROS reporting event may be emitted. -Also called TmeR ("Transaktionen mit erhoehtem Risiko"). -We define fixed criteria that apply to all customers. +We define fixed TmeR criteria that apply to all customers. Examples: @@ -2502,9 +2560,11 @@ FAQ * Q: What's the difference between simple/substantiated suspicion? - A: Simple suspicion is a suspicion according to Art 305ter Abs. 2 StGB. It is - a suspicion that *may* be reported ("Melderecht"). A substantiated suspicion - is according to Art. 9 GwG and *must* be reported ("Meldepflicht") + * A: A simple suspicion is a suspicion according to Art. 305ter Abs. 2 StGB + that *may* be reported ("Melderecht"). A substantiated suspicion under + Art. 9 GwG *must* be reported ("Meldepflicht"). Neither an alert nor a + TmeR alone establishes either result, and a reporting result must not be + recorded as an actual filing until the report has been submitted to MROS. References ----------