truefix-twsapi-client 0.1.4

Thin Interactive Brokers TWS/Gateway protocol client for TrueFix.
Documentation
use super::*;

pub(super) fn proto_order_to_order(order: Option<protobuf::Order>) -> TwsApiResult<Order> {
    let Some(order) = order else {
        return Ok(Order::default());
    };

    Ok(Order {
        soft_dollar_tier: proto_soft_dollar_tier(order.soft_dollar_tier),
        order_id: order.order_id.unwrap_or_default(),
        client_id: order.client_id.unwrap_or_default(),
        perm_id: order.perm_id.unwrap_or_default(),
        action: order.action.unwrap_or_default(),
        total_quantity: parse_decimal_string(order.total_quantity.as_deref())?,
        order_type: order.order_type.unwrap_or_default(),
        limit_price: order.lmt_price.unwrap_or_default(),
        aux_price: order.aux_price.unwrap_or_default(),
        tif: order.tif.unwrap_or_default(),
        active_start_time: order.active_start_time.unwrap_or_default(),
        active_stop_time: order.active_stop_time.unwrap_or_default(),
        oca_group: order.oca_group.unwrap_or_default(),
        oca_type: order.oca_type.unwrap_or_default(),
        order_ref: order.order_ref.unwrap_or_default(),
        transmit: order.transmit.unwrap_or_default(),
        parent_id: order.parent_id.unwrap_or_default(),
        block_order: order.block_order.unwrap_or_default(),
        sweep_to_fill: order.sweep_to_fill.unwrap_or_default(),
        display_size: order.display_size.unwrap_or_default(),
        trigger_method: order.trigger_method.unwrap_or_default(),
        outside_rth: order.outside_rth.unwrap_or_default(),
        hidden: order.hidden.unwrap_or_default(),
        good_after_time: order.good_after_time.unwrap_or_default(),
        good_till_date: order.good_till_date.unwrap_or_default(),
        rule80a: order.rule80_a.unwrap_or_default(),
        all_or_none: order.all_or_none.unwrap_or_default(),
        min_qty: order.min_qty.unwrap_or_default(),
        percent_offset: order.percent_offset.unwrap_or_default(),
        override_percentage_constraints: order.override_percentage_constraints.unwrap_or_default(),
        trail_stop_price: order.trail_stop_price.unwrap_or_default(),
        trailing_percent: order.trailing_percent.unwrap_or_default(),
        fa_group: order.fa_group.unwrap_or_default(),
        fa_method: order.fa_method.unwrap_or_default(),
        fa_percentage: order.fa_percentage.unwrap_or_default(),
        designated_location: order.designated_location.unwrap_or_default(),
        open_close: order.open_close.unwrap_or_default(),
        origin: match order.origin.unwrap_or_default() {
            1 => Origin::Firm,
            2 => Origin::Unknown,
            _ => Origin::Customer,
        },
        short_sale_slot: order.short_sale_slot.unwrap_or_default(),
        exempt_code: order.exempt_code.unwrap_or_default(),
        discretionary_amount: order.discretionary_amt.unwrap_or_default(),
        opt_out_smart_routing: order.opt_out_smart_routing.unwrap_or_default(),
        starting_price: order.starting_price.unwrap_or_default(),
        stock_ref_price: order.stock_ref_price.unwrap_or_default(),
        delta: order.delta.unwrap_or_default(),
        stock_range_lower: order.stock_range_lower.unwrap_or_default(),
        stock_range_upper: order.stock_range_upper.unwrap_or_default(),
        randomize_price: order.randomize_price.unwrap_or_default(),
        randomize_size: order.randomize_size.unwrap_or_default(),
        volatility: order.volatility.unwrap_or_default(),
        volatility_type: order.volatility_type.unwrap_or_default(),
        delta_neutral_order_type: order.delta_neutral_order_type.unwrap_or_default(),
        delta_neutral_aux_price: order.delta_neutral_aux_price.unwrap_or_default(),
        delta_neutral_con_id: order.delta_neutral_con_id.unwrap_or_default(),
        delta_neutral_settling_firm: order.delta_neutral_settling_firm.unwrap_or_default(),
        delta_neutral_clearing_account: order.delta_neutral_clearing_account.unwrap_or_default(),
        delta_neutral_clearing_intent: order.delta_neutral_clearing_intent.unwrap_or_default(),
        delta_neutral_open_close: order.delta_neutral_open_close.unwrap_or_default(),
        delta_neutral_short_sale: order.delta_neutral_short_sale.unwrap_or_default(),
        delta_neutral_short_sale_slot: order.delta_neutral_short_sale_slot.unwrap_or_default(),
        delta_neutral_designated_location: order
            .delta_neutral_designated_location
            .unwrap_or_default(),
        continuous_update: order.continuous_update.unwrap_or_default(),
        reference_price_type: order.reference_price_type.unwrap_or_default(),
        scale_init_level_size: order.scale_init_level_size.unwrap_or_default(),
        scale_subs_level_size: order.scale_subs_level_size.unwrap_or_default(),
        scale_price_increment: order.scale_price_increment.unwrap_or_default(),
        scale_price_adjust_value: order.scale_price_adjust_value.unwrap_or_default(),
        scale_price_adjust_interval: order.scale_price_adjust_interval.unwrap_or_default(),
        scale_profit_offset: order.scale_profit_offset.unwrap_or_default(),
        scale_auto_reset: order.scale_auto_reset.unwrap_or_default(),
        scale_init_position: order.scale_init_position.unwrap_or_default(),
        scale_init_fill_qty: order.scale_init_fill_qty.unwrap_or_default(),
        scale_random_percent: order.scale_random_percent.unwrap_or_default(),
        scale_table: order.scale_table.unwrap_or_default(),
        hedge_type: order.hedge_type.unwrap_or_default(),
        hedge_param: order.hedge_param.unwrap_or_default(),
        hedge_max_size: order.hedge_max_size.unwrap_or_default(),
        account: order.account.unwrap_or_default(),
        settling_firm: order.settling_firm.unwrap_or_default(),
        clearing_account: order.clearing_account.unwrap_or_default(),
        clearing_intent: order.clearing_intent.unwrap_or_default(),
        model_code: order.model_code.unwrap_or_default(),
        algo_strategy: order.algo_strategy.unwrap_or_default(),
        algo_params: proto_tag_values(order.algo_params),
        smart_combo_routing_params: proto_tag_values(order.smart_combo_routing_params),
        algo_id: order.algo_id.unwrap_or_default(),
        what_if: order.what_if.unwrap_or_default(),
        not_held: order.not_held.unwrap_or_default(),
        solicited: order.solicited.unwrap_or_default(),
        order_misc_options: proto_tag_values(order.order_misc_options),
        reference_contract_id: order.reference_contract_id.unwrap_or_default(),
        pegged_change_amount: order.pegged_change_amount.unwrap_or_default(),
        is_pegged_change_amount_decrease: order
            .is_pegged_change_amount_decrease
            .unwrap_or_default(),
        reference_change_amount: order.reference_change_amount.unwrap_or_default(),
        reference_exchange_id: order.reference_exchange_id.unwrap_or_default(),
        adjusted_order_type: order.adjusted_order_type.unwrap_or_default(),
        trigger_price: order.trigger_price.unwrap_or_default(),
        adjusted_stop_price: order.adjusted_stop_price.unwrap_or_default(),
        adjusted_stop_limit_price: order.adjusted_stop_limit_price.unwrap_or_default(),
        adjusted_trailing_amount: order.adjusted_trailing_amount.unwrap_or_default(),
        adjustable_trailing_unit: order.adjustable_trailing_unit.unwrap_or_default(),
        limit_price_offset: order.lmt_price_offset.unwrap_or_default(),
        conditions_cancel_order: order.conditions_cancel_order.unwrap_or_default(),
        conditions_ignore_rth: order.conditions_ignore_rth.unwrap_or_default(),
        ext_operator: order.ext_operator.unwrap_or_default(),
        cash_qty: order.cash_qty.unwrap_or_default(),
        mifid2_decision_maker: order.mifid2_decision_maker.unwrap_or_default(),
        mifid2_decision_algo: order.mifid2_decision_algo.unwrap_or_default(),
        mifid2_execution_trader: order.mifid2_execution_trader.unwrap_or_default(),
        mifid2_execution_algo: order.mifid2_execution_algo.unwrap_or_default(),
        dont_use_auto_price_for_hedge: order.dont_use_auto_price_for_hedge.unwrap_or_default(),
        is_oms_container: order.is_oms_container.unwrap_or_default(),
        discretionary_up_to_limit_price: order.discretionary_up_to_limit_price.unwrap_or_default(),
        auto_cancel_date: order.auto_cancel_date.unwrap_or_default(),
        filled_quantity: parse_decimal_string(order.filled_quantity.as_deref())?,
        ref_futures_con_id: order.ref_futures_con_id.unwrap_or_default(),
        auto_cancel_parent: order.auto_cancel_parent.unwrap_or_default(),
        shareholder: order.shareholder.unwrap_or_default(),
        imbalance_only: order.imbalance_only.unwrap_or_default(),
        route_marketable_to_bbo: order.route_marketable_to_bbo.unwrap_or_default(),
        parent_perm_id: order.parent_perm_id.unwrap_or_default(),
        use_price_mgmt_algo: order.use_price_mgmt_algo.unwrap_or_default(),
        duration: order.duration.unwrap_or_default(),
        post_to_ats: order.post_to_ats.unwrap_or_default(),
        advanced_error_override: order.advanced_error_override.unwrap_or_default(),
        manual_order_time: order.manual_order_time.unwrap_or_default(),
        min_trade_qty: order.min_trade_qty.unwrap_or_default(),
        min_compete_size: order.min_compete_size.unwrap_or_default(),
        compete_against_best_offset: order.compete_against_best_offset.unwrap_or_default(),
        mid_offset_at_whole: order.mid_offset_at_whole.unwrap_or_default(),
        mid_offset_at_half: order.mid_offset_at_half.unwrap_or_default(),
        customer_account: order.customer_account.unwrap_or_default(),
        professional_customer: order.professional_customer.unwrap_or_default(),
        bond_accrued_interest: order.bond_accrued_interest.unwrap_or_default(),
        include_overnight: order.include_overnight.unwrap_or_default(),
        manual_order_indicator: order.manual_order_indicator.unwrap_or_default(),
        submitter: order.submitter.unwrap_or_default(),
        post_only: order.post_only.unwrap_or_default(),
        allow_pre_open: order.allow_pre_open.unwrap_or_default(),
        ignore_open_auction: order.ignore_open_auction.unwrap_or_default(),
        deactivate: order.deactivate.unwrap_or_default(),
        seek_price_improvement: order.seek_price_improvement.unwrap_or_default(),
        what_if_type: order.what_if_type.unwrap_or_default(),
        ..Order::default()
    })
}

pub(super) fn proto_order_state_to_order_state(
    order_state: Option<protobuf::OrderState>,
) -> OrderState {
    let Some(order_state) = order_state else {
        return OrderState::default();
    };

    OrderState {
        status: order_state.status.unwrap_or_default(),
        init_margin_before: order_state.init_margin_before.unwrap_or_default(),
        maint_margin_before: order_state.maint_margin_before.unwrap_or_default(),
        equity_with_loan_before: order_state.equity_with_loan_before.unwrap_or_default(),
        init_margin_change: order_state.init_margin_change.unwrap_or_default(),
        maint_margin_change: order_state.maint_margin_change.unwrap_or_default(),
        equity_with_loan_change: order_state.equity_with_loan_change.unwrap_or_default(),
        init_margin_after: order_state.init_margin_after.unwrap_or_default(),
        maint_margin_after: order_state.maint_margin_after.unwrap_or_default(),
        equity_with_loan_after: order_state.equity_with_loan_after.unwrap_or_default(),
        commission_and_fees: order_state.commission_and_fees.unwrap_or_default(),
        min_commission_and_fees: order_state.min_commission_and_fees.unwrap_or_default(),
        max_commission_and_fees: order_state.max_commission_and_fees.unwrap_or_default(),
        commission_and_fees_currency: order_state.commission_and_fees_currency.unwrap_or_default(),
        margin_currency: order_state.margin_currency.unwrap_or_default(),
        init_margin_before_outside_rth: order_state
            .init_margin_before_outside_rth
            .unwrap_or_default(),
        maint_margin_before_outside_rth: order_state
            .maint_margin_before_outside_rth
            .unwrap_or_default(),
        equity_with_loan_before_outside_rth: order_state
            .equity_with_loan_before_outside_rth
            .unwrap_or_default(),
        init_margin_change_outside_rth: order_state
            .init_margin_change_outside_rth
            .unwrap_or_default(),
        maint_margin_change_outside_rth: order_state
            .maint_margin_change_outside_rth
            .unwrap_or_default(),
        equity_with_loan_change_outside_rth: order_state
            .equity_with_loan_change_outside_rth
            .unwrap_or_default(),
        init_margin_after_outside_rth: order_state
            .init_margin_after_outside_rth
            .unwrap_or_default(),
        maint_margin_after_outside_rth: order_state
            .maint_margin_after_outside_rth
            .unwrap_or_default(),
        equity_with_loan_after_outside_rth: order_state
            .equity_with_loan_after_outside_rth
            .unwrap_or_default(),
        suggested_size: order_state.suggested_size.unwrap_or_default(),
        reject_reason: order_state.reject_reason.unwrap_or_default(),
        order_allocations: order_state
            .order_allocations
            .into_iter()
            .map(proto_order_allocation_to_order_allocation)
            .collect::<TwsApiResult<Vec<_>>>()
            .unwrap_or_default(),
        warning_text: order_state.warning_text.unwrap_or_default(),
        completed_time: order_state.completed_time.unwrap_or_default(),
        completed_status: order_state.completed_status.unwrap_or_default(),
    }
}

fn proto_order_allocation_to_order_allocation(
    allocation: protobuf::OrderAllocation,
) -> TwsApiResult<OrderAllocation> {
    Ok(OrderAllocation {
        account: allocation.account.unwrap_or_default(),
        position: parse_decimal_string(allocation.position.as_deref())?,
        position_desired: parse_decimal_string(allocation.position_desired.as_deref())?,
        position_after: parse_decimal_string(allocation.position_after.as_deref())?,
        desired_alloc_qty: parse_decimal_string(allocation.desired_alloc_qty.as_deref())?,
        allowed_alloc_qty: parse_decimal_string(allocation.allowed_alloc_qty.as_deref())?,
        is_monetary: allocation.is_monetary.unwrap_or_default(),
    })
}

fn proto_soft_dollar_tier(tier: Option<protobuf::SoftDollarTier>) -> SoftDollarTier {
    let Some(tier) = tier else {
        return SoftDollarTier::default();
    };

    SoftDollarTier {
        name: tier.name.unwrap_or_default(),
        value: tier.value.unwrap_or_default(),
        display_name: tier.display_name.unwrap_or_default(),
    }
}

fn proto_tag_values(values: std::collections::HashMap<String, String>) -> Vec<TagValue> {
    values
        .into_iter()
        .map(|(tag, value)| TagValue { tag, value })
        .collect()
}