--- title: "Stock Market Simulation" id: 57260 type: "computer_media" slug: "stock-market-simulation" url: "http://localhost/computer_media/stock-market-simulation/" markdown_url: "http://localhost/computer_media/stock-market-simulation.md" published_at: "2024-10-04T07:44:14+00:00" modified_at: "2026-04-03T07:58:39+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/148_SMS.png" excerpt: "Trade five fictional stocks across multiple days, manage a portfolio with brokerage fees, and watch for surprise dividends and stock splits in this BASIC market simulation." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" indiv: - name: "Donald Burgio" slug: "donald-burgio" taxonomy: "indiv" url: "http://localhost/indiv/donald-burgio/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" - name: "Simulation" slug: "simulation" taxonomy: "genre" url: "http://localhost/type/simulation/" media_contents: - id: 56734 title: "Timex Sinclair Public Domain Library Tape 1003" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1003/" media_type: "Program" programmers: - name: "Donald Burgio" slug: "donald-burgio" taxonomy: "indiv" url: "http://localhost/indiv/donald-burgio/" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/10/148_SMS.png" media_type_tags: "Game, Simulation" --- This program simulates a simple stock market where the player trades shares in five fictional companies (AAA through EEE) starting with $10,000 in capital. Each trading day the player enters buy or sell quantities for each stock, with a 1% brokerage fee applied to all transactions. Stock prices change each turn using a combination of a market-wide trend factor, random noise, and occasional “bull” or “bear” events that apply a ±4 point shock to a randomly selected stock. The program also simulates dividend payments and stock splits, and tracks a running exchange average similar to a market index. *** ## Program Analysis ### Program Structure The program is divided into several logical sections: 1. **Initialization (lines 10–120):** Sets up string arrays for stock ticker names, defines the price-formatting expression string `A$`, and jumps to the main setup block. 2. **Currency Formatter Subroutine (lines 130–220):** Converts a numeric value in `G` into a formatted dollar string `R$` with exactly two decimal places, returning the string length in `L`. 3. **Game Setup (lines 230–780):** Initialises stock prices, asks for the player name, optionally shows instructions, and waits for the player to press S to start. 4. **Main Display Loop (lines 790–2000):** Displays stock prices, portfolio value, handles player transactions, updates prices, and checks for special events (dividends, splits). 5. **Price Update Subroutine (lines 2010–2500):** Called via `GOSUB 2010` each turn; updates all five stock prices with random noise, a trend factor, and occasional bull/bear shocks. 6. **End Game (lines 2510–2800):** Displays profit/loss summary and waits for the player to press S to return to BASIC. ### Currency Formatting Subroutine (lines 130–220) The subroutine at line 130 is the most algorithmically involved part of the program. It extracts integer and fractional parts of `G` using `ABS`, `INT`, and subtraction, then patches up the fractional portion to always produce two decimal digits. The logic at lines 170–200 handles three cases: - If the fractional part is zero, `O$` is set to `"00"`. - If the fractional part is non-zero, it multiplies by 100 and converts to a string. - If the resulting string has a decimal point in its second character (i.e., the value was less than 0.10), it multiplies by 1000 instead to recover a leading zero — a workaround for `STR$` not zero-padding. The formatted result is assembled as `Z$+"."+O$` and its length stored in `L` for right-aligned `TAB` printing. ### Stock Price Update Algorithm The subroutine at line 2010 updates all five stock prices each day. It uses a market-wide factor `A` (a small positive or negative decimal, rerolled periodically by the sub-subroutine at line 2460) multiplied by the current price, plus a random component selected from four quantised levels (0, 0.25, 0.5, 0.75), plus an integer noise term `INT(3-6*RND+.5)` which ranges from −3 to +3. Two “event” counters `D1` and `D2` track countdown timers for bull (`BC=4`) and bear (`BC=-4`) shocks. When a counter expires the shock is applied to one randomly chosen stock. The counters are initialised and reset via `VAL A$`, which evaluates the expression `"INT(RND*4.99)+1"` stored at line 50 to produce a random integer from 1 to 5. ### Notable Techniques - **`VAL A$` as a reusable RNG expression:** The string `A$="INT (RND*4.99)+1"` is evaluated with `VAL` wherever a random integer from 1–5 is needed. This is a compact way to reuse an expression without a subroutine and also saves token memory. - **Right-aligned currency printing:** Values are right-aligned using `TAB (N-L)` where `L` is the length of the formatted string returned by the formatter subroutine, giving a columnar appearance without fixed-width formatting. - **Flicker-free title animation:** Lines 390–410 alternate printing an inverse-video version and a normal version of the title banner in a loop of 20 iterations, creating a simple blinking effect. - **`POKE 16437,255`:** This pokes the ZX81 system variable FRAMES (low byte) to reset the frame counter, effectively preventing an unwanted `BREAK` or timeout after a `PAUSE` statement — a well-known ZX81 technique. - **Random number truncation at line 1930:**`VAL ((STR$ RND)(1 TO 4))` takes the first four characters of the string representation of a random number, then evaluates it back to a float. This clips the value to at most three decimal places and is used to produce a scaled random value for dividend/split decisions. ### Bugs and Anomalies | Line | Issue | | --- | --- | | `720` | `LET S=(5)=115` is syntactically malformed. The intent is `LET S(5)=115` (initialise the fifth stock price). As written, this evaluates `(5)=115` as a boolean (false=0) and assigns the result to the scalar variable `S`, leaving `S(5)` at its default value of 0. | | `1980` | `IF B>.93` should almost certainly be `IF R>.93` (checking the random value for a stock split). `B` is not defined in this scope, so the condition likely always evaluates as false, meaning stock splits never trigger. | | `1990` | The stock split doubles `P(I)` (shares held) but does not halve `S(I)` (price per share). A real split should halve the price and double the shares to keep total value constant; as written the player’s portfolio value doubles on a split. | | `2110` | `LET D2=D2-2` decrements `D2` by 2 each day while `D1` is decremented by 1. This means the bear-shock counter expires roughly twice as fast as the bull-shock counter, creating an asymmetric market bias. | | `1570` | The oversell check `IF -T(I)<=P(I)` is inverted. When `T(I)` is negative (a sell order), `-T(I)` is the number of shares being sold. The condition allows the sale if the number sold is ≤ shares held, which is correct, but the logic only reaches this line when `T(I)<=0` (line 1530), so a zero-share “sell” of 0 passes through without issue. | ### Variable Summary | Variable | Purpose | | --- | --- | | `A$` | Expression string for random integer 1–5, evaluated with `VAL` | | `I$(5,3)` | Ticker symbols for the five stocks | | `S(5)` | Current share prices | | `P(5)` | Shares held in portfolio | | `T(5)` | Transaction quantities entered by player | | `C(5)` | Daily price change per stock | | `C` | Cash balance | | `A` | Market trend multiplier (signed small decimal) | | `D1`, `D2` | Countdown timers for bull/bear shock events | | `G` | Input value to currency formatter subroutine | | `R$`, `L` | Formatted string and its length, returned by formatter | | `DY` | Day counter | | `F` | Flag: 0 on first pass, 1 after first trading day | | `EA` | Exchange average (mean of five prices) | ## Source Code ``` 10 REM STOCK MARKET SIMULATION 20 REM BY DONALD A. BURGIO 25 CLS 30 DIM I$(5,3) 40 DIM O$(2) 50 LET A$="INT (RND*4.99)+1" 60 LET I$(1)="AAA" 70 LET I$(2)="BBB" 80 LET I$(3)="CCC" 90 LET I$(4)="DDD" 100 LET I$(5)="EEE" 110 RAND 120 GOTO 230 130 LET X=ABS G 140 LET B=INT X 150 LET E=X-B 160 LET Z$=STR$ B 170 IF E=0 THEN LET O$="00" 180 IF E<>0 THEN LET O$=STR$ (100*E) 190 IF O$(2)="." THEN LET O$=STR$ ((1000*E)+.01) 200 LET R$=Z$+"."+O$ 210 LET L=LEN R$ 220 RETURN 230 LET A=INT ((RND/10)*100+.5)/100 240 DIM S(5) 250 DIM P(5) 260 DIM T(5) 270 DIM C(5) 280 LET TT=0 290 LET F=0 300 LET D1=0 310 LET D2=0 320 LET P=0 330 LET P2=0 340 LET DY=0 350 LET EA=0 360 SLOW 370 CLS 380 FOR I=1 TO 20 390 PRINT AT 0,0;"% % % % % % %T%H%E% %Z%X% %S%T%O%C%K% %E%X%C%H%A%N%G%E% % % % % " 400 PRINT AT 0,0;"% % % % % % THE ZX STOCK EXCHANGE% % % % % " 410 NEXT I 420 PRINT AT 3,0;"WHAT IS YOUR NAME?" 430 INPUT N$ 440 PRINT AT 3,0;"DO YOU WANT INSTRUCTIONS? (Y/N)" 450 INPUT Z$ 460 IF Z$="N" THEN PRINT AT 3,0;" " 470 IF Z$="N" THEN GOTO 680 480 CLS 490 PRINT "WELCOME TO THE ZX STOCK EXCHANGE" 500 PRINT TAB ((32-LEN N$)/2);N$+"." 510 PRINT "YOUR ACCOUNT CURRENTLY CONTAINS" 520 PRINT "$10,000. YOU MAY BUY OR SELL" 530 PRINT "STOCKS. A TABLE OF AVAILABLE " 540 PRINT "STOCK, THEIR PRICES, AND THE" 550 PRINT "NUMBER OF SHARES IN YOUR PORT-" 560 PRINT "FOLIO WILL BE PRINTED. FOLLOW-" 570 PRINT "ING THIS THE INITIALS OF EACH" 580 PRINT "STOCK WILL BE PRINTED. HERE YOU" 590 PRINT "INDICATE A TRANSACTION. TO BUY" 600 PRINT "A STOCK TYPE XXX, WHERE XXX IS" 610 PRINT "THE NUMBER OF SHARES YOU WISH TO" 620 PRINT "BUY. TO SELL TYPE -XXX, WHERE" 630 PRINT "-XXX IS THE NUMBER OF SHARES YOU" 640 PRINT "WISH TO SELL. A 1 PERCENT BRO-" 650 PRINT "KERAGE FEE WILL AUTOMATICALLY" 660 PRINT "BE CHARGED TO YOUR ACCOUNT." 670 PRINT TAB 11;"GOOD LUCK" 680 LET S(1)=130 690 LET S(2)=90 700 LET S(3)=120 710 LET S(4)=85 720 LET S=(5)=115 730 LET TR=VAL A$ 740 PRINT AT 21,7;"PRESS S TO START." 750 IF INKEY$="S" THEN GOTO 790 760 PRINT AT 0,0;"%W%E%L%C%O%M%E% %T%O% %T%H%E% %Z%X% %S%T%O%C%K% %E%X%C%H%A%N%G%E" 770 PRINT AT 0,0;"WELCOME TO THE ZX STOCK EXCHANGE" 780 GOTO 750 790 IF RND>.5 THEN GOTO 810 800 LET A=-A 810 CLS 820 GOSUB 2010 830 LET C=10000 840 PRINT 850 PRINT "STOCK INT. $/SHARE" 860 PRINT "--------------------------------" 870 LET G=S(1) 880 GOSUB 130 890 PRINT "A AND A ASSOCIATES AAA ";TAB (32-L);R$ 900 LET G=S(2) 910 GOSUB 130 920 PRINT "B AND B BUYERS BBB ";TAB (32-L);R$ 930 LET G=S(3) 940 GOSUB 130 950 PRINT "C AND C COAL CO. CCC ";TAB (32-L);R$ 960 LET G=S(4) 970 GOSUB 130 980 PRINT "D AND D DEVELOPERS DDD ";TAB (32-L);R$ 990 LET G=S(5) 1000 GOSUB 130 1010 PRINT "E AND E ENERGY EEE ";TAB (32-L);R$ 1020 LET TA=EA 1030 LET EA=0 1040 LET SA=0 1050 FOR I=1 TO 5 1060 LET EA=EA+S(I) 1070 LET SA=SA+S(I)*P(I) 1080 NEXT I 1090 LET EA=INT (100*(EA/5)+.5)/100 1100 LET NC=INT ((EA-TA)*100+.5)/100 1110 LET D=SA+C 1120 IF F THEN GOTO 1160 1130 PRINT 1140 PRINT "ZX STOCK EXCHANGE AVER.:";EA 1150 GOTO 1190 1160 PRINT 1170 PRINT "ZX STOCK EXCHANGE AVER.:";EA 1180 PRINT "NET CHANGE:";NC 1190 PRINT 1200 PRINT N$;":" 1210 LET SA=INT (100*SA+.5)/100 1220 LET G=SA 1230 GOSUB 130 1240 PRINT "STOCK ASSETS=$";TAB (24-L);R$ 1250 LET C=INT (100*C+.5)/100 1260 LET G=C 1270 GOSUB 130 1280 PRINT "CASH ASSETS= $";TAB (24-L);R$ 1290 LET D=INT (100*D+.5)/100 1300 LET G=D 1310 GOSUB 130 1320 PRINT "TOTAL ASSETS=$";TAB (24-L);R$ 1330 PRINT 1340 IF NOT F THEN PAUSE 225 1350 IF NOT F THEN POKE 16437,255 1360 IF NOT F THEN GOTO 1400 1370 PRINT "DO YOU WISH TO CONTINUE? (Y/N)" 1380 INPUT C$ 1390 IF C$="N" THEN GOTO 2510 1400 FOR I=10 TO 21 1410 PRINT AT I,0;" " 1420 NEXT I 1430 PRINT AT 11,0;"WHAT IS YOUR TRANSACTION IN:" 1440 FOR I=1 TO 5 1450 PRINT AT 12,0;I$(I);"?" 1460 INPUT T(I) 1470 NEXT I 1480 PRINT AT 20,0;"PLEASE WAIT..." 1490 LET DP=0 1500 LET DS=0 1510 FOR I=1 TO 5 1520 LET T(I)=INT (T(I)+.5) 1530 IF T(I)<=0 THEN GOTO 1560 1540 LET DP=DP+T(I)*S(I) 1550 GOTO 1620 1560 LET DS=DS-T(I)*S(I) 1570 IF -T(I)<=P(I) THEN GOTO 1620 1580 PRINT AT 20,0;"YOU HAVE OVERSOLD A STOCK; TRY AGAIN. " 1590 PAUSE 300 1600 POKE 16437,255 1610 GOTO 1400 1620 NEXT I 1630 LET TT=DP+DS 1640 LET BF=INT (.01*TT*100+.5)/100 1650 LET CT=C-DP-BF+DS 1660 IF CT>=0 THEN GOTO 1720 1670 PRINT AT 19,0;"YOU HAVE TRIED TO SPEND " 1680 PRINT "$";-CT;" MORE THAN YOU HAVE." 1690 PAUSE 300 1700 POKE 16437,255 1710 GOTO 1400 1720 LET C=CT 1730 FOR I=1 TO 5 1740 LET P(I)=P(I)+T(I) 1750 NEXT I 1760 CLS 1770 GOSUB 2010 1780 LET DY=DY+1 1790 PRINT "*** END OF TRADING: DAY ";DY;" ***" 1800 PRINT 1810 PRINT "STK. $/SHR. HDS. $ VALUE CHANGE" 1820 PRINT "................................" 1830 FOR I=1 TO 5 1840 LET G=S(I) 1850 GOSUB 130 1860 PRINT AT 3+I,0;I$(I);AT 3+I,(11-L);R$;AT 3+I,13;P(I); 1870 LET G=S(I)*P(I) 1880 GOSUB 130 1890 PRINT AT 3+I,(24-L);R$;AT 3+I,26;C(I) 1900 NEXT I 1910 LET F=1 1920 PRINT 1930 LET R=VAL ((STR$ RND)(1 TO 4)) 1940 LET I=INT (RND*7) 1945 IF I>5 THEN GOTO 1940 1950 IF I=0 THEN GOTO 1940 1960 IF R<.15 THEN PRINT I$(I);" DECLARES DIVIDENDS OF $";(R*4+.5);"/SHARE" 1970 IF R<.15 THEN LET C=C+P(I)*(R*4+.5) 1980 IF B>.93 THEN PRINT I$(I);" SPLITS STOCK" 1990 IF R>.93 THEN LET P(I)=P(I)*2 2000 GOTO 1020 2010 FAST 2020 IF D1>0 THEN GOTO 2060 2030 LET S=VAL A$ 2040 LET D1=VAL A$ 2050 LET P=1 2060 IF D2>0 THEN GOTO 2100 2070 LET S2=VAL A$ 2080 LET D2=VAL A$ 2090 LET P2=1 2100 LET D1=D1-1 2110 LET D2=D2-2 2120 FOR I=1 TO 5 2130 LET R=RND 2140 IF R>.25 THEN GOTO 2170 2150 LET R=.25 2160 GOTO 2240 2170 IF R>.5 THEN GOTO 2200 2180 LET R=.5 2190 GOTO 2240 2200 IF R>.75 THEN GOTO 2230 2210 LET R=.75 2220 GOTO 2240 2230 LET R=0 2240 LET BC=0 2250 IF P<1 THEN GOTO 2290 2260 IF INT (S+.5)<>INT (I+.5) THEN GOTO 2290 2270 LET BC=4 2280 LET P=0 2290 IF P2<1 THEN GOTO 2330 2300 IF INT (S2+.5)<>INT (I+.5) THEN GOTO 2330 2310 LET BC=-4 2320 LET P2=0 2330 LET C(I)=INT (A*S(I))+R+INT (3-6*RND+.5)+BC 2340 LET C(I)=INT (100*C(I)+.5)/100 2350 LET S(I)=S(I)+C(I) 2360 IF S(I)>0 THEN GOTO 2400 2370 LET C(I)=0 2380 LET S(I)=0 2390 GOTO 2410 2400 LET S(I)=INT (100*S(I)+.5)/100 2410 NEXT I 2420 LET TR=TR-1 2430 IF TR<1 THEN GOSUB 2460 2440 SLOW 2450 RETURN 2460 LET TR=VAL A$ 2470 LET A=INT ((RND/10)*100+.5)/100 2480 IF RND<=.5 THEN GOTO 2500 2490 LET A=-A 2500 RETURN 2510 CLS 2520 PRINT 2530 PRINT 2540 PRINT "AT THE END OF ";DY;" DAYS TRADING:" 2550 IF D>=10000 THEN GOTO 2610 2560 PRINT "YOU HAVE LOST $"; 2570 LET G=10000-D 2580 GOSUB 130 2590 PRINT R$ 2600 GOTO 2650 2610 PRINT "YOU HAVE MADE $"; 2620 LET G=D-10000 2630 GOSUB 130 2640 PRINT R$ 2650 PRINT "ON THE ZX STOCK EXCHANGE." 2660 PRINT 2670 PRINT "HOPE YOU HAD FUN, ";N$;"." 2680 PRINT "COME BACK AGAIN." 2690 PRINT AT 20,2;"PRESS S TO RETURN TO BASIC." 2700 PRINT AT 0,0;"%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$" 2710 PRINT AT 0,0;"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" 2720 PRINT AT 21,0;"%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$%$" 2730 IF INKEY$="S" THEN STOP 2740 PRINT AT 21,0;"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" 2750 GOTO 2700 2760 CLEAR 2770 STOP 2780 SAVE "1014%8" 2800 RUN ```