ctrader-fix 0.5.4

A rust implementation of the cTrader FIX API.
Documentation
use chrono::NaiveDateTime;

use crate::{
    messages::ResponseMessage,
    types::{
        Error, ExecutionReport, ExecutionType, Field, OrderReport, OrderStatus, OrderType,
        PositionReport, Side, SymbolInformation,
    },
};

pub fn parse_security_list(res: &ResponseMessage) -> Result<Vec<SymbolInformation>, Error> {
    let sec_list = res.get_repeating_groups(Field::NoRelatedSym, Field::Symbol, None);
    let mut result = Vec::new();
    for symbol in sec_list.into_iter() {
        if symbol.len() < 3 {
            continue;
        }
        result.push(SymbolInformation {
            name: symbol
                .get(&Field::SymbolName)
                .ok_or(Error::FieldNotFoundError(Field::SymbolName))?
                .clone(),
            id: symbol
                .get(&Field::Symbol)
                .ok_or(Error::FieldNotFoundError(Field::Symbol))?
                .parse::<u32>()
                .unwrap(),
            digits: symbol
                .get(&Field::SymbolDigits)
                .ok_or(Error::FieldNotFoundError(Field::SymbolDigits))?
                .parse::<u32>()
                .unwrap(),
        });
    }
    Ok(result)
}

pub fn parse_positions(res: Vec<ResponseMessage>) -> Result<Vec<PositionReport>, Error> {
    Ok(res
        .into_iter()
        .filter(|res| res.get_field_value(Field::PosReqResult).unwrap() == "0")
        .filter(|res| {
            res.get_field_value(Field::NoPositions)
                .map(|v| v == "1")
                .unwrap_or(false)
        })
        .map(|res| PositionReport {
            symbol_id: res
                .get_field_value(Field::Symbol)
                .unwrap()
                .parse::<u32>()
                .unwrap(),
            position_id: res.get_field_value(Field::PosMaintRptID).unwrap(),
            long_qty: res
                .get_field_value(Field::LongQty)
                .unwrap()
                .parse::<f64>()
                .unwrap(),
            short_qty: res
                .get_field_value(Field::ShortQty)
                .unwrap()
                .parse::<f64>()
                .unwrap(),
            settle_price: res
                .get_field_value(Field::SettlPrice)
                .unwrap()
                .parse::<f64>()
                .unwrap(),
            absolute_tp: res
                .get_field_value(Field::AbsoluteTP)
                .map(|v| v.parse::<f64>().unwrap()),
            absolute_sl: res
                .get_field_value(Field::AbsoluteSL)
                .map(|v| v.parse::<f64>().unwrap()),
            trailing_sl: res.get_field_value(Field::TrailingSL).map(|v| v == "Y"),
            trigger_method_sl: res
                .get_field_value(Field::TriggerMethodSL)
                .map(|v| v.parse::<u32>().unwrap()),
            guaranteed_sl: res.get_field_value(Field::GuaranteedSL).map(|v| v == "Y"),
        })
        .collect())
}

//
// ORDER
//
pub fn parse_execution_report(res: ResponseMessage) -> Result<ExecutionReport, Error> {
    Ok(ExecutionReport {
        exec_type: res
            .get_field_value(Field::ExecType)
            .unwrap()
            .parse::<ExecutionType>()
            .unwrap(),
        order_report: OrderReport {
            symbol: res
                .get_field_value(Field::Symbol)
                .unwrap_or("0".into())
                .parse::<u32>()
                .unwrap(),
            order_id: res.get_field_value(Field::OrderID).unwrap(),
            cl_ord_id: res.get_field_value(Field::ClOrdId).unwrap(),
            pos_main_rept_id: res.get_field_value(Field::PosMaintRptID).unwrap(),
            designation: res.get_field_value(Field::Designation),

            order_status: res
                .get_field_value(Field::OrdStatus)
                .map(|v| v.parse::<OrderStatus>().unwrap())
                .unwrap(),
            order_type: res
                .get_field_value(Field::OrdType)
                .map(|v| v.parse::<OrderType>().unwrap())
                .unwrap(),
            side: res
                .get_field_value(Field::Side)
                .map(|v| Side::try_from(v.parse::<u32>().unwrap()).unwrap())
                .unwrap(),

            price: res
                .get_field_value(Field::Price)
                .map(|v| v.parse::<f64>().unwrap()),
            stop_px: res
                .get_field_value(Field::StopPx)
                .map(|v| v.parse::<f64>().unwrap()),
            avx_px: res
                .get_field_value(Field::AvgPx)
                .map(|v| v.parse::<f64>().unwrap()),

            absolute_tp: res
                .get_field_value(Field::AbsoluteTP)
                .map(|v| v.parse::<f64>().unwrap()),
            reltative_tp: res
                .get_field_value(Field::RelativeTP)
                .map(|v| v.parse::<f64>().unwrap()),
            absolute_sl: res
                .get_field_value(Field::AbsoluteSL)
                .map(|v| v.parse::<f64>().unwrap()),
            reltative_sl: res
                .get_field_value(Field::RelativeSL)
                .map(|v| v.parse::<f64>().unwrap()),
            trailing_sl: res.get_field_value(Field::TrailingSL).map(|v| v == "Y"),
            trigger_method_sl: res
                .get_field_value(Field::TriggerMethodSL)
                .map(|v| v.parse::<u32>().unwrap()),
            guaranteed_sl: res.get_field_value(Field::GuaranteedSL).map(|v| v == "Y"),

            cum_qty: res
                .get_field_value(Field::CumQty)
                .map(|v| v.parse::<f64>().unwrap()),
            order_qty: res
                .get_field_value(Field::OrderQty)
                .unwrap_or("0.0".into())
                .parse::<f64>()
                .unwrap(),
            leaves_qty: res
                .get_field_value(Field::LeavesQty)
                .unwrap_or("0.0".into())
                .parse::<f64>()
                .unwrap(),
            last_qty: res
                .get_field_value(Field::OrderQty)
                .map(|v| v.parse::<f64>().unwrap()),

            time_in_force: res.get_field_value(Field::TimeInForce).unwrap(),
            transact_time: res
                .get_field_value(Field::TransactTime)
                .map(|v| NaiveDateTime::parse_from_str(v.as_str(), "%Y%m%d-%H:%M:%S%.3f").unwrap())
                .unwrap(),
            expire_time: res
                .get_field_value(Field::ExpireTime)
                .map(|v| NaiveDateTime::parse_from_str(v.as_str(), "%Y%m%d-%H:%M:%S%.3f").unwrap()),

            text: res.get_field_value(Field::Text),
        },
    })
}

pub fn parse_order_mass_status(res: Vec<ResponseMessage>) -> Result<Vec<ExecutionReport>, Error> {
    Ok(res
        .into_iter()
        .map(|res| parse_execution_report(res).unwrap())
        .collect::<Vec<_>>())
}