Contributing#
We welcome contributions to FinanceQuery! This guide will help you get started.
warning · V1 (Python) is Not Maintained
The legacy Python implementation in /v1 is no longer actively maintained. All development focuses on the Rust library and server. The v1 source code, workflow, and documentation remain available for reference.
Quick Start#
Clone and set up the development environment:
git clone https://github.com/Verdenroz/finance-query.git
cd finance-query
make install-dev # Installs rustfmt, clippy, prek, sets up pre-commit hooks
This sets up prek (a faster Rust-based pre-commit) which runs fmt, clippy, and check automatically before each commit.
Useful Commands#
Run make help to see all available commands:
make serve # Start dev server (PORT=8000 by default)
make test # Run ALL tests including network integration tests
make test-fast # Run only fast tests (excludes network tests)
make fix # Auto-fix formatting and clippy issues
prek # Run pre-commit checks (fmt, clippy, check)
make docs-pages # Regenerate the derived docs pages
make docs # Serve the docs site at localhost:8080
make build # Build library and server in release mode
docker compose up -d # Start the full stack (v1, v2, Redis, Caddy, monitoring)
Development Workflow#
1. Make Your Changes#
Work on the library (src/) or server (server/src/):
# Start the dev server
make serve
# Run tests as you work
make test-fast # Quick tests only
make test # All tests including network calls
2. Check Your Code#
Before committing, run the pre-commit checks:
make fix # Auto-fix formatting and clippy issues
prek # Verify all checks pass
3. Test Thoroughly#
Run the appropriate tests for your changes:
# Library changes
cargo test -p finance-query
# Server changes
cargo test -p finance-query-server
# Specific test
cargo test test_ticker_quote
# Integration tests (makes real API calls)
cargo test -- --ignored
Code Standards#
Write Idiomatic Rust#
Use standard patterns and avoid unnecessary complexity:
// Good - simple and clear
pub async fn quote(&self) -> Result<Quote> {
self.get_quote_data().await
}
// Bad - over-engineered
pub async fn quote(&self) -> Result<Quote, Box<dyn std::error::Error>> {
match self.get_quote_data().await {
Ok(data) => Ok(data),
Err(e) => Err(Box::new(e)),
}
}
Document Public APIs#
Add doc comments to public items:
/// Fetches the latest quote for the ticker.
///
/// # Example
///
/// ```no_run
/// use finance_query::{Raw, Ticker};
///
/// let ticker = Ticker::builder("AAPL").logo().build().await?;
/// let quote = ticker.quote::<Raw>().await?;
/// println!("Price: ${:.2}", quote.regular_market_price.unwrap_or(0.0));
/// ```
pub async fn quote(&self) -> Result<Quote> {
// ...
}
Testing Guidelines#
Unit Tests#
Keep tests focused and fast:
#[tokio::test]
async fn test_ticker_builder() {
let ticker = Ticker::builder("AAPL")
.region(Region::UnitedStates)
.build()
.await
.unwrap();
assert_eq!(ticker.symbol(), "AAPL");
}
Integration Tests#
Mark network tests with #[ignore]:
use finance_query::format::Raw;
#[tokio::test]
#[ignore = "requires network access"]
async fn test_real_quote() {
let ticker = Ticker::builder("AAPL").logo().build().await.unwrap();
let quote = ticker.quote::<Raw>().await.unwrap();
assert!(!quote.symbol.is_empty());
}
Doc Tests#
Use no_run for examples that require network access:
/// # Example
///
/// ```no_run
/// # use finance_query::{Raw, Ticker};
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let ticker = Ticker::builder("AAPL").logo().build().await?;
/// let quote = ticker.quote::<Raw>().await?;
/// # Ok(())
/// # }
/// ```
Submitting Changes#
1. Create a Branch#
Use descriptive branch names:
git checkout -b fix/quote-timezone-handling
git checkout -b feat/add-options-chain
2. Commit Your Changes#
Write clear commit messages:
git add .
git commit -m "fix: handle timezone correctly in market hours"
3. Push and Create PR#
git push origin fix/quote-timezone-handling
Open a pull request on GitHub with:
- Clear description of what changed and why
- Link to any related issues
- Test results showing everything passes
Common Tasks#
Adding a New Endpoint#
Library side:
- Add endpoint URL in
src/endpoints/:
// src/endpoints/quote.rs
pub fn options_chain(symbol: &str) -> String {
format!("{}/v7/finance/options/{}", BASE_URL, symbol)
}
- Define model in
src/models/:
// src/models/options.rs
#[derive(Debug, Clone, Deserialize)]
pub struct OptionsChain {
pub symbol: String,
pub expiration_dates: Vec<i64>,
// ...
}
- Add method to
Ticker:
// src/ticker/core.rs
pub async fn options(&self) -> Result<OptionsChain> {
let url = endpoints::options_chain(&self.symbol);
self.client.fetch_json(&url).await
}
Server side:
// server/src/main.rs
async fn get_options(
Path(symbol): Path<String>,
) -> Result<Json<OptionsChain>, AppError> {
let ticker = Ticker::new(&symbol).await?;
let options = ticker.options().await?;
Ok(Json(options))
}
// Register route
.route("/v2/options/{symbol}", get(get_options))
Updating Dependencies#
Check for outdated dependencies:
cargo outdated
cargo update
make test # Ensure everything still works
Getting Help#
- Questions: Open a GitHub Discussion
- Bugs: Open a GitHub Issue with reproduction steps
- Security: Email security@finance-query.dev (not public issues)
License#
By contributing, you agree that your contributions will be licensed under the MIT License.
