Compare commits

..

11 Commits

Author SHA1 Message Date
Tyler Goodlet fa7249eea2 binance: add `Pair.opoAllowed` field
Handle new API field per 2025-12-02 update.

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2025-12-18 18:14:54 -05:00
Tyler Goodlet 2b7e2754fd Add some Qt DPI extras to `qt_screen_info.py`
- set `QT_USE_PHYSICAL_DPI='1'` env var for Qt6 high-DPI
  * we likely want to do this in `piker.ui` as well!
- move `pxr` calc from widget to per-screen in loop.
- add `unscaled_size` calc using `pxr * size`.
- switch from `.availableGeometry()` to `.geometry()` for full
  bounds.
- shorten output labels, add `!r` repr formatting
- add Qt6 DPI rounding policy TODO with doc links

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
2025-12-18 18:12:04 -05:00
Tyler Goodlet 2b9eee179d Re-fmt and `.info()` the `.configure_to_dpi()` DPI calcs for now 2025-12-17 11:42:44 -05:00
Tyler Goodlet 74fcf8ec25 Reorder imports in `qt_screen_info.py` ??
For wtv reason on nixos importing `pyqtgraph` first is causing `numpy`
to fail import?? No idea, but likely something to do with recent
`flake.nix`'s ld-lib-linking with `<nixpkgs>` marlarky?
2025-12-16 18:22:05 -05:00
Tyler Goodlet b8fe866826 binance: handle new `TRADIFI_PERPETUAL`.. 2025-12-16 16:20:42 -05:00
Tyler Goodlet e0178f45aa Update `default.nix` (from @nt) for py313 2025-12-16 16:20:42 -05:00
Tyler Goodlet 5e550e18a9 nix: make Qt6 work on wayland
Taking many tips from our `default.nix` (thanks @nt!) this seems to be
the minimal overlay required for a flake to get up and running with
`piker chart` B)

Notes,
- for now, we're pinning to a major `cpython` version (3.13)
- ensure we (can) build with `nixpkgs.qt6.qtwayland`
- add the minimal Qt ld-lib-path linkings including those for plugin
  use (required for wayland mode).
- for now, hardcode "wayland" platform-mode and the linux standard
  "xdg-shell" integration.
- leave some TODOs to better parameterize around py versions.
2025-12-16 16:20:42 -05:00
Tyler Goodlet 944740799c Touch `conf.toml` by default when dne? 2025-12-16 16:20:42 -05:00
Tyler Goodlet 4e7fd0879c Bump `flake.lock`, seemly nicely minimized B) 2025-12-16 16:20:42 -05:00
Tyler Goodlet b2fe31bfc6 Redo `flake.nix` using `pyproject.nix` recos
Particularly using their recommended "impure template",
- https://pyproject-nix.github.io/pyproject.nix/templates.html#impure
- code: https://github.com/pyproject-nix/pyproject.nix/blob/master/templates/impure/flake.nix

Note the `shellHook` now contains various `uv`-specific osenv settings
and cmds to get a dev-env setup the way i would do it by default, that
includes all dev and extra (group) deps. For now i've hard coded the
"virt-env subdir" used by `uv` to match the cpython version. We can
obviously parameterize this much better in the future.

For those who want to spawn a diff shell then bash see the commented
line, for ex. i personally use `nix develop -c uv run xonsh`.
2025-12-16 16:20:42 -05:00
Tyler Goodlet d4fe6b7717 Don't pin `pendulum` version so we can use wheel
Bump version in lock file to match.
2025-12-16 16:20:42 -05:00
8 changed files with 117 additions and 305 deletions

View File

@ -11,11 +11,12 @@ let
libxkbcommonStorePath = lib.getLib libxkbcommon; libxkbcommonStorePath = lib.getLib libxkbcommon;
xcbutilcursorStorePath = lib.getLib xcb-util-cursor; xcbutilcursorStorePath = lib.getLib xcb-util-cursor;
qtpyStorePath = lib.getLib python312Packages.qtpy; pypkgs = python313Packages;
pyqt6StorePath = lib.getLib python312Packages.pyqt6; qtpyStorePath = lib.getLib pypkgs.qtpy;
pyqt6SipStorePath = lib.getLib python312Packages.pyqt6-sip; pyqt6StorePath = lib.getLib pypkgs.pyqt6;
rapidfuzzStorePath = lib.getLib python312Packages.rapidfuzz; pyqt6SipStorePath = lib.getLib pypkgs.pyqt6-sip;
qdarkstyleStorePath = lib.getLib python312Packages.qdarkstyle; rapidfuzzStorePath = lib.getLib pypkgs.rapidfuzz;
qdarkstyleStorePath = lib.getLib pypkgs.qdarkstyle;
xorgLibX11StorePath = lib.getLib xorg.libX11; xorgLibX11StorePath = lib.getLib xorg.libX11;
xorgLibxcbStorePath = lib.getLib xorg.libxcb; xorgLibxcbStorePath = lib.getLib xorg.libxcb;
@ -51,12 +52,12 @@ stdenv.mkDerivation {
xorg.xcbutilrenderutil xorg.xcbutilrenderutil
# Python requirements. # Python requirements.
python312Full python313
python312Packages.uv uv
python312Packages.qdarkstyle pypkgs.qdarkstyle
python312Packages.rapidfuzz pypkgs.rapidfuzz
python312Packages.pyqt6 pypkgs.pyqt6
python312Packages.qtpy pypkgs.qtpy
]; ];
src = null; src = null;
shellHook = '' shellHook = ''
@ -113,11 +114,11 @@ stdenv.mkDerivation {
export LD_LIBRARY_PATH export LD_LIBRARY_PATH
RPDFUZZ_PATH="${rapidfuzzStorePath}/lib/python3.12/site-packages" RPDFUZZ_PATH="${rapidfuzzStorePath}/lib/python3.13/site-packages"
QDRKSTYLE_PATH="${qdarkstyleStorePath}/lib/python3.12/site-packages" QDRKSTYLE_PATH="${qdarkstyleStorePath}/lib/python3.13/site-packages"
QTPY_PATH="${qtpyStorePath}/lib/python3.12/site-packages" QTPY_PATH="${qtpyStorePath}/lib/python3.13/site-packages"
PYQT6_PATH="${pyqt6StorePath}/lib/python3.12/site-packages" PYQT6_PATH="${pyqt6StorePath}/lib/python3.13/site-packages"
PYQT6_SIP_PATH="${pyqt6SipStorePath}/lib/python3.12/site-packages" PYQT6_SIP_PATH="${pyqt6SipStorePath}/lib/python3.13/site-packages"
PATCH="$PATCH:$RPDFUZZ_PATH" PATCH="$PATCH:$RPDFUZZ_PATH"
PATCH="$PATCH:$QDRKSTYLE_PATH" PATCH="$PATCH:$QDRKSTYLE_PATH"
@ -127,8 +128,8 @@ stdenv.mkDerivation {
export PATCH export PATCH
# Install deps # install all dev and extras
uv lock uv sync --dev --all-extras
''; '';
} }

View File

@ -1,135 +1,24 @@
{ {
"nodes": { "nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1692174805, "lastModified": 1765779637,
"narHash": "sha256-xmNPFDi/AUMIxwgOH/IVom55Dks34u1g7sFKKebxUm0=", "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=",
"owner": "NixOS", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "caac0eb6bdcad0b32cb2522e03e4002c8975c62e", "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "nixos",
"ref": "nixos-unstable", "ref": "nixos-unstable",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1692048894,
"narHash": "sha256-cDw03rso2V4CDc3Mll0cHN+ztzysAvdI8pJ7ybbz714=",
"ref": "refs/heads/pyqt6",
"rev": "b059ad4c3051f45d6c912e17747aae37a9ec1544",
"revCount": 2276,
"type": "git",
"url": "file:///home/lord_fomo/repos/poetry2nix"
},
"original": {
"type": "git",
"url": "file:///home/lord_fomo/repos/poetry2nix"
}
},
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
} }
} }
}, },

247
flake.nix
View File

@ -1,180 +1,95 @@
# NOTE: to convert to a poetry2nix env like this here are the # An "impure" template thx to `pyproject.nix`,
# steps: # https://pyproject-nix.github.io/pyproject.nix/templates.html#impure
# - install poetry in your system nix config # https://github.com/pyproject-nix/pyproject.nix/blob/master/templates/impure/flake.nix
# - convert the repo to use poetry using `poetry init`:
# https://python-poetry.org/docs/basic-usage/#initialising-a-pre-existing-project
# - then manually ensuring all deps are converted over:
# - add this file to the repo and commit it
# -
# GROKin tips:
# - CLI eps are (ostensibly) added via an `entry_points.txt`:
# - https://packaging.python.org/en/latest/specifications/entry-points/#file-format
# - https://github.com/nix-community/poetry2nix/blob/master/editable.nix#L49
{ {
description = "piker: trading gear for hackers (pkged with poetry2nix)"; description = "An impure `piker` overlay using `uv` with Nix(OS)";
inputs.flake-utils.url = "github:numtide/flake-utils"; inputs = {
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
# see https://github.com/nix-community/poetry2nix/tree/master#api
inputs.poetry2nix = {
# url = "github:nix-community/poetry2nix";
# url = "github:K900/poetry2nix/qt5-explicit-deps";
url = "/home/lord_fomo/repos/poetry2nix";
inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = { outputs =
self, { nixpkgs, ... }:
nixpkgs,
flake-utils,
poetry2nix,
}:
# TODO: build cross-OS and use the `${system}` var thingy..
flake-utils.lib.eachDefaultSystem (system:
let let
# use PWD as sources inherit (nixpkgs) lib;
projectDir = ./.; forAllSystems = lib.genAttrs lib.systems.flakeExposed;
pyproject = ./pyproject.toml;
poetrylock = ./poetry.lock;
# TODO: port to 3.11 and support both versions?
python = "python3.10";
# for more functions and examples.
# inherit
# (poetry2nix.legacyPackages.${system})
# mkPoetryApplication;
# pkgs = nixpkgs.legacyPackages.${system};
pkgs = nixpkgs.legacyPackages.x86_64-linux;
lib = pkgs.lib;
p2npkgs = poetry2nix.legacyPackages.x86_64-linux;
# define all pkg overrides per dep, see edgecases.md:
# https://github.com/nix-community/poetry2nix/blob/master/docs/edgecases.md
# TODO: add these into the json file:
# https://github.com/nix-community/poetry2nix/blob/master/overrides/build-systems.json
pypkgs-build-requirements = {
asyncvnc = [ "setuptools" ];
eventkit = [ "setuptools" ];
ib-insync = [ "setuptools" "flake8" ];
msgspec = [ "setuptools"];
pdbp = [ "setuptools" ];
pyqt6-sip = [ "setuptools" ];
tabcompleter = [ "setuptools" ];
tractor = [ "setuptools" ];
tricycle = [ "setuptools" ];
trio-typing = [ "setuptools" ];
trio-util = [ "setuptools" ];
xonsh = [ "setuptools" ];
};
# auto-generate override entries
p2n-overrides = p2npkgs.defaultPoetryOverrides.extend (self: super:
builtins.mapAttrs (package: build-requirements:
(builtins.getAttr package super).overridePythonAttrs (old: {
buildInputs = (
old.buildInputs or [ ]
) ++ (
builtins.map (
pkg: if builtins.isString pkg then builtins.getAttr pkg super else pkg
) build-requirements
);
})
) pypkgs-build-requirements
);
# override some ahead-of-time compiled extensions
# to be built with their wheels.
ahot_overrides = p2n-overrides.extend(
final: prev: {
# llvmlite = prev.llvmlite.override {
# preferWheel = false;
# };
# TODO: get this workin with p2n and nixpkgs..
# pyqt6 = prev.pyqt6.override {
# preferWheel = true;
# };
# NOTE: this DOESN'T work atm but after a fix
# to poetry2nix, it will and actually this line
# won't be needed - thanks @k900:
# https://github.com/nix-community/poetry2nix/pull/1257
pyqt5 = prev.pyqt5.override {
# withWebkit = false;
preferWheel = true;
};
# see PR from @k900:
# https://github.com/nix-community/poetry2nix/pull/1257
# pyqt5-qt5 = prev.pyqt5-qt5.override {
# withWebkit = false;
# preferWheel = true;
# };
# TODO: patch in an override for polars to build
# from src! See the details likely needed from
# the cryptography entry:
# https://github.com/nix-community/poetry2nix/blob/master/overrides/default.nix#L426-L435
polars = prev.polars.override {
preferWheel = true;
};
}
);
# WHY!? -> output-attrs that `nix develop` scans for:
# https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-develop.html#flake-output-attributes
in in
rec { {
packages = { devShells = forAllSystems (
# piker = poetry2nix.legacyPackages.x86_64-linux.mkPoetryEditablePackage { system:
# editablePackageSources = { piker = ./piker; }; let
pkgs = nixpkgs.legacyPackages.${system};
piker = p2npkgs.mkPoetryApplication { # do store-path extractions
projectDir = projectDir; qt6baseStorePath = lib.getLib pkgs.qt6.qtbase;
# ?TODO? can remove below since manual linking not needed?
# qt6QtWaylandStorePath = lib.getLib pkgs.qt6.qtwayland;
# SEE ABOVE for auto-genned input set, override # XXX NOTE XXX, for now we overlay specific pkgs via
# buncha deps with extras.. like `setuptools` mostly. # a major-version-pinned-`cpython`
# TODO: maybe propose a patch to p2n to show that you cpython = "python313";
# can even do this in the edgecases docs? pypkgs = pkgs."${cpython}Packages";
overrides = ahot_overrides; in
{
default = pkgs.mkShell {
# XXX: won't work on llvmlite.. packages = with pkgs; [
# preferWheels = true; qt6.qtwayland
}; qt6.qtbase
};
# devShells.default = pkgs.mkShell { uv
# projectDir = projectDir; python313 # ?TODO^ how to set from `cpython` above?
# python = "python3.10"; pypkgs.pyqt6
# overrides = ahot_overrides; pypkgs.pyqt6-sip
# inputsFrom = [ self.packages.x86_64-linux.piker ]; pypkgs.qtpy
# packages = packages; pypkgs.qdarkstyle
# # packages = [ poetry2nix.packages.${system}.poetry ]; pypkgs.rapidfuzz
# }; ];
# TODO: grok the difference here.. shellHook = ''
# - avoid re-cloning git repos on every develop entry.. # unmask to debug **this** dev-shell-hook
# - ideally allow hacking on the src code of some deps # set -e
# (tractor, pyqtgraph, tomlkit, etc.) WITHOUT having to
# re-install them every time a change is made. # set qt-base/plugin path(s)
# - boot a usable xonsh inside the poetry virtualenv when QTBASE_PATH="${qt6baseStorePath}/lib"
# defined via a custom entry point? QT_PLUGIN_PATH="${qt6baseStorePath}/lib/qt-6/plugins"
devShells.default = p2npkgs.mkPoetryEnv { QT_QPA_PLATFORM_PLUGIN_PATH="$QT_PLUGIN_PATH/platforms"
# env = p2npkgs.mkPoetryEnv {
projectDir = projectDir; # link in Qt cc lib paths from <nixpkgs>
python = pkgs.python310; LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$QTBASE_PATH"
overrides = ahot_overrides; LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$QT_PLUGIN_PATH"
editablePackageSources = packages; LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$QT_QPA_PLATFORM_PLUGIN_PATH"
# piker = "./";
# tractor = "../tractor/"; # link-in c++ stdlib for various AOT-ext-pkgs (numpy, etc.)
# }; # wut? LD_LIBRARY_PATH="${pkgs.stdenv.cc.cc.lib}/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
# RUNTIME-SETTINGS
#
# ------ Qt ------
# XXX, unmask to debug qt .so linking/loading deats
# export QT_DEBUG_PLUGINS=1
#
# ALSO, for *modern linux* DEs,
# - maybe set wayland-mode (TODO, parametrtize this!)
# * a chosen wayland-mode shell-integration
export QT_QPA_PLATFORM="wayland"
export QT_WAYLAND_SHELL_INTEGRATION="xdg-shell"
# ------ uv ------
# - always use the ./py313/ venv-subdir
export UV_PROJECT_ENVIRONMENT="py313"
# sync project-env with all extras
uv sync --dev --all-extras
# ------ TIPS ------
# NOTE, to launch the py-venv installed `xonsh` (like @goodboy)
# run the `nix develop` cmd with,
# >> nix develop -c uv run xonsh
'';
}; };
} }
); # end of .outputs scope );
};
} }

View File

@ -223,7 +223,10 @@ class FutesPair(Pair):
assert pair == self.pair # sanity assert pair == self.pair # sanity
return f'{expiry}' return f'{expiry}'
case 'PERPETUAL': case (
'PERPETUAL'
| 'TRADIFI_PERPETUAL'
):
return 'PERP' return 'PERP'
case '': case '':
@ -252,7 +255,10 @@ class FutesPair(Pair):
margin: str = self.marginAsset margin: str = self.marginAsset
match ctype: match ctype:
case 'PERPETUAL': case (
'PERPETUAL'
| 'TRADIFI_PERPETUAL'
):
return f'{margin}M' return f'{margin}M'
case ( case (

View File

@ -261,7 +261,7 @@ def load(
MutableMapping, MutableMapping,
] = tomllib.loads, ] = tomllib.loads,
touch_if_dne: bool = False, touch_if_dne: bool = True,
**tomlkws, **tomlkws,
@ -270,7 +270,7 @@ def load(
Load config file by name. Load config file by name.
If desired config is not in the top level piker-user config path then If desired config is not in the top level piker-user config path then
pass the ``path: Path`` explicitly. pass the `path: Path` explicitly.
''' '''
# create the $HOME/.config/piker dir if dne # create the $HOME/.config/piker dir if dne
@ -285,7 +285,8 @@ def load(
if ( if (
not path.is_file() not path.is_file()
and touch_if_dne and
touch_if_dne
): ):
# only do a template if no path provided, # only do a template if no path provided,
# just touch an empty file with same name. # just touch an empty file with same name.

View File

@ -66,7 +66,7 @@ dependencies = [
"rapidfuzz >=3.5.2, <4.0.0", "rapidfuzz >=3.5.2, <4.0.0",
"pdbp >=1.5.0, <2.0.0", "pdbp >=1.5.0, <2.0.0",
"trio >=0.27", "trio >=0.27",
"pendulum >=3.0.0, <4.0.0", "pendulum",
"httpx >=0.27.0, <0.28.0", "httpx >=0.27.0, <0.28.0",
"cryptofeed >=2.4.0, <3.0.0", "cryptofeed >=2.4.0, <3.0.0",
"pyarrow>=18.0.0", "pyarrow>=18.0.0",

View File

@ -33,7 +33,6 @@ Resource list for mucking with DPIs on multiple screens:
''' '''
import os import os
from pyqtgraph import QtGui
from PyQt6 import ( from PyQt6 import (
QtCore, QtCore,
QtWidgets, QtWidgets,
@ -44,6 +43,7 @@ from PyQt6.QtCore import (
QSize, QSize,
QRect, QRect,
) )
from pyqtgraph import QtGui
# https://doc.qt.io/qt-6/highdpi.html#environment-variable-reference # https://doc.qt.io/qt-6/highdpi.html#environment-variable-reference

View File

@ -1072,7 +1072,7 @@ requires-dist = [
{ name = "numba", specifier = ">=0.61.0" }, { name = "numba", specifier = ">=0.61.0" },
{ name = "numpy", specifier = ">=2.0" }, { name = "numpy", specifier = ">=2.0" },
{ name = "pdbp", specifier = ">=1.5.0,<2.0.0" }, { name = "pdbp", specifier = ">=1.5.0,<2.0.0" },
{ name = "pendulum", specifier = ">=3.0.0,<4.0.0" }, { name = "pendulum" },
{ name = "polars", specifier = ">=0.20.6" }, { name = "polars", specifier = ">=0.20.6" },
{ name = "polars-fuzzy-match", specifier = ">=0.1.5" }, { name = "polars-fuzzy-match", specifier = ">=0.1.5" },
{ name = "pyarrow", specifier = ">=18.0.0" }, { name = "pyarrow", specifier = ">=18.0.0" },