1 stable release
Uses new Rust 2024
| 1.1.0 | Jul 31, 2025 |
|---|
#1493 in Procedural macros
Used in tushare-api
10KB
137 lines
Tushare API - Rust Library
A comprehensive Rust client library for accessing Tushare financial data APIs. This library provides type-safe, async access to all Tushare data interfaces.
✨ Features
- 🚀 Async/Await Support: Built for high-performance async operations
- 🔒 Type Safety: Strongly typed API enums and comprehensive error handling
- 🔧 Developer Friendly: Convenient macros and builder patterns
- 📊 Third-Party Type Support: Built-in support for
rust_decimal,chrono,uuid, andbigdecimal - 🌍 Production Ready: Comprehensive error handling and security features
📋 Requirements
- Tushare API Token: Register at Tushare Pro to get your API token
📦 Installation
Add this to your Cargo.toml:
[dependencies]
tushare-api = "1.1.0"
# Optional: Enable third-party type support
# tushare-api = { version = "1.1.0", features = ["rust_decimal", "chrono"] }
# Or enable all third-party types
# tushare-api = { version = "1.1.0", features = ["all_types"] }
# Optional: Enable tracing support
# tushare-api = { version = "1.1.0", features = ["tracing"] }
🚀 Quick Start
use tushare_api::{TushareClient, Api, request};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Set your token as environment variable
std::env::set_var("TUSHARE_TOKEN", "your_token_here");
// Create client from environment variable
let client = TushareClient::from_env()?;
// Make API call using convenient macro
let response = client.call_api(request!(Api::StockBasic, {
"list_status" => "L",
"exchange" => "SSE"
}, [
"ts_code", "name", "industry"
])).await?;
println!("Retrieved {} records", response.data.items.len());
Ok(())
}
📖 API Usage Guide
1. How to Import Tushare API
// Basic imports
use tushare_api::{TushareClient, TushareRequest, TushareResponse, Api, TushareResult};
// For convenient macros
use tushare_api::{params, fields, request};
// For logging configuration
use tushare_api::{LogLevel, LogConfig, Logger};
// For HTTP client configuration and connection pool settings
use tushare_api::{TushareClientBuilder, HttpClientConfig};
// For custom timeouts
use std::time::Duration;
2. How to Create Tushare Client
The library provides multiple ways to create a client:
Method 1: Using Environment Variable (Recommended)
// Set environment variable first
std::env::set_var("TUSHARE_TOKEN", "your_token_here");
// Create client with default settings
let client = TushareClient::from_env()?;
// Create client with custom timeouts
let client = TushareClient::from_env_with_timeout(
Duration::from_secs(10), // connect timeout
Duration::from_secs(60) // request timeout
)?;
Method 2: Direct Token
// Create client with default settings
let client = TushareClient::new("your_token_here");
// Create client with custom timeouts
let client = TushareClient::with_timeout(
"your_token_here",
Duration::from_secs(10), // connect timeout
Duration::from_secs(60) // request timeout
);
Method 3: Using Builder Pattern
// Basic builder with timeouts and logging
let client = TushareClient::builder()
.with_token("your_token_here")
.with_connect_timeout(Duration::from_secs(10))
.with_timeout(Duration::from_secs(60))
.with_log_level(LogLevel::Debug)
.log_requests(true)
.log_responses(false)
.log_sensitive_data(false)
.log_performance(true)
.build()?;
// Advanced builder with connection pool settings
let client = TushareClient::builder()
.with_token("your_token_here")
.with_connect_timeout(Duration::from_secs(5))
.with_timeout(Duration::from_secs(60))
.with_pool_max_idle_per_host(20) // Max 20 idle connections per host
.with_pool_idle_timeout(Duration::from_secs(60)) // Keep connections for 60s
.with_log_level(LogLevel::Info)
.build()?;
// Using HttpClientConfig for advanced HTTP settings
let http_config = HttpClientConfig::new()
.with_connect_timeout(Duration::from_secs(3))
.with_timeout(Duration::from_secs(30))
.with_pool_max_idle_per_host(15)
.with_pool_idle_timeout(Duration::from_secs(45));
let client = TushareClient::builder()
.with_token("your_token_here")
.with_http_config(http_config)
.with_log_level(LogLevel::Debug)
.build()?;
3. How to Send Requests
Method 1: Using Convenient Macros (Recommended)
use tushare_api::{request, Api};
// Single API call with parameters and fields
let response = client.call_api(request!(Api::StockBasic, {
"list_status" => "L",
"exchange" => "SSE"
}, [
"ts_code", "name", "industry", "area"
])).await?;
// API call without parameters
let response = client.call_api(request!(Api::TradeCal, {}, [
"exchange", "cal_date", "is_open"
])).await?;
// API call without fields (get all fields)
let response = client.call_api(request!(Api::StockBasic, {
"list_status" => "L"
}, [])).await?;
Method 2: Using Individual Macros
use tushare_api::{params, fields, TushareRequest, Api};
// Create parameters
let params = params!(
"list_status" => "L",
"exchange" => "SSE"
);
// Create fields
let fields = fields![
"ts_code", "name", "industry"
];
// Create request
let request = TushareRequest {
api_name: Api::StockBasic,
params,
fields,
};
// Send request
let response = client.call_api(request).await?;
Method 3: Manual Construction
use std::collections::HashMap;
let mut params = HashMap::new();
params.insert("list_status".to_string(), "L".to_string());
params.insert("exchange".to_string(), "SSE".to_string());
let fields = vec![
"ts_code".to_string(),
"name".to_string(),
"industry".to_string(),
];
let request = TushareRequest {
api_name: Api::StockBasic,
params,
fields,
};
let response = client.call_api(request).await?;
4. Automatic Struct Conversion with Procedural Macros
The library provides powerful procedural macros to automatically convert Tushare API responses into strongly-typed Rust structs, eliminating the need for manual parsing.
Using Procedural Macros
use tushare_api::{TushareClient, Api, request, TushareEntityList};
use tushare_api::DeriveFromTushareData;
// Define your struct with automatic conversion
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct Stock {
pub ts_code: String,
pub symbol: String,
pub name: String,
pub area: Option<String>,
pub industry: Option<String>,
pub market: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
// Using call_api_as for direct conversion to TushareEntityList<Stock>
let stocks: TushareEntityList<Stock> = client.call_api_as(request!(Api::StockBasic, {
"list_status" => "L",
"exchange" => "SSE"
}, [
"ts_code", "symbol", "name", "area", "industry", "market"
])).await?;
// Access the data directly
println!("Found {} stocks:", stocks.len());
for stock in stocks.iter().take(5) {
println!(" {}: {} ({})", stock.ts_code, stock.name, stock.market);
}
// Access pagination information
println!("Current page: {} items", stocks.len());
println!("Total records: {}", stocks.count());
println!("Has more pages: {}", stocks.has_more());
Ok(())
}
Field Mapping and Optional Fields
use tushare_api::{TushareClient, Api, request, TushareEntityList};
use tushare_api::DeriveFromTushareData;
// Advanced struct with field mapping and optional fields
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct StockInfo {
pub ts_code: String,
// Map API field "symbol" to struct field "stock_symbol"
#[tushare(field = "symbol")]
pub stock_symbol: String,
pub name: String,
// Optional fields are automatically handled
pub area: Option<String>,
pub industry: Option<String>,
// Skip fields that don't exist in API response
#[tushare(skip)]
pub calculated_value: f64,
}
// Implement Default for convenience
impl Default for StockInfo {
fn default() -> Self {
Self {
ts_code: String::new(),
stock_symbol: String::new(),
name: String::new(),
area: None,
industry: None,
calculated_value: 0.0,
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
let stock_info: TushareEntityList<StockInfo> = client.call_api_as(request!(Api::StockBasic, {
"list_status" => "L"
}, [
"ts_code", "symbol", "name", "area", "industry"
])).await?;
for info in stock_info.iter().take(3) {
println!("Stock: {} ({}) - Industry: {:?}",
info.name, info.stock_symbol, info.industry);
}
Ok(())
}
Generated Struct Structure
When you use the new generic pagination container, you get a clean, type-safe interface:
// Your original struct
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct Stock {
pub ts_code: String,
pub name: String,
pub area: Option<String>,
}
// Use the generic TushareEntityList<T> container:
// TushareEntityList<Stock> {
// pub items: Vec<Stock>, // Your data items
// pub has_more: bool, // Pagination: more pages available?
// pub count: i64, // Pagination: total record count
// }
When you call:
let stocks: TushareEntityList<Stock> = client.call_api_as(request).await?;
// OR
let stocks = client.call_api_as::<Stock>(request).await?;
You get a TushareEntityList<Stock> struct with:
items-Vec<Stock>containing the actual converted datahas_more-boolindicating if there are more pages to fetchcount-i64showing the total number of records available
Plus these automatically generated methods:
stocks.len()- Number of items in current pagestocks.is_empty()- Whether current page is emptystocks.items()- Get items as slicestocks.has_more()- Check if more pages availablestocks.count()- Get total record countstocks.iter()- Iterate over items (via Deref)for stock in &stocks { ... }- Direct iteration support
Pagination Support
The generic TushareEntityList<T> container provides built-in pagination support with a clean, intuitive interface:
items: Vec<T>- The actual data itemshas_more: bool- Whether more pages are availablecount: i64- Total number of records
use tushare_api::{TushareClient, Api, request, DeriveFromTushareData};
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct Stock {
pub ts_code: String,
pub name: String,
pub area: Option<String>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
// Get paginated results
let stocks: TushareEntityList<Stock> = client.call_api_as(request!(Api::StockBasic, {
"list_status" => "L",
"limit" => "100",
"offset" => "0"
}, [
"ts_code", "name", "area"
])).await?;
// Access pagination information
println!("Current page: {} stocks", stocks.len());
println!("Total available: {} stocks", stocks.count());
println!("Has more pages: {}", stocks.has_more());
// Iterate through current page items
for stock in &stocks {
println!("{}: {} ({})",
stock.ts_code,
stock.name,
stock.area.as_deref().unwrap_or("Unknown"));
}
// Access items directly
let first_stock = &stocks.items()[0];
println!("First stock: {}", first_stock.name);
Ok(())
}
Supported Field Types
The procedural macros support the following Rust types:
Basic Types:
String- Required string fieldOption<String>- Optional string fieldf64,f32- Required floating-point numbersOption<f64>,Option<f32>- Optional floating-point numbersi64,i32,i16,i8,isize- Required signed integersOption<i64>,Option<i32>, etc. - Optional signed integersu64,u32,u16,u8,usize- Required unsigned integersOption<u64>,Option<u32>, etc. - Optional unsigned integersbool- Required booleanOption<bool>- Optional booleanchar- Required characterOption<char>- Optional character
Third-Party Types (with feature flags):
rust_decimal::Decimal- High-precision decimal (feature:rust_decimal)bigdecimal::BigDecimal- Arbitrary precision decimal (feature:bigdecimal)chrono::NaiveDate- Date without timezone (feature:chrono)chrono::NaiveDateTime- DateTime without timezone (feature:chrono)chrono::DateTime<Utc>- UTC DateTime (feature:chrono)uuid::Uuid- UUID type (feature:uuid)- All above types with
Option<T>for optional fields
5. Third-Party Type Support
The library provides built-in support for popular third-party types through optional feature flags. This is especially useful for financial applications that require high-precision arithmetic or date/time handling.
Enabling Third-Party Types
Add the desired features to your Cargo.toml:
[dependencies]
# Enable specific types
tushare-api = { version = "1.1.0", features = ["rust_decimal", "chrono"] }
# Or enable all third-party types
tushare-api = { version = "1.1.0", features = ["all_types"] }
Example with High-Precision Decimals
use tushare_api::{TushareClient, Api, request, TushareEntityList, DeriveFromTushareData};
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct FinancialData {
#[tushare(field = "ts_code")]
pub stock_code: String,
#[tushare(field = "trade_date")]
pub date: String,
// High-precision decimal for financial calculations
#[tushare(field = "close")]
pub close_price: rust_decimal::Decimal,
#[tushare(field = "vol")]
pub volume: Option<rust_decimal::Decimal>,
#[tushare(field = "amount")]
pub amount: Option<rust_decimal::Decimal>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
let data: TushareEntityList<FinancialData> = client.call_api_as(request!(
Api::Daily, {
"ts_code" => "000001.SZ",
"trade_date" => "20240315"
}, [
"ts_code", "trade_date", "close", "vol", "amount"
]
)).await?;
for record in data.iter() {
println!("Stock: {} - Price: {} on {}",
record.stock_code,
record.close_price,
record.date);
}
Ok(())
}
Example with Date/Time Types
use tushare_api::{TushareClient, Api, request, TushareEntityList, DeriveFromTushareData};
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct DateTimeData {
#[tushare(field = "ts_code")]
pub stock_code: String,
// Automatic parsing from YYYYMMDD format
#[tushare(field = "trade_date")]
pub trade_date: chrono::NaiveDate,
// Optional datetime field
#[tushare(field = "update_time")]
pub update_time: Option<chrono::NaiveDateTime>,
// High-precision price
#[tushare(field = "close")]
pub close_price: rust_decimal::Decimal,
}
Custom Date Format Support
The library supports custom date format parsing using the #[tushare(date_format = "...")] attribute. This is especially useful when dealing with APIs that return dates in non-standard formats.
use tushare_api::{TushareClient, Api, request, TushareEntityList, DeriveFromTushareData};
#[derive(Debug, Clone, DeriveFromTushareData)]
pub struct CustomDateFormats {
#[tushare(field = "ts_code")]
pub stock_code: String,
// Standard date format (auto-detected: YYYYMMDD, YYYY-MM-DD, etc.)
#[tushare(field = "trade_date")]
pub trade_date: chrono::NaiveDate,
// European date format: DD/MM/YYYY
#[tushare(field = "european_date", date_format = "%d/%m/%Y")]
pub european_date: chrono::NaiveDate,
// US date format: MM-DD-YYYY
#[tushare(field = "us_date", date_format = "%m-%d-%Y")]
pub us_date: chrono::NaiveDate,
// German date format: DD.MM.YYYY
#[tushare(field = "german_date", date_format = "%d.%m.%Y")]
pub german_date: Option<chrono::NaiveDate>,
// Custom datetime format: YYYY/MM/DD HH:MM
#[tushare(field = "custom_datetime", date_format = "%Y/%m/%d %H:%M")]
pub custom_datetime: chrono::NaiveDateTime,
// Chinese date format: YYYY年MM月DD日
#[tushare(field = "chinese_date", date_format = "%Y年%m月%d日")]
pub chinese_date: Option<chrono::NaiveDate>,
// UTC datetime with timezone: YYYY-MM-DD HH:MM:SS +ZZZZ
#[tushare(field = "utc_datetime", date_format = "%Y-%m-%d %H:%M:%S %z")]
pub utc_datetime: chrono::DateTime<chrono::Utc>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
// Example API call (note: actual API may not return these exact formats)
let data: TushareEntityList<CustomDateFormats> = client.call_api_as(request!(
Api::StockBasic, {
"list_status" => "L",
"limit" => "10"
}, [
"ts_code", "trade_date", "european_date", "us_date",
"german_date", "custom_datetime", "chinese_date", "utc_datetime"
]
)).await?;
for record in data.iter() {
println!("Stock: {} - Trade Date: {}", record.stock_code, record.trade_date);
println!(" European: {}", record.european_date);
println!(" US: {}", record.us_date);
println!(" German: {:?}", record.german_date);
println!(" Custom DateTime: {:?}", record.custom_datetime);
println!(" Chinese: {:?}", record.chinese_date);
println!(" UTC: {}", record.utc_datetime);
println!("---");
}
Ok(())
}
Common Date Format Patterns
| Format String | Example Input | Description |
|---|---|---|
"%Y-%m-%d" |
"2024-03-15" |
ISO date format |
"%d/%m/%Y" |
"15/03/2024" |
European format |
"%m-%d-%Y" |
"03-15-2024" |
US format |
"%d.%m.%Y" |
"15.03.2024" |
German format |
"%Y年%m月%d日" |
"2024年03月15日" |
Chinese format |
"%Y%m%d" |
"20240315" |
Compact format |
"%Y-%m-%d %H:%M:%S" |
"2024-03-15 14:30:00" |
DateTime format |
"%Y/%m/%d %H:%M" |
"2024/03/15 14:30" |
Custom DateTime |
"%Y-%m-%d %H:%M:%S %z" |
"2024-03-15 14:30:00 +0800" |
With timezone |
Benefits of Custom Date Formats
- Precise Control: Specify exact format per field
- No Wrapper Types: Use chrono types directly
- Type Safety: Compile-time format validation
- Flexible: Works with optional fields
- Clear Syntax: Declarative and intuitive
- Error Handling: Detailed error messages for debugging
Supported Third-Party Types
| Type | Feature Flag | Description | Example Values |
|---|---|---|---|
rust_decimal::Decimal |
rust_decimal |
High-precision decimal | "123.456", 123.456 |
bigdecimal::BigDecimal |
bigdecimal |
Arbitrary precision | "999999999999999999999.123" |
chrono::NaiveDate |
chrono |
Date without timezone | "20240315", "2024-03-15" |
chrono::NaiveDateTime |
chrono |
DateTime without timezone | "2024-03-15 14:30:00" |
chrono::DateTime<Utc> |
chrono |
UTC DateTime | RFC3339 format |
uuid::Uuid |
uuid |
UUID type | "550e8400-e29b-41d4-a716-446655440000" |
For detailed documentation and examples, see Third-Party Types Guide.
Manual Conversion (Alternative Approach)
If you prefer not to use procedural macros, you can still use the manual approach:
use tushare_api::{TushareClient, Api, request, utils::response_to_vec, traits::FromTushareData};
use tushare_api::error::TushareError;
use serde_json::Value;
#[derive(Debug, Clone)]
pub struct Stock {
pub ts_code: String,
pub name: String,
pub area: Option<String>,
}
// Manual implementation of FromTushareData
impl FromTushareData for Stock {
fn from_row(fields: &[String], values: &[Value]) -> Result<Self, TushareError> {
let ts_code_idx = fields.iter().position(|f| f == "ts_code")
.ok_or_else(|| TushareError::ParseError("Missing ts_code field".to_string()))?;
let name_idx = fields.iter().position(|f| f == "name")
.ok_or_else(|| TushareError::ParseError("Missing name field".to_string()))?;
let area_idx = fields.iter().position(|f| f == "area");
Ok(Stock {
ts_code: values[ts_code_idx].as_str()
.ok_or_else(|| TushareError::ParseError("Invalid ts_code".to_string()))?
.to_string(),
name: values[name_idx].as_str()
.ok_or_else(|| TushareError::ParseError("Invalid name".to_string()))?
.to_string(),
area: area_idx.and_then(|idx| values[idx].as_str().map(|s| s.to_string())),
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = TushareClient::from_env()?;
// Get raw response
let response = client.call_api(request!(Api::StockBasic, {
"list_status" => "L"
}, [
"ts_code", "name", "area"
])).await?;
// Convert to Vec<Stock>
let stocks = response_to_vec::<Stock>(response)?;
println!("Found {} stocks", stocks.len());
for stock in stocks.iter().take(3) {
println!(" {}: {} - Area: {:?}", stock.ts_code, stock.name, stock.area);
}
Ok(())
}
5. How to Configure Logging
The library supports both log and tracing ecosystems with flexible configuration.
Using with env_logger (Default)
// Set log level and initialize logger
std::env::set_var("RUST_LOG", "tushare_api=debug");
env_logger::init();
// Create client with logging configuration
let client = TushareClient::builder()
.with_token("your_token_here")
.with_log_level(LogLevel::Debug)
.log_requests(true) // Log request details
.log_responses(false) // Don't log response content (can be large)
.log_sensitive_data(false) // Don't log sensitive data like tokens
.log_performance(true) // Log performance metrics
.build()?;
Using with tracing (Optional Feature)
First, enable the tracing feature in your Cargo.toml:
[dependencies]
tushare-api = { version = "1.1.0", features = ["tracing"] }
tracing = "0.1"
tracing-subscriber = "0.3"
Then in your code:
use tracing_subscriber;
// Initialize tracing subscriber
std::env::set_var("RUST_LOG", "tushare_api=trace");
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
// Client configuration remains the same
let client = TushareClient::builder()
.with_token("your_token_here")
.with_log_level(LogLevel::Trace)
.log_requests(true)
.log_responses(true)
.log_performance(true)
.build()?;
Using tracing-log Bridge
If you want to use tracing but the library is compiled without the tracing feature:
[dependencies]
tushare-api = "1.1.0" # Without tracing feature
tracing = "0.1"
tracing-subscriber = "0.3"
tracing-log = "0.2"
use tracing_subscriber;
use tracing_log::LogTracer;
// Initialize log-to-tracing bridge
LogTracer::init()?;
// Set up tracing subscriber
std::env::set_var("RUST_LOG", "tushare_api=debug");
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();
Log Levels and Output
LogLevel::Off: No loggingLogLevel::Error: Only errorsLogLevel::Warn: Errors and warningsLogLevel::Info: Basic API call information (default)LogLevel::Debug: Detailed request/response informationLogLevel::Trace: All information including raw response content
Example log output:
INFO [abc123] Starting Tushare API call: stock_basic, params count: 2, fields count: 3
DEBUG [abc123] API request details - API: stock_basic, params: {...}, fields: [...]
DEBUG [abc123] Sending HTTP request to Tushare API
DEBUG [abc123] Received HTTP response, status code: 200
INFO [abc123] API call successful, duration: 245ms, data rows returned: 100
6. Main Data Structures
TushareClient
The main client for interacting with Tushare APIs.
pub struct TushareClient {
// Internal fields are private
}
impl TushareClient {
// Creation methods
pub fn new(token: &str) -> Self;
pub fn from_env() -> TushareResult<Self>;
pub fn with_timeout(token: &str, connect_timeout: Duration, timeout: Duration) -> Self;
pub fn from_env_with_timeout(connect_timeout: Duration, timeout: Duration) -> TushareResult<Self>;
pub fn builder() -> TushareClientBuilder;
// API call methods
pub async fn call_api(&self, request: TushareRequest) -> TushareResult<TushareResponse>;
pub async fn call_api_as<T>(&self, request: TushareRequest) -> TushareResult<T>
where
T: TryFrom<TushareResponse, Error = TushareError>;
}
TushareRequest
Represents an API request with parameters and field specifications.
#[derive(Debug, Clone)]
pub struct TushareRequest {
pub api_name: Api, // Which API to call
pub params: HashMap<String, String>, // Request parameters
pub fields: Vec<String>, // Fields to return
}
TushareResponse
Represents the response from Tushare API.
#[derive(Debug)]
pub struct TushareResponse {
pub request_id: String, // Unique request identifier
pub code: i32, // Response code (0 = success)
pub msg: String, // Response message
pub data: TushareData, // Actual data
}
TushareData
Contains the actual data returned by the API.
#[derive(Debug)]
pub struct TushareData {
pub fields: Vec<String>, // Field names
pub items: Vec<Vec<String>>, // Data rows
}
Api Enum
Strongly typed enumeration of all supported APIs.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Api {
// Stock APIs
StockBasic, // Basic stock information
Daily, // Daily stock prices
DailyBasic, // Daily basic stock data
Weekly, // Weekly stock data
Monthly, // Monthly stock data
StockCompany, // Company information
// Fund APIs
FundBasic, // Basic fund information
FundDaily, // Daily fund data
FundPortfolio, // Fund portfolio
// Index APIs
IndexBasic, // Basic index information
IndexDaily, // Daily index data
IndexWeekly, // Weekly index data
IndexMonthly, // Monthly index data
IndexDailyBasic, // Daily basic index data
// Market Data APIs
TradeCal, // Trading calendar
Margin, // Margin trading data
MarginDetail, // Detailed margin data
Moneyflow, // Money flow data
MoneyflowMktDc, // Market money flow
MoneyflowIndustryThs, // Industry money flow
// Financial APIs
FinaIndicator, // Financial indicators
FinaMainbz, // Main business data
FinaMainbzVip, // VIP main business data
Balancesheet, // Balance sheet
Income, // Income statement
Cashflow, // Cash flow statement
// Other APIs
StkHoldernumber, // Shareholder numbers
ThsIndex, // THS index data
ThsMember, // THS member data
ThsDaily, // THS daily data
ThsHot, // THS hot data
// US Market APIs
UsBasic, // US stock basic info
UsDaily, // US stock daily data
// Custom API
Custom(String), // For any other API by name
}
Error Types
Comprehensive error handling with specific error types.
#[derive(Debug)]
pub enum TushareError {
HttpError(reqwest::Error), // HTTP request errors
ApiError { code: i32, message: String }, // API response errors
SerializationError(serde_json::Error), // JSON parsing errors
TimeoutError, // Network timeout errors
InvalidToken, // Invalid API token
Other(String), // Other errors
}
pub type TushareResult<T> = Result<T, TushareError>;
Logging Configuration
#[derive(Debug, Clone)]
pub enum LogLevel {
Off, // No logging
Error, // Errors only
Warn, // Errors and warnings
Info, // Basic information (default)
Debug, // Detailed information
Trace, // All information including raw data
}
#[derive(Debug, Clone)]
pub struct LogConfig {
pub level: LogLevel, // Log level
pub log_requests: bool, // Log request parameters
pub log_responses: bool, // Log response content
pub log_sensitive_data: bool, // Log sensitive data (tokens)
pub log_performance: bool, // Log performance metrics
}
🔧 Advanced Usage
Processing Response Data
let response = client.call_api(request!(Api::StockBasic, {
"list_status" => "L"
}, [
"ts_code", "name", "industry"
])).await?;
// Access field names
println!("Fields: {:?}", response.data.fields);
// Process each data row
for item in response.data.items {
if item.len() >= 3 {
println!("Stock: {} - {} ({})", item[0], item[1], item[2]);
}
}
Custom API Usage
// For APIs not yet included in the enum
let response = client.call_api(request!(Api::Custom("new_api".to_string()), {
"param1" => "value1"
}, [
"field1", "field2"
])).await?;
🔍 Examples
Check out the examples/ directory for complete working examples:
# Run basic usage example
cargo run --example logging_example
# Run tracing integration example
cargo run --example tracing_example --features tracing
📋 Changelog
For a detailed history of changes, new features, and bug fixes, see CHANGELOG.md.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
📞 Support
Dependencies
~155–560KB
~13K SLoC