alpaca-mock 0.30.0

Executable mock server for alpaca-rust trading and market-data flows
Documentation
use alpaca_trade::assets::{Asset, AssetAttribute, AssetClass, AssetStatus, Exchange};
use axum::{
    Json,
    extract::{Path, Query, State},
};
use serde::Deserialize;

use crate::auth::MockHttpError;
use crate::state::{ListAssetsFilter, MockServerState};

#[derive(Debug, Default, Deserialize)]
pub(crate) struct ListAssetsQuery {
    status: Option<AssetStatus>,
    asset_class: Option<AssetClass>,
    exchange: Option<Exchange>,
    attributes: Option<String>,
}

pub(crate) async fn assets_get(
    State(state): State<MockServerState>,
    Path(symbol_or_asset_id): Path<String>,
) -> Result<Json<Asset>, MockHttpError> {
    state
        .get_asset(&symbol_or_asset_id)
        .map(Json)
        .ok_or_else(|| MockHttpError::not_found("asset was not found"))
}

pub(crate) async fn assets_list(
    State(state): State<MockServerState>,
    Query(query): Query<ListAssetsQuery>,
) -> Result<Json<Vec<Asset>>, MockHttpError> {
    Ok(Json(state.list_assets(ListAssetsFilter {
        status: query.status,
        asset_class: query.asset_class,
        exchange: query.exchange,
        attributes: parse_attributes(query.attributes)?,
    })))
}

fn parse_attributes(value: Option<String>) -> Result<Option<Vec<AssetAttribute>>, MockHttpError> {
    value
        .map(|value| {
            value
                .split(',')
                .map(|value| match value.trim() {
                    "ptp_no_exception" => Ok(AssetAttribute::PtpNoException),
                    "ptp_with_exception" => Ok(AssetAttribute::PtpWithException),
                    "ipo" => Ok(AssetAttribute::Ipo),
                    "has_options" => Ok(AssetAttribute::HasOptions),
                    "options_late_close" => Ok(AssetAttribute::OptionsLateClose),
                    "fractional_eh_enabled" => Ok(AssetAttribute::FractionalEhEnabled),
                    "overnight_tradable" => Ok(AssetAttribute::OvernightTradable),
                    "overnight_halted" => Ok(AssetAttribute::OvernightHalted),
                    _ => Err(MockHttpError::bad_request("unsupported asset attribute")),
                })
                .collect()
        })
        .transpose()
}