Skip to main content

df_papertrade_core/trade/writes/
open.rs

1//! Opening a position: checked against a fresh trading state and the account, then signed by
2//! the session key.
3
4use df_hyperliquid_core::client::Http;
5use df_hyperliquid_core::utils::Address;
6
7use crate::account::Account;
8use crate::account::store::Store;
9use crate::client::{Accepted, Client, Error};
10use crate::trade::{Intent, OpenPosition, deadline, math};
11use crate::utils::Lane;
12
13/// A new position to open.
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct Open {
16    pub instrument_id: u32,
17    pub is_long: bool,
18    /// Margin in collateral units (18 decimals).
19    pub margin: u128,
20    /// Leverage multiple, at most the market's cap.
21    pub leverage: u32,
22    /// Takes the margin from the queued balance instead of the available one.
23    pub use_debt: bool,
24}
25
26impl<H: Http + Clone> Client<H> {
27    /// Opens a position for `owner`, signed by the session key, at the BBO mid of the relayer's
28    /// batch. It is first checked against a fresh trading state, `account` as last streamed, and
29    /// `mark` (the current mid, raw) for the side's open-interest cap.
30    pub async fn open(
31        &self,
32        store: &impl Store,
33        owner: Address,
34        account: &Account,
35        open: Open,
36        mark: Option<u128>,
37    ) -> Result<Accepted, Error> {
38        let state = self.trading_state().await?;
39        let instrument = state
40            .instrument(open.instrument_id)
41            .ok_or_else(|| Error::Invalid(format!("no market {}", open.instrument_id)))?;
42        let (available, room) = if open.use_debt {
43            let room = math::debt_room(account.consumable_debt, account.queued, account.funds.available_queued);
44            (account.funds.available_queued, Some(room))
45        } else {
46            (account.funds.available_balance, None)
47        };
48        math::check_open(&state, instrument, open.is_long, open.margin, open.leverage, mark, available, room)?;
49        let key = self.session_key(store, owner, Some(account)).await?;
50        let nonce = self.take_nonce(store, owner, Lane::Exchange).await?;
51        let intent = OpenPosition::new(
52            owner,
53            open.instrument_id,
54            open.is_long,
55            open.margin,
56            open.leverage,
57            open.use_debt,
58            nonce,
59            deadline(self.now_ms()),
60        )?;
61        let signed =
62            key.sign(Intent::OpenPosition(intent), self.now_ms()).map_err(|e| Error::Signature(e.to_string()))?;
63        self.submit(&signed).await
64    }
65}