# CSV Purchase Import Guide

MyDeepStack imports purchase data from comma-separated (`.csv`) or tab-separated (`.tsv` or plain-text) files. Each data row represents one purchase lot.

Import is additive: it adds purchases to the stack, merges matching holdings, skips invalid rows, and never deletes existing data.

The importer is only for purchases. Do not select a sales export: it cannot restore the sales/removals ledger, and its shared inventory columns may instead be interpreted as new purchase rows.

## Recommended Simple Format

For a reliable hand-made import, use these columns:

```csv
Metal,Item,Quantity,Fine Oz Each,Purchase Date,Total Cost USD,Dealer Or Source,Notes
Silver,American Silver Eagle,20,1,2026-07-01,760,Local Coin Shop,"Sealed tube"
Gold,Custom Gold Bar,2,1,2026-07-02,6800,Online Dealer,
Platinum,Custom Platinum Round,1,0.5,,,,"Cost not tracked"
```

The safest minimum is:

```csv
Metal,Item,Quantity,Fine Oz Each
Silver,Custom Silver Bar,4,10
```

## Required Data

The header row must contain:

- `Metal`
- `Quantity`

Each row must have:

- A recognized metal: `Gold`, `Silver`, or `Platinum`. The codes `XAU`, `XAG`, and `XPT` are also accepted.
- A numeric `Quantity` greater than zero.
- A numeric `Fine Oz Each` greater than zero, unless the item is matched to a catalog item with a fixed weight.

`Item` is strongly recommended. If it is blank, the app uses `Imported item`. A custom or unmatched item needs `Fine Oz Each`; the text in `Weight` is only a display label and is not converted into fine ounces.

## Supported Columns

| Preferred header | Accepted aliases | Purpose |
| --- | --- | --- |
| `Metal` | `Metal` | Gold, Silver, or Platinum |
| `Category` | `Category Name` | Helps match an item to the built-in catalog |
| `Item` | `Item Name`, `Name` | Catalog item name or custom item name |
| `Weight` | `Weight Label` | Optional display label, such as `10 oz`; does not replace `Fine Oz Each` |
| `Fine Oz Each` | `Fine Weight Oz Each`, `Fine Oz`, `Fine Weight Oz`, `Fine Ounces` | Fine troy ounces in one item |
| `Quantity` | `Qty`, `Count` | Number of items in the purchase lot |
| `Purchase Date` | `Date` | Purchase date |
| `Cost Method` | `Method` | How cost basis should be calculated |
| `Unit Cost USD` | `Unit Cost`, `Cost Per Unit` | Cost of one item |
| `Total Cost USD` | `Total Cost` | Total cost for the entire row |
| `Premium USD Per Oz` | `Premium Per Oz`, `Dollar Premium`, `Premium USD` | Dollar premium per fine ounce |
| `Premium Percent` | `Premium Pct` | Percentage premium over spot |
| `Purchase Spot USD Per Oz` | `Purchase Spot`, `Spot At Purchase`, `Spot` | USD spot price used for a premium-based cost |
| `Cost Basis USD` | `Cost Basis` | Explicit total cost-basis fallback |
| `Dealer Or Source` | `Dealer`, `Source` | Seller or purchase source |
| `Notes` | `Note` | Free-form notes |
| `Category ID` | `Category ID` | Exact built-in category identifier from an app export |
| `Item ID` | `Item ID` | Exact built-in item identifier from an app export |

Header matching ignores capitalization, spaces, and punctuation. For example, `fine_oz_each` normalizes to the same header as `Fine Oz Each`. Prefer the documented headers for clarity and export compatibility.

## Dates

Accepted date formats are:

- `YYYY-MM-DD`, such as `2026-07-01`
- `M/D/YYYY`, such as `7/1/2026`
- `M/D/YY`, such as `7/1/26`

If `Purchase Date` is blank or cannot be parsed, the app assigns the date on which the import is performed.

## Cost-Basis Options

All costs and spot prices are in U.S. dollars.

### Per-item cost

```csv
Metal,Item,Quantity,Fine Oz Each,Cost Method,Unit Cost USD
Silver,American Silver Eagle,20,1,Per Unit,38
```

Cost basis is `Quantity × Unit Cost USD`.

If `Unit Cost USD` is present and `Cost Method` is blank or unrecognized, the importer automatically uses the per-unit method.

### Total lot cost

```csv
Metal,Item,Quantity,Fine Oz Each,Cost Method,Total Cost USD
Silver,10 oz Silver Bar,4,10,Total,1260
```

`Total Cost USD` applies to the entire row. If `Total Cost USD` or `Cost Basis USD` is present and `Cost Method` is blank or unrecognized, the importer automatically uses the total-cost method.

### Dollar premium over spot

```csv
Metal,Item,Quantity,Fine Oz Each,Cost Method,Purchase Spot USD Per Oz,Premium USD Per Oz
Silver,American Silver Eagle,20,1,Dollar Over Spot,31.25,5.50
```

Cost basis is:

```text
Quantity × Fine Oz Each × (Purchase Spot USD Per Oz + Premium USD Per Oz)
```

### Percentage premium over spot

```csv
Metal,Item,Quantity,Fine Oz Each,Cost Method,Purchase Spot USD Per Oz,Premium Percent
Gold,Custom Gold Bar,2,1,Percent Over Spot,3350,2.5
```

Cost basis is:

```text
Quantity × Fine Oz Each × Purchase Spot USD Per Oz × (1 + Premium Percent ÷ 100)
```

For either premium-based method, include the matching `Cost Method`; premium columns alone do not cause the importer to infer that method.

Rows without usable cost data are still imported, but their cost basis is left untracked. A cost basis may be zero, but a negative or non-finite calculated cost is discarded.

## Item Matching and Merging

The importer identifies an item in this order:

1. Valid `Category ID` and `Item ID` values, normally copied from a MyDeepStack export.
2. An exact, case-insensitive catalog item-name match, optionally narrowed by `Category`.
3. A custom item under that metal's `Other (custom)` category.

For fixed-weight catalog items, the catalog's fine weight and weight label override values in the CSV.

Rows that identify the same metal, category, item, item name, weight label, and fine weight are grouped into one holding with multiple purchase lots. Matching existing holdings are merged as well. Re-importing the same file therefore adds the lots again; it does not deduplicate past imports.

## Full Export-Compatible Header

The app's stack export uses the following complete header:

```csv
Metal,Category,Item,Weight,Fine Oz Each,Quantity,Purchase Date,Cost Method,Unit Cost USD,Total Cost USD,Premium USD Per Oz,Premium Percent,Purchase Spot USD Per Oz,Cost Basis USD,Spot At Purchase USD Per Oz,Current Value USD,Dealer Or Source,Notes,Category ID,Item ID
```

`Current Value USD` is export-only and is ignored during import. In a full exported file, `Purchase Spot USD Per Oz` supplies the imported purchase spot; the later `Spot At Purchase USD Per Oz` column is derived export information and does not override it.

## File Formatting

- Use UTF-8 text. A UTF-8 byte-order mark is accepted but not required.
- Use commas or tabs consistently. The importer detects the delimiter from the header row.
- Use a period as the decimal separator.
- Currency symbols, percent signs, and numeric grouping commas are removed when parsing numbers. In a comma-separated file, a value containing a comma must still be quoted.
- Wrap a field in double quotes if it contains a comma, tab, quotation mark, or line break.
- Represent a quotation mark inside a quoted field by doubling it.

Example:

```csv
Metal,Item,Quantity,Fine Oz Each,Dealer Or Source,Notes
Silver,Custom Round,5,1,"Local Shop, Inc.","Special edition, marked ""First Strike"""
```

## Importing in the App

1. Open **Settings**.
2. Open **Export & Import**.
3. Choose **Import Purchases from CSV**.
4. Select the CSV, TSV, or text file.
5. Review the result message for imported purchases and skipped rows.

Invalid rows do not prevent valid rows in the same file from importing. The result reports the skipped-row count and details for up to the first three skipped rows.

## Implementation Reference

This guide is verified against:

- `MyDeepStackApp/Services/PortfolioCSV.swift`
- `MyDeepStackApp/App/PortfolioStore.swift`
- `MyDeepStackApp/Views/SetupTabView.swift`
- `MyDeepStackTests/PortfolioCSVTests.swift`
