diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce92952..fd9e398 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,24 +17,24 @@ jobs: - name: checkout-code uses: actions/checkout@v4 - name: cargo-check - run: nix develop --command cargo check + run: nix develop .#ci --command cargo check - name: cargo-test - run: nix develop --command cargo test --all-features + run: nix develop .#ci --command cargo test --all-features - name: cargo-clippy - run: nix develop --command cargo clippy + run: nix develop .#ci --command cargo clippy - name: cargo-bench - run: nix develop --command cargo bench --no-run # Just to make sure it compiles + run: nix develop .#ci --command cargo bench --no-run # Just to make sure it compiles - name: cargo-fmt - run: nix develop --command cargo fmt --check + run: nix develop .#ci --command cargo fmt --check - name: cargo-doc - run: nix develop --command cargo doc + run: nix develop .#ci --command cargo doc - name: nix-flake-check run: nix flake check - name: nix-deadnix - run: nix develop --command deadnix + run: nix develop .#ci --command deadnix - name: nix-statix - run: nix develop --command statix check + run: nix develop .#ci --command statix check - name: nix-alejandra - run: nix develop --command alejandra --check . + run: nix develop .#ci --command alejandra --check . - name: yamlfmt - run: nix develop --command yamlfmt -lint . + run: nix develop .#ci --command yamlfmt -lint --gitignore_excludes . diff --git a/Cargo.lock b/Cargo.lock index 95cf60a..2fd8f06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1136,7 +1136,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lfest" -version = "0.138.3" +version = "0.138.4" dependencies = [ "assert2", "const-decimal", diff --git a/Cargo.toml b/Cargo.toml index 4ee9f00..6d6087e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lfest" -version = "0.138.3" +version = "0.138.4" authors = ["MathisWellmann "] edition = "2024" license-file = "LICENSE" diff --git a/flake.nix b/flake.nix index 442655f..02f3c31 100644 --- a/flake.nix +++ b/flake.nix @@ -80,19 +80,29 @@ ]; in with pkgs; { - devShells.default = mkShell { - buildInputs = buildInputs ++ rust_tools ++ nix_tools ++ tools; - RUST_BACKTRACE = "1"; - }; - # Minimal shell for the AI PR review workflow - # (.github/workflows/review.yml). Kept tiny on purpose: `nix develop` - # builds this closure on the runner, so no rust/pythonEnv here. - devShells.review = mkShell { - buildInputs = with pkgs; [ - llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.pi - jq - curl - ]; + devShells = { + # Minimal shell for .github/workflows/ci.yml: only the tools CI runs. + # The default shell also builds creusot, hongdown and the cargo-* + # tools, for which nix fetches crate tarballs from crates.io at + # build time; CI never uses them. + ci = mkShell { + buildInputs = [rust] ++ [deadnix statix alejandra yamlfmt]; + RUST_BACKTRACE = "1"; + }; + default = mkShell { + buildInputs = buildInputs ++ rust_tools ++ nix_tools ++ tools; + RUST_BACKTRACE = "1"; + }; + # Minimal shell for the AI PR review workflow + # (.github/workflows/review.yml). Kept tiny on purpose: `nix develop` + # builds this closure on the runner, so no rust/pythonEnv here. + review = mkShell { + buildInputs = [ + llm-agents.packages.${pkgs.stdenv.hostPlatform.system}.pi + jq + curl + ]; + }; }; } ); diff --git a/src/account/position.rs b/src/account/position.rs index 9a0c7fe..5098817 100644 --- a/src/account/position.rs +++ b/src/account/position.rs @@ -91,6 +91,27 @@ where } } + /// The signed position quantity as an `f64` (negative for a short position). + #[must_use] + #[inline(always)] + pub fn to_f64(&self) -> f64 { + self.quantity.into() + } + + /// Whether the position is long (positive quantity). + #[must_use] + #[inline(always)] + pub fn is_long(&self) -> bool { + self.quantity.is_positive() + } + + /// Whether the position is short (negative quantity). + #[must_use] + #[inline(always)] + pub fn is_short(&self) -> bool { + self.quantity.is_negative() + } + /// Return the positions unrealized profit and loss. #[must_use] #[inline(always)] @@ -332,6 +353,31 @@ mod tests { assert_eq!(size_of::>>(), 16); } + #[test] + fn position_to_f64_and_side_predicates() { + let long = Position::new( + BaseCurrency::::new(17, 2), + QuoteCurrency::new(100, 0), + ) + .unwrap(); + assert!(long.is_long()); + assert!(!long.is_short()); + assert_eq!(long.to_f64(), 0.17); + + let short = Position::new( + -BaseCurrency::::new(17, 2), + QuoteCurrency::new(100, 0), + ) + .unwrap(); + assert!(short.is_short()); + assert!(!short.is_long()); + assert_eq!(short.to_f64(), -0.17); + + let neutral: Position> = Position::default(); + assert!(!neutral.is_long()); + assert!(!neutral.is_short()); + } + #[test] fn position_side_invert() { use PositionSide::*; diff --git a/src/market_state.rs b/src/market_state.rs index 882c4e8..418bae9 100644 --- a/src/market_state.rs +++ b/src/market_state.rs @@ -82,6 +82,18 @@ where self.step += 1; } + /// The best (highest) bid price. + #[inline(always)] + pub fn best_bid(&self) -> QuoteCurrency { + self.bid + } + + /// The best (lowest) ask price. + #[inline(always)] + pub fn best_ask(&self) -> QuoteCurrency { + self.ask + } + /// Get the mid price #[inline(always)] pub fn mid_price(&self) -> QuoteCurrency { @@ -130,6 +142,22 @@ mod test { ); } + #[test] + fn market_state_best_bid_and_ask() { + let mut state = MarketState::::default(); + let pf = PriceFilter::default(); + state.update_state::<_, BaseCurrency<_, 1>>( + &Bba { + bid: QuoteCurrency::::new(100, 0), + ask: QuoteCurrency::new(101, 0), + timestamp_exchange_ns: 1.into(), + }, + &pf, + ); + assert_eq!(state.best_bid(), QuoteCurrency::new(100, 0)); + assert_eq!(state.best_ask(), QuoteCurrency::new(101, 0)); + } + #[test] fn market_state_mid_price() { let mut state = MarketState::::default();