Skip to main content

TbankRaw

Struct TbankRaw 

Source
pub struct TbankRaw { /* private fields */ }
Expand description

Direct access to any T-Bank API method, made by MarketDataConnector::raw or UserDataConnector::raw. See the module docs. Cheap to clone.

Reads go through call. Anything that changes the account goes through call_command, which never lets a lost answer look like a refusal.

§Examples

A command that the broker refuses is [Error::Rejected]. Here the order does not exist.

use brokapi_core::Error;
use brokapi_runtime::BrokerLimiter;
use brokapi_tbank::limits::default_limits;
use brokapi_tbank::{UserDataConfig, UserDataConnector};
use brokapi_tbank_protocol::v1 as pb;
use tokio::sync::mpsc;

let (events_tx, _events) = mpsc::channel(64);
let limiter = BrokerLimiter::new(&default_limits());
let ud = UserDataConnector::start(UserDataConfig::new(cfg, "tbank-ud"), limiter, events_tx).await?;
let raw = ud.raw();
let account = raw.account_id().expect("a user data connector has an account").to_string();

let result = raw
    .call_command("CancelOrder", |c| async move {
        c.orders()
            .cancel_order(pb::CancelOrderRequest {
                account_id: account,
                order_id: "no-such-order".into(),
                order_id_type: Some(pb::OrderIdType::Request as i32),
            })
            .await
    })
    .await;
assert!(matches!(result, Err(Error::Rejected { .. })));

Implementations§

Source§

impl TbankRaw

Source

pub fn account_id(&self) -> Option<&str>

Account of the connector this came from (user data connector only).

None for a market data connector, which has no account. The raw layer does not add the account to your requests: put it into the request yourself.

§Examples
use brokapi_runtime::BrokerLimiter;
use brokapi_tbank::limits::default_limits;
use brokapi_tbank::{MarketDataConfig, MarketDataConnector};
use tokio::sync::mpsc;

let (events_tx, _events) = mpsc::channel(64);
let md = MarketDataConnector::start(
    MarketDataConfig::new(cfg, "tbank-md"),
    BrokerLimiter::new(&default_limits()),
    events_tx,
)
.await?;
assert_eq!(md.raw().account_id(), None);
Source

pub fn timeout(&self) -> Duration

The deadline applied to every call: the request_timeout of the connector’s config (30 s by default).

§Examples
use std::time::Duration;
use brokapi_runtime::BrokerLimiter;
use brokapi_tbank::limits::default_limits;
use brokapi_tbank::{MarketDataConfig, MarketDataConnector};
use tokio::sync::mpsc;

let (events_tx, _events) = mpsc::channel(64);
let md = MarketDataConnector::start(
    MarketDataConfig::new(cfg, "tbank-md"),
    BrokerLimiter::new(&default_limits()),
    events_tx,
)
.await?;
assert_eq!(md.raw().timeout(), Duration::from_secs(5));
Source

pub async fn call<T, F, Fut>(&self, method: &str, f: F) -> Result<T, Error>
where F: FnOnce(TbankClient) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,

Reads: takes quota for method, runs the call, maps errors.

For a server-streaming method the result type is tonic::Streaming<_>: the quota covers opening the stream, the stream is then yours to read.

§Errors
  • [Error::RateLimited] when the limiter has no quota for method. The request is not sent. Wait retry_after and call again.
  • [Error::Transport] on a timeout or a network status. A read may be repeated.
  • [Error::Rejected] when the broker refuses the request (InvalidArgument, NotFound, which is the code not_found, PermissionDenied, …).
  • [Error::Config] when the token is not accepted (Unauthenticated).
§Examples
use brokapi_core::Error;
use brokapi_runtime::BrokerLimiter;
use brokapi_tbank::limits::limits_from_toml_str;
use brokapi_tbank::{MarketDataConfig, MarketDataConnector};
use brokapi_tbank_protocol::v1 as pb;
use tokio::sync::mpsc;

// A tiny quota for the demo: one call per minute for every method.
let limits = limits_from_toml_str(
    r#"
[tbank.buckets.one]
rate = 1
per = "60s"
burst = 1
[tbank.route]
"*" = ["one"]
[tbank.conns]
max_conns = 1
max_subs_per_conn = 1
supports_multiplex = false
"#,
)?;
let (events_tx, _events) = mpsc::channel(64);
let md = MarketDataConnector::start(MarketDataConfig::new(cfg, "tbank-md"), BrokerLimiter::new(&limits), events_tx).await?;
let raw = md.raw();

let list = |c: brokapi_tbank_protocol::TbankClient| async move {
    c.users().get_accounts(pb::GetAccountsRequest { status: None }).await
};
assert!(raw.call("GetAccounts", list).await.is_ok());
// The second call finds the bucket empty: it is refused locally and never sent.
assert!(matches!(raw.call("GetAccounts", list).await, Err(Error::RateLimited { .. })));
Source

pub async fn call_command<T, F, Fut>( &self, method: &str, f: F, ) -> Result<T, Error>
where F: FnOnce(TbankClient) -> Fut, Fut: Future<Output = Result<Response<T>, Status>>,

Commands: like call, but a timeout, a break or a server fault is Error::Unknown, because the broker may have executed the command. Never retried.

§Errors
  • [Error::RateLimited] when the limiter has no quota. Nothing was sent.
  • [Error::Rejected] when the broker refuses (InvalidArgument, NotFound, PermissionDenied, FailedPrecondition, …). The broker did not act.
  • [Error::Config] when the token is not accepted. The broker did not act.
  • [Error::Unknown] on a timeout, a broken connection or a server fault (Unavailable, Internal, …). The command may or may not have been done. Do not repeat it. Ask the broker for the state of the order or account first.
§Examples
use brokapi_core::Error;
use brokapi_runtime::BrokerLimiter;
use brokapi_tbank::limits::default_limits;
use brokapi_tbank::{UserDataConfig, UserDataConnector};
use brokapi_tbank_protocol::v1 as pb;
use tokio::sync::mpsc;

let (events_tx, _events) = mpsc::channel(64);
let limiter = BrokerLimiter::new(&default_limits());
let ud = UserDataConnector::start(UserDataConfig::new(cfg, "tbank-ud"), limiter, events_tx).await?;

// Cancelling an order the broker does not know is a definite refusal.
let outcome = ud
    .raw()
    .call_command("CancelOrder", |c| async move {
        c.orders()
            .cancel_order(pb::CancelOrderRequest {
                account_id: ACCOUNT_A.into(),
                order_id: "no-such-order".into(),
                order_id_type: Some(pb::OrderIdType::Request as i32),
            })
            .await
    })
    .await;
match outcome {
    Err(Error::Rejected { code, .. }) => assert_eq!(code, "not_found"),
    other => panic!("expected a refusal, got {other:?}"),
}

Trait Implementations§

Source§

impl Clone for TbankRaw

Source§

fn clone(&self) -> TbankRaw

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> FromRef<T> for T
where T: Clone,

§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

§

impl<T> IntoRequest<T> for T

§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
§

impl<L> LayerExt<L> for L

§

fn named_layer<S>(&self, service: S) -> Layered<<L as Layer<S>>::Service, S>
where L: Layer<S>,

Applies the layer to a service and wraps it in [Layered].
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more