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

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 (lines 1054-1087). 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.

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), 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 (lines 591-597) 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.

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), 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) 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.

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)
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. 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 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →