Do you really need tens of thousands of ratios at one time? Not that you could have that number of columns as the default limit is 2000 columns per table it can be increased to up to 32767. Limits In SQLite - Maximum Number Of Columns
You can however have tens/hundreds of thousands rows per table. As such you might wish to consider a row per stockcode per date.
Perhaps consider the following, which doesn't add a column to the table BUT instead gets a ratio (no idea if this is the exact calculation you want) derived from the stockprice for a date range for two stockcodes :-
DROP TABLE IF EXISTS closing_price; 
CREATE TABLE IF NOT EXISTS closing_price (closingdate TEXT, stockcode TEXT, stockprice REAL, UNIQUE(closingdate, stockcode));
INSERT INTO closing_price VALUES
    ('2001-01-03','AAOI',null),('2001-01-03','ABIL',null),('2001-01-03','ACIA',null),('2001-01-03','ACIW',8.94),('2001-01-03','ZG',null),('2001-01-03','ZIXI',37.19),
    ('2001-01-04','AAOI',null),('2001-01-04','ABIL',null),('2001-01-04','ACIA',null),('2001-01-04','ACIW',8.33),('2001-01-04','ZG',null),('2001-01-04','ZIXI',36.50),
    ('2001-01-05','AAOI',null),('2001-01-05','ABIL',null),('2001-01-05','ACIA',null),('2001-01-05','ACIW',8.06),('2001-01-05','ZG',null),('2001-01-05','ZIXI',37.28),
    ('2001-01-06','AAOI',null),('2001-01-06','ABIL',null),('2001-01-06','ACIA',null),('2001-01-06','ACIW',7.98),('2001-01-06','ZG',null),('2001-01-06','ZIXI',35.25),
    ('2001-01-07','AAOI',null),('2001-01-07','ABIL',null),('2001-01-07','ACIA',null),('2001-01-07','ACIW',7.81),('2001-01-07','ZG',null),('2001-01-07','ZIXI',38.00)
;
-- Ratio for a single day between ACIW and ZIXI
SELECT (
    SELECT sum(stockprice) FROM closing_price WHERE stockcode = 'ACIW' AND closingdate BETWEEN '2001-01-03' AND '2001-01-03'
    ) 
    / (
    SELECT sum(stockprice) FROM closing_price WHERE stockcode = 'ZIXI' AND closingdate BETWEEN '2001-01-03' AND '2001-01-03'
    ) 
    AS ratio
;
-- Ratio for the 5 days between ACIW and ZIXI
SELECT (
    SELECT sum(stockprice) FROM closing_price WHERE stockcode = 'ACIW' AND closingdate BETWEEN '2001-01-03' AND '2001-01-07'
    ) 
    / (
    SELECT sum(stockprice) FROM closing_price WHERE stockcode = 'ZIXI' AND closingdate BETWEEN '2001-01-03' AND '2001-01-07'
    ) 
    AS ratio
;
The above utilises a single table but a row per stockcode/closingdate combination with a UNIQUE index comprised of the combination of the stockcode/closingdate.
The table would look like :-

It then, using a query, calculates the ratio for a specific pair of stockcodes for a given date range (first query is for a single day, the second for a 4 day range).
The results are :-
- (between ACIW and ZIXI for the 1 day 2001-01-03)

- (between ACIW and ZIXI for the 5 days 2001-01-03 through to 2001-01-07)

Additional
  would it be possible to get the ratios using the method you listed
  without calling them individually (far too many combinations here)?
Ignoring nulls (at least for brevity/usefulness) the you could do something like the following (BUT beware the processing time) then perhaps the following would suit :-
WITH
allstocks AS (SELECT DISTINCT stockcode FROM closing_price),
combined AS (
SELECT DISTINCT closing_price.closingdate, closing_price.stockcode AS sc1, allstocks.stockcode AS sc2 
FROM closing_price JOIN allstocks ON closing_price.stockcode <> allstocks.stockcode
)
SELECT closingdate, sc1, sc2, 
    (SELECT stockprice FROM closing_price WHERE stockcode = sc1 AND closing_price.closingdate = combined.closingdate) /
    (SELECT stockprice FROM closing_price WHERE stockcode = sc2 AND closing_price.closingdate = combined.closingdate) AS ratio
FROM combined WHERE ratio IS NOT NULL;
This would result in :-

(that's from 150 combinations, the rest being nulls)
You could add a date range by amemding the above using something like :-
WITH
allstocks AS (SELECT DISTINCT stockcode FROM closing_price),
combined AS (
SELECT DISTINCT closing_price.closingdate, closing_price.stockcode AS sc1, allstocks.stockcode AS sc2 
FROM closing_price JOIN allstocks ON closing_price.stockcode <> allstocks.stockcode
WHERE closingdate BETWEEN '2001-01-04' AND '2001-01-06' --<<<<<<<<<< ADDED
)
SELECT closingdate, sc1, sc2, 
    (SELECT stockprice FROM closing_price WHERE stockcode = sc1 AND closing_price.closingdate = combined.closingdate) /
    (SELECT stockprice FROM closing_price WHERE stockcode = sc2 AND closing_price.closingdate = combined.closingdate) AS ratio
FROM combined 
WHERE ratio IS NOT NULL
;
which would result in :-

- The above  - 
- creates a CTE (Common Table Expression (a temporary table as such)) for each UNIQUE stockcode, the CTE is given the name allstocks. 
- creates another CTE based upon the closing_price table joined with the allstocks CTE where the stockcode doesn't match (and for the given date range for the second example). The resultane CTE is named combined 
- Each row in the combined CTE is then selected and as per the first example the ratio is derived according to the two stock codes in each row.
 
If you wanted to store the ratios in a table the you could define a table such as :-
CREATE TABLE IF NOT EXISTS ratio (closingdate TEXT, stockcode1 TEXT, stockcode2 TEXT, ratio, PRIMARY KEY(closingdate, stockcode1,stockcode2));
And use :-
WITH
allstocks AS (SELECT DISTINCT stockcode FROM closing_price),
combined AS (
SELECT DISTINCT closing_price.closingdate, closing_price.stockcode AS sc1, allstocks.stockcode AS sc2 
FROM closing_price JOIN allstocks ON closing_price.stockcode <> allstocks.stockcode
WHERE closingdate BETWEEN '2001-01-04' AND '2001-01-06'
)
INSERT OR IGNORE INTO ratio SELECT closingdate, sc1, sc2, 
    (SELECT stockprice FROM closing_price WHERE stockcode = sc1 AND closing_price.closingdate = combined.closingdate) /
    (SELECT stockprice FROM closing_price WHERE stockcode = sc2 AND closing_price.closingdate = combined.closingdate) AS ratio
FROM combined
;
- Note that the PRIMARY KEY in conjunction with INSERT OR IGNORE removes the chance for duplicates being added.