# How to Handle CSV/JSON Import/Export for Option Chains with the optionstratlib `chains` Module

> Easily import and export option chain data using CSV/JSON with the optionstratlib chains module. Handle missing data and format conversions automatically. Learn more.

- Repository: [Joaquin Bejar Garcia/optionstratlib](https://github.com/joaquinbejar/optionstratlib)
- Tags: how-to-guide
- Published: 2026-03-04

---

**The `chains` module provides synchronous and asynchronous methods on the `OptionChain` struct to serialize option market data to CSV or JSON files and reconstruct it later, handling missing fields and format conversions automatically.**

The `chains` module in the `joaquinbejar/optionstratlib` repository manages option market data through the `OptionChain` struct, which stores collections of option contracts. When you need to persist this data for backtesting, caching, or interoperability with other tools, the module offers a robust **CSV/JSON import/export** pipeline that handles serialization, deserialization, and data normalization without manual field mapping.

## Synchronous CSV Export

To persist an option chain to a CSV file, use the `save_to_csv` method implemented in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) (lines [1054-1087](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs#L1054-L1087)). This method generates a filename from the chain title (formatted as `symbol-expiration-price`) and writes a header row followed by one record per `OptionData` contract.

The CSV writer includes standard market data fields such as **strike**, **bid/ask prices**, **implied volatility (IV)**, **Greeks**, **volume**, and **open interest (OI)**. When numeric fields are missing, the helper `default_empty_string` ensures the output contains empty strings rather than malformed data.

```rust
use optionstratlib::chains::chain::OptionChain;
use optionstratlib::error::chains::ChainError;

fn persist_chain(chain: &OptionChain) -> Result<(), ChainError> {
    // Saves to "output/SP500-18-oct-2024-5781.88.csv"
    chain.save_to_csv("output")?;
    Ok(())
}

```

## Synchronous CSV Import

Loading a previously saved chain uses the `load_from_csv` method (lines [1159-1195](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs#L1159-L1195)), which returns `Result<OptionChain, ChainError>`. The implementation uses `csv::Reader` to parse each record and applies the internal `parse` helper from [`src/chains/utils.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/utils.rs) (lines [591-597](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/utils.rs#L591-L597)) to convert string fields into numeric types.

Missing or malformed numeric fields become `None` rather than causing panics. After parsing, the method calculates mid-prices via `set_mid_prices` and assembles the records into a `BTreeSet<OptionData>` within a new `OptionChain` instance.

```rust
use optionstratlib::chains::chain::OptionChain;
use optionstratlib::error::chains::ChainError;

fn restore_chain() -> Result<OptionChain, ChainError> {
    let chain = OptionChain::load_from_csv("data/SP500-18-oct-2024-5781.88.csv")?;
    // Chain is ready for analysis or strategy application
    Ok(chain)
}

```

## JSON Export and Import

For structured storage and REST API interoperability, the `OptionChain` struct derives `Serialize` and `Deserialize` from **serde**, enabling seamless JSON round-tripping.

Exporting uses `save_to_json` (lines [1121-1126](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs#L1121-L1126)), which calls `serde_json::to_writer_pretty` to generate a human-readable JSON file in the specified directory.

Importing via `load_from_json` (lines [1235-1252](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs#L1235-L1252)) uses `serde_json::from_reader` followed by post-processing: the chain recalculates mid-prices, updates Greeks, and normalizes implied volatility from percentage to decimal format via `check_and_convert_implied_volatility`.

```rust
use optionstratlib::chains::chain::OptionChain;

// Export to JSON
chain.save_to_json("cache")?;

// Later, reconstruct exactly
let restored = OptionChain::load_from_json("cache/SP500-18-oct-2024-5781.88.json")?;

```

## Asynchronous Operations

When compiling with the `async` Cargo feature, the module provides non-blocking variants that wrap the synchronous implementations in blocking tasks compatible with Tokio. These methods return `Result` types identical to their blocking counterparts.

- `save_to_csv_async(&self, dir: &str)`
- `load_from_csv_async(path: &str)`
- `save_to_json_async(&self, dir: &str)`
- `load_from_json_async(path: &str)`

```rust
use optionstratlib::chains::chain::OptionChain;
use optionstratlib::error::chains::ChainError;

#[tokio::main]
async fn main() -> Result<(), ChainError> {
    let chain = OptionChain::load_from_csv_async("data/my_chain.csv").await?;
    chain.save_to_json_async("cache").await?;
    Ok(())
}

```

## Error Handling

All import/export operations propagate errors through the `ChainError` type defined in [`src/error/chains.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/error/chains.rs). This unified error type encapsulates IO failures, CSV parsing errors, JSON deserialization issues, and format validation problems, allowing callers to handle persistence failures gracefully without matching multiple error variants.

## Summary

- The `OptionChain` struct in [`src/chains/chain.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/chain.rs) implements **CSV/JSON import/export** via eight dedicated methods (four synchronous, four asynchronous).
- CSV operations use the `csv` crate with custom parsing logic in [`src/chains/utils.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/utils.rs) to handle missing numeric fields safely.
- JSON operations rely on **serde** for automatic serialization, with post-import normalization of implied volatility and mid-prices.
- Async variants require the `async` feature flag and integrate with Tokio’s blocking task API.
- All methods return `Result` types with `ChainError` for consistent error handling across IO and parsing boundaries.

## Frequently Asked Questions

### What file naming convention does `save_to_csv` use?

The method automatically constructs filenames using the pattern `symbol-expiration-price.csv` derived from the `OptionChain` title field, placing the file in the directory specified by the `dir` parameter.

### How does the library handle missing or empty CSV fields?

During import, the generic `parse` helper in [`src/chains/utils.rs`](https://github.com/joaquinbejar/optionstratlib/blob/main/src/chains/utils.rs) converts empty or invalid strings into `None` for optional numeric fields, ensuring the `OptionData` struct initializes correctly without panicking on malformed market data.

### Can I use these methods in an async runtime like Tokio?

Yes. Enable the `async` feature in your [`Cargo.toml`](https://github.com/joaquinbejar/optionstratlib/blob/main/Cargo.toml) to access `load_from_csv_async`, `save_to_json_async`, and related methods. These spawn blocking operations that prevent the async runtime from stalling during file IO.

### What data format does the JSON export produce?

The JSON output is prettified (human-readable) and contains the complete `OptionChain` structure, including the `BTreeSet<OptionData>` collection with all strikes, Greeks, and market data fields as defined in the source struct.