Build a DRAM Price-Change Calculator in Python

To combine consecutive DRAM price changes, multiply their growth factors instead of adding percentages. This Python calculator reads a CSV, starts a hypothetical index at 100, and creates a local HTML chart. The sample is invented; its output is not observed DRAM pricing, Samsung’s realized ASP, or a profit forecast.

Quick diagnosis: if +20% followed by −20% gives you “back to zero,” check the denominator. The second change applies to the new level. Run the supplied CSV first and compare the output before using any permitted real dataset. No API key, package install, or paid service is needed.

Executed October 18, 2026 on Windows 11 with Python 3.12.14 and 3.14.3. Standard library only. Other operating systems were not executed. The script reads your chosen CSV and exclusively creates a new chart file; it refuses to overwrite an existing output.

Key terms, defined first

  • QoQ percentage change: change from the immediately preceding quarter, not from a fixed base year.
  • Growth factor: 1 + percentage ÷ 100. For example, +20% becomes 1.20.
  • Index: a relative level rebased to 100 here—not a currency price per chip or per gigabit.
  • Cumulative change: the final level divided by the initial level, minus one.
  • CSV: comma-separated tabular text. Our two columns are period and qoq_pct.
  • Decimal: Python’s decimal arithmetic type. The code constructs values from text; it does not make market assumptions correct.
  • ASP: a company’s average realized selling price across a defined basket of actual sales.

1. Understand the compounding rule

Definition: each quarter’s rate changes the preceding level. The calculation is next index = previous index × (1 + QoQ rate ÷ 100). Cumulative change is (final index ÷ starting index − 1) × 100. Rates are in percent; index levels have no currency unit.

Example: start at 100. A 20% rise gives 120. A 20% fall then gives 96, because the fall is 20% of 120. A further 10% rise gives 105.60. The combined change is +5.60%; adding 20 − 20 + 10 would incorrectly give +10%.

Use: chain adjacent changes for one consistently defined series. Do not chain year-over-year rates as if they were sequential quarter-over-quarter rates: overlapping comparison windows would answer a different question.

2. Create the hypothetical CSV

Save the following as sample.csv in a small working folder. These numbers are deliberately invented. Enter 20 for +20%, not 0.20 and not the text “20%.” BASE in the output means the level immediately before the first listed quarter.

sample.csv · hypothetical data

period,qoq_pct
2026Q1,20
2026Q2,-20
2026Q3,10

The code requires exactly these two headers, chronological consecutive quarters and one rate per row. It does not sort rows, fill missing quarters, or quietly treat a blank rate as zero. Before adapting it, keep the source, date, product basket, contract-versus-spot basis and permitted usage of your data in a separate note.

3. Copy the calculator and chart code

Save this complete script as price_change.py. The central calculation is the line beginning level =; the other sections validate inputs, build the chart and handle files. D is simply an alias for Decimal.

Python’s CSV documentation describes DictReader and opening files with newline="". The Decimal documentation explains decimal representation and precision. We keep intermediate values unrounded explicitly and format the display to two decimal places; normal Decimal context precision still applies, so this is not arbitrary-precision financial software.

price_change.py · complete tested source

import csv
import re
import sys
from decimal import Decimal, InvalidOperation
from html import escape

D = Decimal


def calculate(stream):
    reader = csv.DictReader(stream, strict=True)
    if reader.fieldnames != ["period", "qoq_pct"]:
        raise ValueError("header must be period,qoq_pct")
    points = [("BASE", D("0"), D("100"))]
    previous = None
    for row in reader:
        period = (row.get("period") or "").strip()
        if None in row or not re.fullmatch(r"[1-9][0-9]{3}Q[1-4]", period):
            raise ValueError("use exactly two fields and period YYYYQ1..YYYYQ4")
        ordinal = int(period[:4]) * 4 + int(period[-1]) - 1
        if previous is not None and ordinal != previous + 1:
            raise ValueError("quarters must be consecutive, ordered and unique")
        try:
            rate = D((row.get("qoq_pct") or "").strip())
        except InvalidOperation as exc:
            raise ValueError("qoq_pct must be a decimal percentage") from exc
        if not rate.is_finite() or not D("-100") < rate <= D("1000"):
            raise ValueError("qoq_pct must be finite, above -100 and at most 1000")
        if len(points) > 40:
            raise ValueError("this teaching example supports at most 40 quarters")
        level = points[-1][2] * (D("1") + rate / D("100"))
        points.append((period, rate, level))
        previous = ordinal
    if len(points) == 1:
        raise ValueError("CSV must contain at least one data row")
    return points


def chart_html(points):
    peak = max(level for _, _, level in points)
    bars = []
    for period, _, level in points:
        width = level / peak * D("100")
        bars.append(
            f'<p style="margin:18px 0 6px;">{escape(period)}: '
            f'<strong>{level:.2f}</strong></p>'
            f'<div style="height:24px;background:#303238;">'
            f'<div style="width:{width:.2f}%;height:24px;'
            f'background:#8ab4f8;"></div></div>'
        )
    figure = (
        '<figure id="index-chart" aria-label="Hypothetical chained price index" '
        'style="margin:0;padding:16px;background:#27282c;'
        'border:1px solid #414247;border-radius:12px;">'
        '<strong>Hypothetical index - base 100</strong>'
        + "".join(bars)
        + '<figcaption style="font-size:16px;color:#c4c7cf;margin-top:18px;">'
        'Invented data, not observed DRAM prices. Bars start at zero; '
        'the largest displayed index fills the width.</figcaption></figure>'
    )
    return (
        '<!doctype html><html lang="en"><meta charset="utf-8">'
        '<meta name="viewport" content="width=device-width,initial-scale=1">'
        '<title>Hypothetical price index</title>'
        '<body style="margin:0;padding:18px;max-width:760px;'
        'background:#1e1f21;color:#e6e6e9;'
        'font:20px/1.9 system-ui,sans-serif;overflow-wrap:anywhere;">'
        + figure + '</body></html>'
    )


def main():
    if len(sys.argv) != 3:
        raise ValueError("usage: python price_change.py sample.csv chart.html")
    with open(sys.argv[1], encoding="utf-8-sig", newline="") as stream:
        points = calculate(stream)
    # Exclusive creation: never overwrite an existing output file.
    with open(sys.argv[2], "x", encoding="utf-8") as output:
        output.write(chart_html(points))
    print("HYPOTHETICAL DATA ONLY - not observed DRAM prices")
    print("period,qoq_pct,index,cumulative_pct")
    for period, rate, level in points:
        print(f"{period},{rate:.2f},{level:.2f},{level - D('100'):.2f}")
    print(f"sum_of_rates_only={sum(p[1] for p in points):.2f}%")
    print(f"chart_written={sys.argv[2]}")


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError, InvalidOperation, csv.Error) as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        sys.exit(2)

The chart uses only local HTML and inline presentation, with text labels and zero-based bars. Its largest index fills the available width; it does not pretend that 100 is a monetary quote. The rate bounds and 40-quarter cap are teaching-program guardrails, not claims about the possible behavior of a market.

4. Run it and compare the actual output

From that folder, first check python --version. Then run the command below. If your installation uses python3 instead, substitute that command. Avoid running unreviewed downloads or changing security settings to make this example work.

Terminal command

python price_change.py sample.csv chart.html

Observed execution output: the table below was produced locally from the supplied CSV. Both tested Python versions produced the same index values and identical chart HTML; only the deliberately different second output filename changed.

Actual local output · hypothetical input

HYPOTHETICAL DATA ONLY - not observed DRAM prices
period,qoq_pct,index,cumulative_pct
BASE,0.00,100.00,0.00
2026Q1,20.00,120.00,20.00
2026Q2,-20.00,96.00,-4.00
2026Q3,10.00,105.60,5.60
sum_of_rates_only=10.00%
chart_written=chart.html

Open the newly created chart.html in your browser. The following is the same generated figure embedded in this article, not a screenshot of real market data:

Hypothetical index - base 100

BASE: 100.00

2026Q1: 120.00

2026Q2: 96.00

2026Q3: 105.60

Invented data, not observed DRAM prices. Bars start at zero; the largest displayed index fills the width.

Read the columns: qoq_pct is that row’s input rate, index is the chained level, and cumulative_pct is the change from BASE. Because BASE is exactly 100, the last column is numerically index minus 100 in this example. With another starting level, use the general ratio formula instead.

5. Recognize bad inputs and file errors

SymptomSafe next step
File existsChoose a new output name, such as chart2.html. The old file was not overwritten.
Quarter-order errorCheck for duplicates, gaps or reversed rows. Do not invent a missing rate.
Rate errorUse a finite decimal number above −100 and at most 1000, without a percent sign.
Header or CSV errorKeep period,qoq_pct exactly, with two fields per row and valid quoting.
File not foundCheck your working folder and the input filename. No permission escalation is required.

Verification: five test methods passed on both Python versions, including 12 invalid-input subcases, year rollover, the 40-quarter limit and malformed quoting. A second attempt to create the existing chart file was also checked: Python returned error exit code 2 and left the existing output intact. A shell wrapper may expose a different outer status, so use the printed error message as well.

The strict rate lower bound excludes a zero-price collapse, where a subsequent ordinary percentage change would need different treatment. The program supports up to 40 quarters, reads UTF-8 with an optional BOM, and ignores entirely blank CSV lines through the CSV reader. It is not a production data-cleaning or index-construction service.

6. Know what the index cannot tell you

Definition: rebasing a series changes its scale, not its meaning. A correctly compounded index still needs a consistent product definition and source methodology. It does not reveal an absolute DRAM price without a matching starting quote and unit.

Do not splice different products or mix spot quotes with contract prices just because each number is called “DRAM.” Nor can a basket’s percentage change be substituted for Samsung’s realized ASP or operating-profit growth. Actual sales mix, volume and costs are separate inputs. Our related English analysis explains the boundary between memory prices and Samsung’s profit estimate; the calculator does not estimate that relationship.

Takeaway: validate the period and units, multiply growth factors, keep calculations separate from display rounding, and label every illustrative result. Your next exercise is to change one invented rate, choose a new output filename, and explain the result before considering any licensed real data.

Appendix. Reproduction and sources

  • Test environment: Windows 11, CPython 3.12.14 and 3.14.3; no third-party packages, network calls or paid APIs in the example.
  • Official Python 3.12 CSV reference — parsing, headers, file newline handling.
  • Official Python 3.12 Decimal reference — decimal values, arithmetic and finite precision.
  • Checked October 18, 2026. Local tests are not a DRAM-market observation, hardware benchmark, investment model or traffic-performance claim.

댓글

이 블로그의 인기 게시물

소니 A6000 Wi-Fi 전송 (컴퓨터로 보내기)

C#, cmd 명령어 창 안보이게 실행하기

SVN 서버 설치 및 웹 접속 설정-Ubuntu 14.04