# Assembly code – Exotic option simulation

THIS PROGRAM SIMULATES THE BEHAVIOR OF AN EXOTIC OPTION.

THE PROGRAM ASKS THE USER WHETHER HE PREFERS TO BUY A PUT OR A CALL. SUBSEQUENTLY, IT PRICES THE DERIVATIVE VIA MONTECARLO. A SIMULATED UNDERLYING ASSET TRAJECTORY IS COMPUTED AND SHOWN THE USER. A PAYOFF IS CALCULATED AND THE USER IS FINALLY INFORMED OF THE OUTCOME OF HIS BET.

This is a project of a certain size that replicates, in Assembly code, some of the concepts studied in the Master’s Degree in Quantitative Finance. I created it in order to become more familiar with low-level programming.

To contain the complexity of the project, some mathematical and financial simplifications have been introduced. In particular, a Geometric Brownian Motion is used, while more appropriate models like Black-Scholes would be used in practice. The properties of the Gaussian distribution are also simplified, assuming daily variance, and the method of calculating the final price is simplified.

The code is deposited at https://github.com/giosds/giasm

The 8088 interpreter was used for development, Assembler and Tracer Toolkit: https://github.com/StoDevX/as88

Più sotto, in lingua inglese, è riportata la documentazione presente all’interno del codice.

ChatGPT

Here is the English documentation for the code:

Main Features:

• Generation of pseudorandom numbers, with a different sequence in each execution.
• Pricing of a double-barrier option using Monte Carlo simulation.
• Animated graphical representation of the Monte Carlo method.
• Generation of the random path of a hypothetical underlying asset and communication of the profit (or loss) generated by a derivative contract to the user.
• Text-based console graphics.
• Numerous limitations due to the architecture used, including the inability to perform floating-point arithmetic (only integer), 16-bit registers, and only 7 available system-library calls.
• Each element of the program is parameterized. For computational or other reasons (e.g., making certain components absolute or relative to each other), financial parameters undergo some manipulations in some cases, which the algorithm handles.

The first file calls the necessary functions, which are located in other linked files.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184

RANDOMNESS GENERATION

The limitations of the interpreter do not allow access to the system timer or other sources of randomness, so interaction with the user is used.

• The user is asked to hold down the Enter key for a certain amount of time and then press a different key. This generates and counts a certain number of characters (‘\n’); to make the interaction more realistic, too short a press resets the count. Of course, it would be possible to count individual Enter key presses, but this is not a cryptographic application…
• Three numbers are generated as described above. The lower 4 bits (corresponding to MOD 16, less manageable by the user) are retained for each of the 3 numbers. They are then recombined in various possible combinations to recreate 3 new 12-bit numbers, independent of the duration of the Enter key presses. These are the seeds that feed the random number generation algorithm.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
! THIS MODULE GENERATES RANDOMNESS ! The getnum function returns a random value ! Prints messages. User is asked to press key in order ! to generate one component of a random seed. ! Returns seed in AX  .SECT .TEXT                   !   ----------------------------------------------------------------- ! BUILD SEEDS ! Builds 3 random seeds using the 3 components ! Seeds vary between [0, 4367] getSeeds:                                                                     PUSH BP     MOV  BP,SP                                                        PUSH (chaos1)                                   ! Compute seed 1     PUSH (chaos2)     PUSH (chaos3)     CALL getSeed     MOV (seed1), AX                             ! Store seed1 in memory          PUSH (chaos3)                                   ! Compute seed 2     PUSH (chaos1)     PUSH (chaos2)     CALL getSeed     MOV (seed2), AX                             ! Store seed2 in memory          PUSH (chaos2)                                   ! Compute seed 3     PUSH (chaos3)     PUSH (chaos1)     CALL getSeed     MOV (seed3), AX                             ! Store seed3 in memory      ! PRINT SEEDS     MOV AX, step     MOV DX,(seed1)     CALL prnt_msg     MOV AX, step     MOV DX,(seed2)     CALL prnt_msg     MOV AX, step     MOV DX,(seed3)     CALL prnt_msg          MOV SP,BP                        POP BP     RET  !   ----------------------------------------------------------------- !   ASK USER TO GENERATE 3 NUMBERS get3nums:      PUSH BP     MOV  BP,SP           PUSH BX     PUSH DI               MOV AX,msg1                                     ! Msg to user     CALL prnt_msg     MOV BX,0     MOV CX, (lp_chaos)                          ! Times the input is asked     MOV DI,chaos3                                   ! 3rd seed component  1:MOV AX, step                                      !Msg to user     MOV DX,CX                                           !    Num value for print (loop n. CX)     CALL prnt_msg     CALL getNum                                     ! Get random seed: prompt msg to user                                                                     !       collects input, returns rnd seed [0,9]                                                                     !       uses BX to generate seed (seed accumulator)                                          STOS                                                        ! Store seed in AX to DI variable, then increment DI     MOV BX,0                                                ! Reset seed accumulator     LOOP 1b                                             ! More input asked for      POP DI                                                  ! Reset state     POP BX     MOV SP,BP                        POP BP     RET  getNum:                                  PUSH BP     MOV  BP,SP                       PUSH BX                                             ! Save state          MOV AX, prompt_ms                           ! Print message     CALL prnt_msg                                   ! =     CALL pause                                          ! Wait     PUSH _GETCHAR                               ! =     CALL  cls                                               ! Clear screen (azzera AL)      1:                                                              ! Enter-reading loop     INC BX                                                  ! Increment counter     SYS                                                     ! Get char      CMPB AL,'\n'                     JE 1b                                                       ! If Enter, continues loop.      rd:SYS                                                      ! Clears possible extra chars     CMPB AL,'\n'                                        ! =     JNE rd                                                  ! =          CMP BX, (n_chars)                               ! If not Enter, tries to exit loop: if BX below n_chars stays in, otherwise exits loop     JLE k_psh                                               ! If below threshold: prints message, decreases counter, loops back to 1     CALL  cls                                               ! Exit conditions satisfied:  exit getNum function     AND BX, 0XF                                     ! Only keeps the last 4 bits, yelding mod 16          MOV AX,BX                                           ! Return value in register AX                                                                      ! Reset state     POP BX     MOV SP,BP                                           ! Clear stack     POP BP     RET !   ----------------------------------------------------------------- ! PRINTS "KEEP PUSHING" if release is too quick k_psh:     PUSH BP                          MOV BP,SP     SUB BX, (bck_chars)                         ! Decrements counter     PUSH k_push             ! Prints 'keep pushing'     PUSH _PRINTF     SYS     PUSH _GETCHAR     SYS      CALL  cls     MOV SP,BP     POP BP     JMP 1b                                                  ! Back to loop   !   ----------------------------------------------------------------- !   GET ONE SEED OUT OF THREE !   KEYPRESS COMPONENTS !   components: number after AND (0-15)  getSeed:                                                    ! needs 3 components on the stack, returns                                                                     ! seed in AX     PUSH BP     MOV BP,SP         ! ADD AND SHIFT  ! Equivalent to: n1*16*16+n2*16+n3     MOVB CL,4                        MOV AX, 4(BP)                                   ! Use 1st seed component     SHL AX, CL                       ADD AX, 6(BP)                                   ! Use 2nd seed component     SHL AX, CL     ADD AX, 8(BP)                                   ! Use 3rd seed component     MOV SP,BP     POP BP     RET 6                                                   ! Clean up the stack

Starting from the seeds, various sequences are generated.

• Three linear congruential generators create a sequence of 48 uniform numbers.
• These three sequences interact to feed the seed of a fourth sequence of 48 numbers, which will have a longer period than each of the previous ones.
• A summation is performed on the 48 numbers, which will return a random variable distributed in a Gaussian manner.
• The Gaussian variable is standardized.

In the previous operations, difficulties arise due to the limited size of the registers (carry-over must be used) and the absence of decimal arithmetic. The formulas are adapted to address some of the issues upstream.

The new Gaussian number will be the random component of a Geometric Brownian Motion. It represents the path of a hypothetical underlying asset. As many Gaussian numbers are generated as there are intervals in the considered period (according to the provided parameters: 252 days).

• This Gaussian component is modified in terms of mean and variance according to the required financial parameters (drift and daily variance).
• It is necessary to limit the range of this Brownian motion to reduce the complexity of the arithmetic. Barriers come in handy here.
• The barriers, cap, and floor, when touched, fix the price of the underlying asset until the end of the period.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391

Many sequences are generated as there are iterations required for the Monte Carlo simulation.

• Each trajectory generates a Payoff, depending on financial parameters (strike levels and barriers).
• The iterations, exceeding the bits allowed by the CX register, require a double loop. Even the sums of Payoffs would exceed the allowed limits.
• In the inner loop, the average of Payoffs is calculated; in the outer loop, the average of averages.
• The average Payoff is updated according to an interest rate R.
 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178

The values of each trajectory will be displayed on the text interface using ASCII characters.

• A plot area is characterized by a certain number (configurable) of rows and columns. If the trajectory exceeds the expected number of columns, an additional plot area is generated.
• All values of the trajectory are scaled to fit within this matrix – the plot area. The price range is segmented into the available number of columns so that the entire trajectory is always visible (and does not overflow the matrix, which is composed of memory space with a length of n_columns * n_rows).
• During the Monte Carlo simulation, some trajectories are drawn. To ensure they remain visible for a sufficient amount of time, a large number of NOP (no-operation) instructions are inserted between them.
 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453

User interaction includes, in addition to generating random components, the choice of direction: whether to buy a PUT or a CALL option. After pricing the option, a final trajectory is generated; the Payoff is derived from it. The cost of the derivative is subtracted, and the outcome is communicated to the user.

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
! This file contains parameters that are allowed to be changed !   for debugging or usage.  !   ###################################### !   USER MODIFIABLE DATA  !   ----------------------------------------------------------------- !   FINANCIAL DATA   _days = 252!45                                     ! Length of the generated final sequence. Note: Tracer only supports up to 45. _startPr = 10000                                        ! The value of the asset at T0.                                                                      !   Should be small enough to fit in a register when growing,                                                                      !       big enough to get meaningful results when manipulating.                                                                     !       Multiple of 10.000 gets the drift right                                                                     !   Also used as the quotient of percent values _bpDrift =  0!25                                        ! Basis points; eg: 0.25% of 20000 basePct yelds 50 absolute  _bpReqStd=100                                      ! Basis points; eg: 0.100% of 20000 basePct yelds 200 absolute.                                                                     ! The absolute value should be a multiple of 4 for a good mean calculation _rateR = 200                                            ! Rate of return - yearly, in basis points.                                                                      ! Eg: price in T252 = 30000; bpR = 200; basePct = 20000 -> return 1%                                                                     ! Discount in T0: (30000*200)/20000=300  _put = TRUE  _strike = 10000  _cap = 12000  _floor = 8000    !   ###################################### !   DEBUG AND PROGRAM   !   ----------------------------------------------------------------- ! PLOTS ! Number of rows and columns _nRows = 30 _nCols = 150   !   -----------------------------------------------------------------  _debug = FALSE                                     ! TRUE if debug mode, FALSE otherwise                                                                         ! If DEBUG, jumps over the user input with preset values  _dbgSd1 = 50  _dbgSd2 = 70  _dbgSd3 = 90    .SECT .TEXT .SECT .DATA  .SECT .BSS  ! SEQUENCES rndSeq1: .SPACE 2*_nSeq rndSeq2: .SPACE 2*_nSeq rndSeq3: .SPACE 2*_nSeq rndSeq4: .SPACE 2*_nSeq  ! GAUSSIAN VARIABLES gaussSeq: .SPACE _days*2  ! BROWNIAN MOTION brownSeq: .SPACE _days*2
 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196

 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
! This file contains constants used along the program. ! No interaction is ever expected between the user and these values. ! The parameters the user is allowed to tweak (for debugging or usage)  !   are grouped in usrPrms.h  !   ###################################### ! SYSTEM AND LIBRARY CALLS  _EXIT      =      1  _GETCHAR   =    117  _SPRINTF   =    121 _PRINTF    =    127   !  ######################################  ! OTHER CONSTANTS  TRUE = 1  FALSE = 0    ZEROINT = 0  _HUNDRED = 100   !   ###################################### !   PROGRAM CONSTANTS  !   ----------------------------------------------------------------- !   SEQUENCES _nSeq = 48                                          !   Number of variables in a generated uniform sequence                                                                 !   Should match the gaussian generator                                                                 !   To be changed to obtain longer uniform sequences                                                                 !   DO NOT CHANGE: the formulas depend on it                                                                 !           In case it is changed, halfStd has                                                                 !           to be changed accordingly with the squared-root                                                                  !           formula in series.s   !   ----------------------------------------------------------------- !   MATH   _pcFier = 10000                               ! DO NOT CHANGE, used to set drift and Std percentages     !   ----------------------------------------------------------------- !   LOOPS   ! MONTECARLO     !   The Montecarlo trajectories amount to H*L     ! Neither should exceed 16 bit unsigned     ! Neither should exceed 32 bit unsigned, when multiplied by m_gauss  _hMcCount = 20  _lMcCount = 50    ! PLOT  NOP1 = 1000    ! Higher and lower parts of the waiting cycles for displaying Mc Plots  NOP2 = 32000  MAXMCPLT = 20  ! Total number of Montecarlo plots to display         .SECT .TEXT .SECT .DATA .SECT .BSS

OPTION BEHAVIOR: THE OPTION PAYS THE DIFFERENCE BETWEEN THE UNDERLYING ASSET AND THE STRIKE IF IT IS IN THE MONEY, ZERO OTHERWISE. THERE ARE TWO KNOCK-OUT BARRIERS, A CAP AND A FLOOR: IF THE ASSET REACHES A BARRIER, THE PRICE IS FIXED UNTIL EXPIRATION.

PROGRAM DESCRIPTION:

Whenever possible, multiplications are done before divisions, in order to contain remainder approximations.

The user helps generating 3 initial numbers by keeping the Enter key pressed for a while (too small a time is not allowed). The lowest binary digits of those initial numbers form the basis for new numbers. Three different combinations of those basis are assembled to form 3 different numbers, which are less predictable than the original ones. These new numbers are 3 random seeds for 3 Linear Congruential Generators. Three sequences are thus generated: they contain uniform pseudo-random variables. The 3 sequences interact to generate one longer-period sequence (still uniforms). This one is used as a source of randomness for all the simulations. Next: 48 uniform variables are used at a time to yeld an approximated gaussian-distributed random variable, relying on the Central Limit theorem. The number 48 is a convenient number, it being: 2 squared, times 12. Still, it produces better gaussians than 12. 12 is related to the uniform distribution standard deviation; 4 saves us a square-root computation, which would complicate matters in an environment where floating-point arithmetic is not available. The gaussian variable is appropriately standardized and scaled (the values must fit in the registers) and used to form a Geometric Brownian Motion for the prices of the asset. Then barriers are applied, limiting the asset prices. A payoff is computed, relative to a strike price. Using Montecarlo, many payoffs are averaged and discounted to price the derivative. Some of the asset trajectories are shown in an animated plot, in the text-based console. Prices are scaled and matched to a row number. If the columns allowed are fewer than the whole trajectory, the plot is split into as many parts as needed. The trajectory is rescaled for every example, so that each time the maximum and minimum values are placed on the upper and lower rows of the plot. The strike price is shown as a heavy line: ======

PARAMETERS FOR THE PROGRAM

USER MODIFIABLE PARAMETERS [usrPrms.h] file: The first section of the file contains the parameters that determine the asset trajectory in time and the behavior of the derivative. The user can modify them. _days: the number of days the simulation will cover. E.g. Tn = 252 _startPr: price of the asset in T0. It is also the basis for other transformations. 20000 performs better in divisions (smaller remainders), 10000 is nicer _bpDrift: drift of the asset, expressed in basis points, relative to the prices of the GBM _bpReqStd: the standard deviation which the simulator is required to generate for the asset _put: whether the option is a put or a call _strike: the strike price _cap: the upper barrier _floor: the lower barrier

DEBUG AND PROGRAM: Parameters used for debugging and fine-tuning the program. _nRows, _nCols: rows and columns to display the plot. _dbgSd1, _dbgSd2, _dbgSd3: random seeds used to bypass the user-generation section.

CONSTANTS [consts.h] file: This file should need no modifications. It is worth mentioning the LOOPS part. Two variables are used to overcome the 16-bit constraint on the CX register. _hMcCount * _lMcCount is the number of trajectories Montecarlo runs on. NOP1 * NOP2 is the number of cycles the program skips between the demonstrative plots.

## RUN AS: ./s88 main

ASSEMBLE AS: ./as88 main prFuncs series genrand vars mtCarlo prTraj

Note: Automatically translated from Italian.