← Financial markets technology: fundamentals and operations
09 / 10 · 60 MIN

Workshop: partial settlement and pending cancellation

Reconstruct portions, residuals, and maintenance requests without turning incomplete observations into a business conclusion.

Start with the obligation

The lesson uses a fictional instruction for 1500 units and 450001 cents. Start by identifying service, account, instrument, instruction reference, and currency. The same short reference can exist in another scope; finding the first record with that text is insufficient. Record the original total, confirmed outcomes, and unexplained remainder. This record helps L3 scope the incident and helps the manager communicate impact. No exercise field represents an instruction sent to actual financial infrastructure. These are controlled inputs for interpretation and reconciliation practice. Keep the original obligation alongside every derived result so a reviewer can reconstruct your conclusion.

Separate portions and residuals

The first exercise confirmation contains 900 units and 270000 cents. That leaves 600 units and 180001 cents. Keep the additional cent visible even if a dashboard displays whole euros. The ECB’s public partial-settlement explanation distinguishes executed portions from the remainder; the exercise uses its own numbers. It does not implement T2S selection algorithms, windows, minimums, or rounding rules. It receives already confirmed amounts and checks conservation of totals. Predicting the remainder before running the code helps reveal a mapping defect that a success message would miss. Compare securities quantities and cash separately rather than combining them into one success flag.

A request is not an outcome

Sending cancellation does not automatically turn the remaining 600 units into cancelled units. In the inspected GFS bilateral flow, a counterparty dependency can keep the request pending. Read maintenance state and the referenced instruction state while retaining their identifiers. Under the local contract, only cancel-confirm transfers the remainder to the cancelled portion; the 900 settled units remain in history. The equality original = settled + cancelled + pending must remain true. If a delayed confirmation then arrives, model rejection requires reconciliation; it does not prove the external movement invalid. Establish effective times and authoritative confirmations before deciding on any resubmission.

Interpret status dimensions

The case contains a complete snapshot with matched and two holds. The next removes PARTY but retains CSD. The consumer must represent the remaining hold. ECB guidance on multiple statuses helps explain why one ready label can hide restrictions. The program does not decide whether an infrastructure may settle: it accounts for observations and provides a local follow-up flag. A confirmation can arrive after a status describing another instant. Separate origin, receipt, and processing times during investigation. Do not erase a movement merely because the current dashboard displays a hold whose timeline has not yet been reconciled. Record the inconsistency as an explicit exception.

Replay without duplicating effects

Copy the program into a temporary directory and run python3 run.py --output evidence.json using Python 3.13. The model reduces identical source/id repeats and rejects changed content under the same identity. It also checks movementRef: a new message id does not turn an old movement into a second movement. The function uses copies and leaves inputs intact on failure. This in-memory property does not demonstrate persistence, distributed transactions, or crash recovery. First duplicate a message; then change only its id while retaining the economic reference. Explain why the results differ and which evidence you would need to qualify a real replay process.

Deliver a checkable conclusion

Allow fifteen minutes to predict portions, fifteen to execute cases, and fifteen to introduce conflicts. In the final fifteen minutes, produce a short note stating original obligation, confirmed portions, remainder, references, uncertainties, owner, and next update. Another person should be able to repeat the arithmetic without asking which file you selected. Confirm the version applicable to the environment: the inspected index lists R2026.NOV documents published in September and R2026.JUN as previous, which does not establish activation in your environment. Lab conclusions cover only local rules and executed cases. Actual acceptance requires authorized contracts, systems, and participants.

"""Original DR post-trade observation exercise, Python 3.13.
Controlled inputs only; no T2S/DTCC messages, banking ledger or execution.
python3 run.py --output evidence.json
"""
import argparse
from copy import deepcopy
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import sys

checks=[]
def check(name,actual,expected):
 assert actual==expected,(name,actual,expected)
 checks.append(dict(name=name,actual=actual,expected=expected,passed=True))
def reject(fn):
 try:fn
 except ValueError as e:return str(e)
 raise AssertionError('Expected rejection')
def positive(value):
 if type(value)is not int or value<=0:raise ValueError('positive integer required')
 return value
def unique(events):
 seen={};out=[]
 for e in events:
 key=(e['source'],e['id'])
 if key in seen:
 if seen[key]!=e:raise ValueError('message identity conflict')
 continue
 seen[key]=deepcopy(e);out.append(deepcopy(e))
 return out

def settlement(instruction,events):
 total=positive(instruction['quantity']);amount=positive(instruction['cashCents'])
 out=dict(original=total,settled=0,cancelled=0,pending=total,cashSettled=0,cashCancelled=0,cashPending=amount,matched=False,holds=[],cancelRequested=False,cancelConfirmed=False,movements=[])
 seen=set
 for e in unique(events):
 if e['scope']!=instruction['scope']:raise ValueError('instruction scope mismatch')
 kind=e['kind']
 if kind=='status':
 if type(e['matched'])is not bool or not isinstance(e['holds'],list):raise ValueError('invalid status snapshot')
 out['matched']=e['matched'];out['holds']=sorted(set(e['holds']))
 elif kind=='cancel-request':out['cancelRequested']=True
 elif kind=='cancel-confirm':
 if out['cancelConfirmed']:raise ValueError('duplicate cancellation outcome')
 out['cancelConfirmed']=True;out['cancelRequested']=False
 out['cancelled']=out['pending'];out['cashCancelled']=out['cashPending']
 out['pending']=0;out['cashPending']=0
 elif kind=='settlement':
 if out['cancelConfirmed']:raise ValueError('settlement after cancellation needs reconciliation')
 if e['movementRef']in seen:raise ValueError('duplicate movement reference')
 qty=positive(e['quantity']);cash=positive(e['cashCents'])
 if qty>out['pending'] or cash>out['cashPending']:raise ValueError('exceeds remaining obligation')
 if (qty==out['pending'])!=(cash==out['cashPending']):raise ValueError('final quantity cash mismatch')
 seen.add(e['movementRef']);out['movements'].append(e['movementRef'])
 out['settled']+=qty;out['pending']-=qty;out['cashSettled']+=cash;out['cashPending']-=cash
 else:raise ValueError('unknown settlement observation')
 # Diagnostic local flag only; it does not command or permit settlement.
 out['localFollowUp']=out['pending']>0 and (bool(out['holds'])or not out['matched']or out['cancelRequested'])
 return out

def corporate(scope,events):
 out=dict(noticeRevision=None,preAdvice={},credits={},reversed=[],adjustments={},net=0)
 effect_refs=set
 for e in unique(events):
 if e['scope']!=scope:raise ValueError('corporate scope mismatch')
 kind=e['kind']
 if kind=='notice':
 revision=positive(e['revision'])
 if out['noticeRevision'] is not None and revision<=out['noticeRevision']:raise ValueError('non increasing local notice revision')
 out['noticeRevision']=revision
 elif kind=='preadvice':out['preAdvice'][e['id']]=positive(e['amount'])
 elif kind=='cancel-preadvice':
 if e['ref']not in out['preAdvice']:raise ValueError('missing preadvice reference')
 del out['preAdvice'][e['ref']]
 elif kind in('credit','reversal','adjustment'):
 if e['effectRef']in effect_refs:raise ValueError('duplicate economic reference')
 if kind=='credit':
 value=positive(e['amount']);out['credits'][e['effectRef']]=value;out['net']+=value
 else:
 parent=e['ref']
 if parent not in out['credits']:raise ValueError('missing original credit')
 if parent in out['reversed']:raise ValueError('original credit already reversed')
 if kind=='reversal':
 value=positive(e['amount'])
 if value!=out['credits'][parent]:raise ValueError('reversal amount mismatch')
 if any(a['ref']==parent for a in out['adjustments'].values):raise ValueError('adjusted credit requires separate reconciliation')
 out['reversed'].append(parent);out['net']-=value
 else:
 value=e['amount']
 if type(value)is not int or value==0:raise ValueError('nonzero integer adjustment required')
 if out['net']+value<0:raise ValueError('outside nonnegative fixture scope')
 out['adjustments'][e['effectRef']]=dict(ref=parent,amount=value);out['net']+=value
 effect_refs.add(e['effectRef'])
 else:raise ValueError('unknown corporate observation')
 return out

def deadline(received,limit):
 a=datetime.fromisoformat(received);b=datetime.fromisoformat(limit)
 if a.utcoffsetis None or b.utcoffsetis None:raise ValueError('explicit UTC offset required')
 return (a.astimezone(timezone.utc)<=b.astimezone(timezone.utc))

def main:
 sc=['DR-CSD','ACCOUNT-A','INSTRUMENT-X','INSTRUCTION-1','EUR']
 instruction=dict(scope=sc,quantity=1500,cashCents=450001)
 def se(id,kind,**extra):return dict(source='DR-OBS',id=id,scope=sc,kind=kind,**extra)
 status=se('s1','status',matched=True,holds=['CSD','PARTY'])
 part=se('s2','settlement',movementRef='M1',quantity=900,cashCents=270000)
 req=se('s3','cancel-request')
 events=[status,part,req];saved=deepcopy(events);a=settlement(instruction,events)
 check('partial quantity remains',a['pending'],600)
 check('partial cash remainder preserves cent',a['cashPending'],180001)
 check('request preserves settled quantity',a['settled'],900)
 check('request does not cancel remainder',a['cancelled'],0)
 check('request remains pending',a['cancelRequested'],True)
 check('matching does not erase holds',a['holds'],['CSD','PARTY'])
 check('partial request needs follow up',a['localFollowUp'],True)
 release=se('s4','status',matched=True,holds=['CSD'])
 check('one hold release retains another',settlement(instruction,[status,release])['holds'],['CSD'])
 b=settlement(instruction,events+[se('s5','cancel-confirm')])
 check('confirmed cancellation retains settled quantity',b['settled'],900)
 check('confirmed cancellation closes remaining quantity',b['cancelled'],600)
 check('confirmed cancellation retains cash remainder separately',b['cashCancelled'],180001)
 check('quantity conservation after cancellation',b['settled']+b['cancelled']+b['pending'],1500)
 check('cash conservation after cancellation',b['cashSettled']+b['cashCancelled']+b['cashPending'],450001)
 final=se('s6','settlement',movementRef='M2',quantity=600,cashCents=180001)
 complete=settlement(instruction,[part,final])
 check('final movement consumes residual cent',complete['cashSettled'],450001)
 check('complete quantity has no pending remainder',complete['pending'],0)
 check('identical message replay unchanged',settlement(instruction,[part,part]),settlement(instruction,[part]))
 check('changed payload with same identity rejected',reject(lambda:settlement(instruction,[part,{**part,'quantity':800}])),'message identity conflict')
 check('new message cannot reuse movement reference',reject(lambda:settlement(instruction,[part,{**part,'id':'new'}])),'duplicate movement reference')
 check('excess quantity rejected',reject(lambda:settlement(instruction,[{**part,'quantity':1501}])),'exceeds remaining obligation')
 check('final quantity with incomplete cash rejected',reject(lambda:settlement(instruction,[part,{**final,'cashCents':180000}])),'final quantity cash mismatch')
 check('wrong account scope rejected',reject(lambda:settlement(instruction,[{**part,'scope':['OTHER']}])),'instruction scope mismatch')
 check('late settlement after cancel held for reconciliation',reject(lambda:settlement(instruction,events+[se('s5','cancel-confirm'),final])),'settlement after cancellation needs reconciliation')
 check('input history preserved',events==saved,True)
 ca=['DR-CA','EVENT-1','ACCOUNT-A','INSTRUMENT-X','CASH','OPTION-1','EUR']
 def ce(id,kind,**extra):return dict(source='DR-OBS',id=id,scope=ca,kind=kind,**extra)
 notice=ce('a1','notice',revision=1);pre=ce('a2','preadvice',amount=7200)
 credit=ce('a3','credit',effectRef='C1',amount=7200)
 reversal=ce('a4','reversal',effectRef='R1',ref='C1',amount=7200)
 replacement=ce('a5','credit',effectRef='C2',amount=7500)
 check('announcement does not book cash',corporate(ca,[notice])['net'],0)
 check('preadvice does not book cash',corporate(ca,[notice,pre])['net'],0)
 check('cancel preadvice keeps net unchanged',corporate(ca,[pre,ce('a6','cancel-preadvice',ref='a2')])['net'],0)
 check('confirmed credit books amount',corporate(ca,[credit])['net'],7200)
 check('reversal removes original credit effect',corporate(ca,[credit,reversal])['net'],0)
 corrected=corporate(ca,[notice,pre,credit,ce('a7','notice',revision=2),reversal,replacement])
 check('replacement follows reversal without double count',corrected['net'],7500)
 check('original credit retained in history',corrected['credits']['C1'],7200)
 check('replayed credit counts once',corporate(ca,[credit,credit])['net'],7200)
 check('reversal before original is held',reject(lambda:corporate(ca,[reversal])),'missing original credit')
 check('mismatched reversal amount rejected',reject(lambda:corporate(ca,[credit,{**reversal,'amount':7100}])),'reversal amount mismatch')
 check('second reversal with new id rejected',reject(lambda:corporate(ca,[credit,reversal,{**reversal,'id':'a8','effectRef':'R2'}])),'original credit already reversed')
 check('same effect with new message rejected',reject(lambda:corporate(ca,[credit,{**credit,'id':'a9'}])),'duplicate economic reference')
 adj=ce('a10','adjustment',effectRef='D1',ref='C1',amount=-300)
 check('delta adjustment does not repay full value',corporate(ca,[credit,adj])['net'],6900)
 check('adjustment retains original credit',corporate(ca,[credit,adj])['credits']['C1'],7200)
 check('wrong corporate scope rejected',reject(lambda:corporate(ca,[{**credit,'scope':['OTHER']}])),'corporate scope mismatch')
 check('old notice revision does not overwrite new',reject(lambda:corporate(ca,[ce('a11','notice',revision=2),notice])),'non increasing local notice revision')
 check('offsets identify same deadline instant',deadline('2026-10-04T16:00:00+01:00','2026-10-04T15:00:00+00:00'),True)
 check('one second past deadline rejected locally',deadline('2026-10-04T16:00:01+01:00','2026-10-04T15:00:00+00:00'),False)
 check('naive time cannot prove deadline',reject(lambda:deadline('2026-10-04T16:00:00','2026-10-04T15:00:00+00:00')),'explicit UTC offset required')
 check('equal wall clocks can be different instants',deadline('2026-10-04T16:00:00+00:00','2026-10-04T16:00:00+01:00'),False)
 check('fractional local quantity rejected',reject(lambda:settlement(instruction,[{**part,'quantity':900.5}])),'positive integer required')
 check('reversal after adjustment needs separate reconciliation',reject(lambda:corporate(ca,[credit,adj,reversal])),'adjusted credit requires separate reconciliation')
 out=dict(scope='Original local observation reducers only; no T2S/DTCC protocol, settlement execution, bank ledger, legal calendar, actual instruction or independent acceptance.',python=sys.version.split[0],runnerSha256=hashlib.sha256(Path(__file__).read_bytes).hexdigest,passed=len(checks),checks=checks)
 parser=argparse.ArgumentParser;parser.add_argument('--output',required=True);Path(parser.parse_args.output).write_text(json.dumps(out,indent=2)+'\n');print(json.dumps({'passed':len(checks)}))
if __name__=='__main__':main
IN PRACTICE

Fictional case: 900 units settled, 600 pending, and cancellation requested. The dashboard closes the instruction too early; reconciliation preserves portions and the 180001-cent remainder.

Common pitfalls

Summing snapshots as movements; cancelling settled portions; losing residuals; interpreting matched as settled; confusing local rejection with external invalidity.

Related topics: FIX sessions and order state · Post-trade and settlement · Operations, change, and T+1 readiness

Take this idea with you

Preserving the original obligation, portions, and uncertainty makes the outcome explainable and supports the next decision without fabricating confirmations.

Create account

Reference: T2S General Functional Specifications R2026.JUN · BigSavant financial markets technology professional assessment2026.10