pineforge-data 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pineforge_data/__init__.py +124 -0
- pineforge_data/backtest.py +552 -0
- pineforge_data/cli/__init__.py +1 -0
- pineforge_data/cli/backtest.py +354 -0
- pineforge_data/compile_cache.py +171 -0
- pineforge_data/docker_runtime.py +226 -0
- pineforge_data/engine.py +185 -0
- pineforge_data/errors.py +5 -0
- pineforge_data/models.py +199 -0
- pineforge_data/providers/__init__.py +69 -0
- pineforge_data/providers/base.py +51 -0
- pineforge_data/providers/ccxt.py +411 -0
- pineforge_data/providers/local.py +207 -0
- pineforge_data/providers/registry.py +252 -0
- pineforge_data/providers/sqlalchemy.py +138 -0
- pineforge_data/providers/tabular.py +530 -0
- pineforge_data/py.typed +1 -0
- pineforge_data/release_contract.py +153 -0
- pineforge_data/requests.py +87 -0
- pineforge_data/server.py +646 -0
- pineforge_data/server_client.py +142 -0
- pineforge_data-0.2.0.dist-info/METADATA +364 -0
- pineforge_data-0.2.0.dist-info/RECORD +26 -0
- pineforge_data-0.2.0.dist-info/WHEEL +4 -0
- pineforge_data-0.2.0.dist-info/entry_points.txt +3 -0
- pineforge_data-0.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Provider-neutral request and subscription objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
from .models import AssetClass, Instrument, MarketListing, MarketType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class MarketQuery:
|
|
12
|
+
"""Provider-neutral filters for catalog discovery."""
|
|
13
|
+
|
|
14
|
+
asset_class: AssetClass | None = None
|
|
15
|
+
market_types: frozenset[MarketType] = field(default_factory=frozenset)
|
|
16
|
+
base: str = ""
|
|
17
|
+
quote: str = ""
|
|
18
|
+
settle: str = ""
|
|
19
|
+
active: bool | None = None
|
|
20
|
+
margin_supported: bool | None = None
|
|
21
|
+
linear: bool | None = None
|
|
22
|
+
inverse: bool | None = None
|
|
23
|
+
|
|
24
|
+
def matches(self, listing: MarketListing) -> bool:
|
|
25
|
+
"""Return whether a normalized listing satisfies every supplied filter."""
|
|
26
|
+
|
|
27
|
+
instrument = listing.instrument
|
|
28
|
+
contract = instrument.contract
|
|
29
|
+
return (
|
|
30
|
+
(self.asset_class is None or instrument.asset_class is self.asset_class)
|
|
31
|
+
and (not self.market_types or instrument.market_type in self.market_types)
|
|
32
|
+
and (not self.base or instrument.base.casefold() == self.base.casefold())
|
|
33
|
+
and (not self.quote or instrument.quote.casefold() == self.quote.casefold())
|
|
34
|
+
and (not self.settle or instrument.settle.casefold() == self.settle.casefold())
|
|
35
|
+
and (self.active is None or listing.active is self.active)
|
|
36
|
+
and (self.margin_supported is None or listing.margin_supported is self.margin_supported)
|
|
37
|
+
and (self.linear is None or (contract is not None and contract.linear is self.linear))
|
|
38
|
+
and (
|
|
39
|
+
self.inverse is None or (contract is not None and contract.inverse is self.inverse)
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True, slots=True)
|
|
45
|
+
class BarRequest:
|
|
46
|
+
instrument: Instrument
|
|
47
|
+
timeframe: str
|
|
48
|
+
start_ms: int
|
|
49
|
+
end_ms: int
|
|
50
|
+
limit: int | None = None
|
|
51
|
+
|
|
52
|
+
def __post_init__(self) -> None:
|
|
53
|
+
if not self.timeframe.strip():
|
|
54
|
+
raise ValueError("timeframe must not be empty")
|
|
55
|
+
if self.start_ms < 0 or self.end_ms <= self.start_ms:
|
|
56
|
+
raise ValueError("bar request requires 0 <= start_ms < end_ms")
|
|
57
|
+
if self.limit is not None and self.limit <= 0:
|
|
58
|
+
raise ValueError("limit must be positive")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True, slots=True)
|
|
62
|
+
class TradeSubscription:
|
|
63
|
+
"""A live stream beginning at ``start_ms`` and after ``start_sequence``."""
|
|
64
|
+
|
|
65
|
+
instrument: Instrument
|
|
66
|
+
start_ms: int | None = None
|
|
67
|
+
start_sequence: int = 0
|
|
68
|
+
|
|
69
|
+
def __post_init__(self) -> None:
|
|
70
|
+
if self.start_ms is not None and self.start_ms < 0:
|
|
71
|
+
raise ValueError("start_ms must be non-negative")
|
|
72
|
+
if self.start_sequence < 0:
|
|
73
|
+
raise ValueError("start_sequence must be non-negative")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@dataclass(frozen=True, slots=True)
|
|
77
|
+
class MacroRequest:
|
|
78
|
+
key: str
|
|
79
|
+
currency: str
|
|
80
|
+
start_ms: int
|
|
81
|
+
end_ms: int
|
|
82
|
+
|
|
83
|
+
def __post_init__(self) -> None:
|
|
84
|
+
if not self.key.strip() or not self.currency.strip():
|
|
85
|
+
raise ValueError("key and currency must not be empty")
|
|
86
|
+
if self.start_ms < 0 or self.end_ms <= self.start_ms:
|
|
87
|
+
raise ValueError("macro request requires 0 <= start_ms < end_ms")
|