tradepose-client 0.1.1__py3-none-any.whl → 0.1.2__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.

Potentially problematic release.


This version of tradepose-client might be problematic. Click here for more details.

@@ -33,9 +33,11 @@ class TradingContextProxy:
33
33
  context_type: 上下文类型("base" 或 "advanced")
34
34
  """
35
35
 
36
- def __init__(self, context_name: str, context_type: str):
36
+ def __init__(
37
+ self,
38
+ context_name: str,
39
+ ):
37
40
  self._context_name = context_name
38
- self._context_type = context_type
39
41
 
40
42
  @property
41
43
  def entry_price(self) -> pl.Expr:
@@ -59,32 +61,8 @@ class TradingContextProxy:
59
61
  - base_trading_context 使用 "bars_in_position"
60
62
  - advanced 使用 "bars_since_advanced_entry"(请用 .bars_since_entry)
61
63
  """
62
- if self._context_type != "base":
63
- raise AttributeError(
64
- f"'bars_in_position' only available for base context. "
65
- f"Use '.bars_since_entry' for advanced contexts."
66
- )
67
64
  return pl.col(self._context_name).struct.field("bars_in_position")
68
65
 
69
- @property
70
- def bars_since_entry(self) -> pl.Expr:
71
- """
72
- 进场后 K 线数(仅适用于 advanced contexts)
73
-
74
- Returns:
75
- pl.Expr: Polars 表达式
76
-
77
- Note:
78
- - advanced_entry/exit_trading_context 使用 "bars_since_advanced_entry"
79
- - base 使用 "bars_in_position"(请用 .bars_in_position)
80
- """
81
- if self._context_type == "base":
82
- raise AttributeError(
83
- f"'bars_since_entry' not available for base context. "
84
- f"Use '.bars_in_position' for base context."
85
- )
86
- return pl.col(self._context_name).struct.field("bars_since_advanced_entry")
87
-
88
66
  @property
89
67
  def highest_since_entry(self) -> pl.Expr:
90
68
  """
@@ -151,9 +129,9 @@ class TradingContext:
151
129
  timeout_condition = TradingContext.advanced_entry.bars_since_entry > 100
152
130
  """
153
131
 
154
- base = TradingContextProxy("base_trading_context", "base")
155
- advanced_entry = TradingContextProxy("advanced_entry_trading_context", "advanced")
156
- advanced_exit = TradingContextProxy("advanced_exit_trading_context", "advanced")
132
+ base = TradingContextProxy("base_trading_context")
133
+ advanced_entry = TradingContextProxy("advanced_entry_trading_context")
134
+ advanced_exit = TradingContextProxy("advanced_exit_trading_context")
157
135
 
158
136
 
159
137
  # 向后兼容的别名
@@ -0,0 +1,300 @@
1
+ """
2
+ Indicator Pydantic Models for Strong Typing
3
+
4
+ 为每个指标类型创建强类型的 Pydantic 模型,提供:
5
+ - 编译时类型检查
6
+ - IDE 自动补全
7
+ - 参数验证
8
+ - 与 Rust backend 对齐的序列化
9
+
10
+ All models are aligned with Rust Indicator enum variants.
11
+ """
12
+
13
+ from typing import Any, Dict, List, Literal, Optional
14
+ from pydantic import BaseModel, Field
15
+
16
+
17
+ # ============================================================================
18
+ # 移动平均类指标(Moving Average Indicators)
19
+ # ============================================================================
20
+
21
+
22
+ class SMAIndicator(BaseModel):
23
+ """Simple Moving Average (SMA) 指标
24
+
25
+ 对应 Rust: Indicator::SMA { period, column }
26
+
27
+ Args:
28
+ period: 计算周期(必须 > 0)
29
+ column: 计算列名(默认 "close")
30
+
31
+ Example:
32
+ >>> sma = SMAIndicator(period=20)
33
+ >>> sma = SMAIndicator(period=50, column="high")
34
+ """
35
+ type: Literal["SMA"] = "SMA"
36
+ period: int = Field(gt=0, le=500, description="Period must be > 0 and <= 500")
37
+ column: str = Field(
38
+ default="close",
39
+ pattern="^(open|high|low|close|volume)$",
40
+ description="OHLCV column name"
41
+ )
42
+
43
+
44
+ class EMAIndicator(BaseModel):
45
+ """Exponential Moving Average (EMA) 指标
46
+
47
+ 对应 Rust: Indicator::EMA { period, column }
48
+ """
49
+ type: Literal["EMA"] = "EMA"
50
+ period: int = Field(gt=0, le=500)
51
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
52
+
53
+
54
+ class SMMAIndicator(BaseModel):
55
+ """Smoothed Moving Average (SMMA) 指标
56
+
57
+ 对应 Rust: Indicator::SMMA { period, column }
58
+ """
59
+ type: Literal["SMMA"] = "SMMA"
60
+ period: int = Field(gt=0, le=500)
61
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
62
+
63
+
64
+ class WMAIndicator(BaseModel):
65
+ """Weighted Moving Average (WMA) 指标
66
+
67
+ 对应 Rust: Indicator::WMA { period, column }
68
+ """
69
+ type: Literal["WMA"] = "WMA"
70
+ period: int = Field(gt=0, le=500)
71
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
72
+
73
+
74
+ # ============================================================================
75
+ # 波动率类指标(Volatility Indicators)
76
+ # ============================================================================
77
+
78
+
79
+ class ATRIndicator(BaseModel):
80
+ """Average True Range (ATR) 指标
81
+
82
+ 对应 Rust: Indicator::ATR { period }
83
+
84
+ Returns:
85
+ 单一数值字段(不是 struct)
86
+
87
+ Example:
88
+ >>> atr = ATRIndicator(period=14)
89
+ >>> atr = ATRIndicator(period=21)
90
+ """
91
+ type: Literal["ATR"] = "ATR"
92
+ period: int = Field(gt=0, le=200, description="Period must be > 0 and <= 200")
93
+
94
+
95
+ class ATRQuantileIndicator(BaseModel):
96
+ """ATR Rolling Quantile 指标
97
+
98
+ 对应 Rust: Indicator::AtrQuantile { atr_column, window, quantile }
99
+
100
+ 计算 ATR 的滚动分位数,用于动态止损等场景。
101
+
102
+ 注意:这是依赖指标,必须先定义 ATR 指标。
103
+
104
+ Args:
105
+ atr_column: 引用的 ATR 列名(如 "ATR|14")
106
+ window: 滚动窗口大小
107
+ quantile: 分位数值 (0, 1),0.5 表示中位数
108
+
109
+ Returns:
110
+ 单一数值字段(不是 struct)
111
+
112
+ Example:
113
+ >>> atr_q = ATRQuantileIndicator(
114
+ ... atr_column="ATR|21",
115
+ ... window=40,
116
+ ... quantile=0.75 # 75th percentile
117
+ ... )
118
+ """
119
+ type: Literal["AtrQuantile"] = "AtrQuantile"
120
+ atr_column: str = Field(min_length=1, description="Reference to existing ATR column")
121
+ window: int = Field(gt=0, le=500, description="Rolling window size")
122
+ quantile: float = Field(gt=0, lt=1, description="Quantile value in (0, 1)")
123
+
124
+
125
+ # ============================================================================
126
+ # 趋势类指标(Trend Indicators)
127
+ # ============================================================================
128
+
129
+
130
+ class SuperTrendIndicator(BaseModel):
131
+ """SuperTrend 指标
132
+
133
+ 对应 Rust: Indicator::SuperTrend { multiplier, volatility_column, high, low, close }
134
+
135
+ Args:
136
+ multiplier: ATR 倍数(通常 2.0-3.0)
137
+ volatility_column: 引用的波动率列名(如 "ATR|14",注意是列名不是 struct 字段)
138
+ high: 高价列名(默认 "high")
139
+ low: 低价列名(默认 "low")
140
+ close: 收盘价列名(默认 "close")
141
+
142
+ Returns:
143
+ Struct { direction: i32, value: f64, trend: f64, ...}
144
+
145
+ Example:
146
+ >>> st = SuperTrendIndicator(
147
+ ... multiplier=3.0,
148
+ ... volatility_column="ATR|21"
149
+ ... )
150
+ """
151
+ type: Literal["SuperTrend"] = "SuperTrend"
152
+ multiplier: float = Field(gt=0, le=10, description="ATR multiplier (typically 2.0-3.0)")
153
+ volatility_column: str = Field(
154
+ min_length=1,
155
+ description="Reference to volatility column (e.g., 'ATR|14')"
156
+ )
157
+ high: str = Field(default="high", pattern="^(high|open|low|close)$")
158
+ low: str = Field(default="low", pattern="^(high|open|low|close)$")
159
+ close: str = Field(default="close", pattern="^(high|open|low|close)$")
160
+
161
+
162
+ class MACDIndicator(BaseModel):
163
+ """Moving Average Convergence Divergence (MACD) 指标
164
+
165
+ 对应 Rust: Indicator::MACD { fast_period, slow_period, signal_period, column, fields }
166
+
167
+ Returns:
168
+ Struct { macd: f64, signal: f64, histogram: f64 }
169
+ """
170
+ type: Literal["MACD"] = "MACD"
171
+ fast_period: int = Field(default=12, gt=0, le=100)
172
+ slow_period: int = Field(default=26, gt=0, le=200)
173
+ signal_period: int = Field(default=9, gt=0, le=50)
174
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
175
+ fields: Optional[List[str]] = Field(
176
+ default=None,
177
+ description="Select specific fields: ['macd', 'signal', 'histogram']"
178
+ )
179
+
180
+
181
+ class ADXIndicator(BaseModel):
182
+ """Average Directional Index (ADX) 指标
183
+
184
+ 对应 Rust: Indicator::ADX { period, fields }
185
+
186
+ Returns:
187
+ Struct { adx: f64, plus_di: f64, minus_di: f64 }
188
+ """
189
+ type: Literal["ADX"] = "ADX"
190
+ period: int = Field(default=14, gt=0, le=100)
191
+ fields: Optional[List[str]] = Field(
192
+ default=None,
193
+ description="Select specific fields: ['adx', 'plus_di', 'minus_di']"
194
+ )
195
+
196
+
197
+ # ============================================================================
198
+ # 动量类指标(Momentum Indicators)
199
+ # ============================================================================
200
+
201
+
202
+ class RSIIndicator(BaseModel):
203
+ """Relative Strength Index (RSI) 指标
204
+
205
+ 对应 Rust: Indicator::RSI { period, column }
206
+
207
+ Returns:
208
+ 单一数值字段(0-100 范围)
209
+ """
210
+ type: Literal["RSI"] = "RSI"
211
+ period: int = Field(default=14, gt=0, le=100)
212
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
213
+
214
+
215
+ class CCIIndicator(BaseModel):
216
+ """Commodity Channel Index (CCI) 指标
217
+
218
+ 对应 Rust: Indicator::CCI { period }
219
+
220
+ Returns:
221
+ 单一数值字段
222
+ """
223
+ type: Literal["CCI"] = "CCI"
224
+ period: int = Field(default=20, gt=0, le=100)
225
+
226
+
227
+ class StochasticIndicator(BaseModel):
228
+ """Stochastic Oscillator 指标
229
+
230
+ 对应 Rust: Indicator::Stochastic { k_period, d_period, fields }
231
+
232
+ Returns:
233
+ Struct { k: f64, d: f64 }
234
+ """
235
+ type: Literal["Stochastic"] = "Stochastic"
236
+ k_period: int = Field(default=14, gt=0, le=100, description="%K period")
237
+ d_period: int = Field(default=3, gt=0, le=50, description="%D period (smoothing)")
238
+ fields: Optional[List[str]] = Field(
239
+ default=None,
240
+ description="Select specific fields: ['k', 'd']"
241
+ )
242
+
243
+
244
+ # ============================================================================
245
+ # 其他指标(Other Indicators)
246
+ # ============================================================================
247
+
248
+
249
+ class BollingerBandsIndicator(BaseModel):
250
+ """Bollinger Bands 指标
251
+
252
+ 对应 Rust: Indicator::BollingerBands { period, num_std, column, fields }
253
+
254
+ Returns:
255
+ Struct { upper: f64, middle: f64, lower: f64, bandwidth: f64 }
256
+ """
257
+ type: Literal["BollingerBands"] = "BollingerBands"
258
+ period: int = Field(default=20, gt=0, le=200)
259
+ num_std: float = Field(default=2.0, gt=0, le=5.0, description="Standard deviation multiplier")
260
+ column: str = Field(default="close", pattern="^(open|high|low|close|volume)$")
261
+ fields: Optional[List[str]] = Field(
262
+ default=None,
263
+ description="Select specific fields: ['upper', 'middle', 'lower', 'bandwidth']"
264
+ )
265
+
266
+
267
+ class MarketProfileIndicator(BaseModel):
268
+ """Market Profile 指标
269
+
270
+ 对应 Rust: Indicator::MarketProfile { anchor_config, tick_size, value_area_pct, fields }
271
+
272
+ Returns:
273
+ Struct { poc: f64, vah: f64, val: f64, value_area: List[f64], total_tpo: i32, ...}
274
+ """
275
+ type: Literal["MarketProfile"] = "MarketProfile"
276
+ anchor_config: Dict[str, Any] = Field(
277
+ ...,
278
+ description="Anchor configuration (use create_daily_anchor or create_weekly_anchor)"
279
+ )
280
+ tick_size: float = Field(gt=0, description="Tick size for price levels")
281
+ value_area_pct: float = Field(default=0.7, gt=0, lt=1, description="Value area percentage")
282
+ fields: Optional[List[str]] = Field(
283
+ default=None,
284
+ description="Select specific fields: ['poc', 'vah', 'val', 'value_area', 'total_tpo']"
285
+ )
286
+ shape_config: Optional[Dict[str, Any]] = Field(
287
+ default=None,
288
+ description="Profile shape recognition config (advanced feature)"
289
+ )
290
+
291
+
292
+ class RawOhlcvIndicator(BaseModel):
293
+ """Raw OHLCV Column 指标
294
+
295
+ 对应 Rust: Indicator::RawOhlcv { column }
296
+
297
+ 直接引用 OHLCV 列,用于需要原始数据的场景。
298
+ """
299
+ type: Literal["RawOhlcv"] = "RawOhlcv"
300
+ column: str = Field(pattern="^(open|high|low|close|volume)$")
@@ -9,14 +9,87 @@ Pydantic 模型用於策略配置 (V2 - 自動轉換版本)
9
9
  import json
10
10
  from enum import Enum
11
11
  from io import StringIO
12
- from typing import Any, Dict, List, Optional, Union
12
+ from typing import Annotated, Any, Dict, List, Optional, Union
13
13
 
14
14
  import polars as pl
15
- from pydantic import BaseModel, Field, field_serializer, field_validator
15
+ from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator
16
16
 
17
17
  # Import enums from enums.py
18
18
  from .enums import Freq, OrderStrategy, Weekday, TradeDirection, TrendType
19
19
 
20
+ # Import Indicator models
21
+ from .indicator_models import (
22
+ SMAIndicator,
23
+ EMAIndicator,
24
+ SMMAIndicator,
25
+ WMAIndicator,
26
+ ATRIndicator,
27
+ ATRQuantileIndicator,
28
+ SuperTrendIndicator,
29
+ MACDIndicator,
30
+ ADXIndicator,
31
+ RSIIndicator,
32
+ CCIIndicator,
33
+ StochasticIndicator,
34
+ BollingerBandsIndicator,
35
+ MarketProfileIndicator,
36
+ RawOhlcvIndicator,
37
+ )
38
+
39
+
40
+ # ============================================================================
41
+ # Indicator Discriminated Union (强类型 Indicator 配置)
42
+ # ============================================================================
43
+
44
+ IndicatorConfig = Annotated[
45
+ Union[
46
+ # 移动平均类
47
+ SMAIndicator,
48
+ EMAIndicator,
49
+ SMMAIndicator,
50
+ WMAIndicator,
51
+ # 波动率类
52
+ ATRIndicator,
53
+ ATRQuantileIndicator,
54
+ # 趋势类
55
+ SuperTrendIndicator,
56
+ MACDIndicator,
57
+ ADXIndicator,
58
+ # 动量类
59
+ RSIIndicator,
60
+ CCIIndicator,
61
+ StochasticIndicator,
62
+ # 其他
63
+ BollingerBandsIndicator,
64
+ MarketProfileIndicator,
65
+ RawOhlcvIndicator,
66
+ ],
67
+ Field(discriminator='type')
68
+ ]
69
+ """
70
+ 强类型 Indicator 配置
71
+
72
+ 使用 Pydantic discriminated union,根据 'type' 字段自动选择正确的模型:
73
+ - type="SMA" → SMAIndicator
74
+ - type="ATR" → ATRIndicator
75
+ - type="SuperTrend" → SuperTrendIndicator
76
+ - ... etc
77
+
78
+ 优点:
79
+ - 编译时类型检查
80
+ - IDE 自动补全
81
+ - 参数验证
82
+ - 自动 JSON 序列化/反序列化
83
+
84
+ Example:
85
+ >>> # 方式 1: 直接创建强类型模型
86
+ >>> atr = ATRIndicator(period=21)
87
+ >>>
88
+ >>> # 方式 2: 从 Dict 自动转换(API 兼容)
89
+ >>> indicator: IndicatorConfig = {"type": "ATR", "period": 21}
90
+ >>> # Pydantic 自动识别为 ATRIndicator
91
+ """
92
+
20
93
 
21
94
  class PolarsExprField:
22
95
  """自定義 Polars Expr 字段處理"""
@@ -165,8 +238,8 @@ class Indicator:
165
238
  """
166
239
 
167
240
  @staticmethod
168
- def sma(period: int, column: str = "close") -> Dict[str, Any]:
169
- """創建 SMA 指標配置
241
+ def sma(period: int, column: str = "close") -> SMAIndicator:
242
+ """創建 SMA 指標配置(强类型)
170
243
 
171
244
  對應 Rust: Indicator::SMA { period, column }
172
245
 
@@ -175,72 +248,42 @@ class Indicator:
175
248
  column: 計算欄位(預設 "close")
176
249
 
177
250
  Returns:
178
- SMA 指標配置 dict
251
+ SMAIndicator 強類型模型
179
252
 
180
253
  Example:
181
- >>> Indicator.sma(period=20)
182
- >>> Indicator.sma(period=20, column="high")
254
+ >>> atr = Indicator.sma(period=20)
255
+ >>> assert isinstance(atr, SMAIndicator)
256
+ >>> atr_high = Indicator.sma(period=20, column="high")
183
257
  """
184
- return {"type": "SMA", "period": period, "column": column}
258
+ return SMAIndicator(period=period, column=column)
185
259
 
186
260
  @staticmethod
187
- def ema(period: int, column: str = "close") -> Dict[str, Any]:
188
- """創建 EMA 指標配置
261
+ def ema(period: int, column: str = "close") -> EMAIndicator:
262
+ """創建 EMA 指標配置(强类型)
189
263
 
190
264
  對應 Rust: Indicator::EMA { period, column }
191
-
192
- Args:
193
- period: 週期
194
- column: 計算欄位(預設 "close")
195
-
196
- Returns:
197
- EMA 指標配置 dict
198
-
199
- Example:
200
- >>> Indicator.ema(period=12)
201
- >>> Indicator.ema(period=12, column="low")
202
265
  """
203
- return {"type": "EMA", "period": period, "column": column}
266
+ return EMAIndicator(period=period, column=column)
204
267
 
205
268
  @staticmethod
206
- def smma(period: int, column: str = "close") -> Dict[str, Any]:
207
- """創建 SMMA 指標配置
269
+ def smma(period: int, column: str = "close") -> SMMAIndicator:
270
+ """創建 SMMA 指標配置(强类型)
208
271
 
209
272
  對應 Rust: Indicator::SMMA { period, column }
210
-
211
- Args:
212
- period: 週期
213
- column: 計算欄位(預設 "close")
214
-
215
- Returns:
216
- SMMA 指標配置 dict
217
-
218
- Example:
219
- >>> Indicator.smma(period=14)
220
273
  """
221
- return {"type": "SMMA", "period": period, "column": column}
274
+ return SMMAIndicator(period=period, column=column)
222
275
 
223
276
  @staticmethod
224
- def wma(period: int, column: str = "close") -> Dict[str, Any]:
225
- """創建 WMA 指標配置
277
+ def wma(period: int, column: str = "close") -> WMAIndicator:
278
+ """創建 WMA 指標配置(强类型)
226
279
 
227
280
  對應 Rust: Indicator::WMA { period, column }
228
-
229
- Args:
230
- period: 週期
231
- column: 計算欄位(預設 "close")
232
-
233
- Returns:
234
- WMA 指標配置 dict
235
-
236
- Example:
237
- >>> Indicator.wma(period=10)
238
281
  """
239
- return {"type": "WMA", "period": period, "column": column}
282
+ return WMAIndicator(period=period, column=column)
240
283
 
241
284
  @staticmethod
242
- def atr(period: int) -> Dict[str, Any]:
243
- """創建 ATR 指標配置
285
+ def atr(period: int) -> ATRIndicator:
286
+ """創建 ATR 指標配置(强类型)
244
287
 
245
288
  對應 Rust: Indicator::ATR { period }
246
289
 
@@ -270,15 +313,15 @@ class Indicator:
270
313
  ... (pl.col("close") - 2 * pl.col("ATR|14")).alias("stop_loss")
271
314
  ... ])
272
315
  """
273
- return {"type": "ATR", "period": period}
316
+ return ATRIndicator(period=period)
274
317
 
275
318
  @staticmethod
276
319
  def atr_quantile(
277
320
  atr_column: str,
278
321
  window: int,
279
322
  quantile: float,
280
- ) -> Dict[str, Any]:
281
- """創建 AtrQuantile 指標配置
323
+ ) -> ATRQuantileIndicator:
324
+ """創建 AtrQuantile 指標配置(强类型)
282
325
 
283
326
  對應 Rust: Indicator::AtrQuantile { atr_column, window, quantile }
284
327
 
@@ -350,12 +393,11 @@ class Indicator:
350
393
  ... order_strategy="ImmediateEntry"
351
394
  ... )
352
395
  """
353
- return {
354
- "type": "AtrQuantile",
355
- "atr_column": atr_column,
356
- "window": window,
357
- "quantile": quantile,
358
- }
396
+ return ATRQuantileIndicator(
397
+ atr_column=atr_column,
398
+ window=window,
399
+ quantile=quantile,
400
+ )
359
401
 
360
402
  @staticmethod
361
403
  def supertrend(
@@ -365,8 +407,8 @@ class Indicator:
365
407
  low: str = "low",
366
408
  close: str = "close",
367
409
  fields: Optional[List[str]] = None,
368
- ) -> Dict[str, Any]:
369
- """創建 SuperTrend 指標配置
410
+ ) -> SuperTrendIndicator:
411
+ """創建 SuperTrend 指標配置(强类型)
370
412
 
371
413
  對應 Rust: Indicator::SuperTrend { multiplier, volatility_column, high, low, close, fields }
372
414
 
@@ -408,17 +450,14 @@ class Indicator:
408
450
  ... fields=["direction", "supertrend"]
409
451
  ... )
410
452
  """
411
- config = {
412
- "type": "SuperTrend",
413
- "multiplier": float(multiplier),
414
- "volatility_column": volatility_column,
415
- "high": high,
416
- "low": low,
417
- "close": close,
418
- }
419
- if fields is not None:
420
- config["fields"] = fields
421
- return config
453
+ return SuperTrendIndicator(
454
+ multiplier=float(multiplier),
455
+ volatility_column=volatility_column,
456
+ high=high,
457
+ low=low,
458
+ close=close,
459
+ fields=fields,
460
+ )
422
461
 
423
462
  @staticmethod
424
463
  def market_profile(
@@ -427,8 +466,8 @@ class Indicator:
427
466
  value_area_pct: float = 0.7,
428
467
  fields: Optional[List[str]] = None,
429
468
  shape_config: Optional[Dict[str, Any]] = None,
430
- ) -> Dict[str, Any]:
431
- """創建 Market Profile 指標配置
469
+ ) -> MarketProfileIndicator:
470
+ """創建 Market Profile 指標配置(强类型)
432
471
 
433
472
  對應 Rust: Indicator::MarketProfile { anchor_config, tick_size, ... }
434
473
 
@@ -486,21 +525,17 @@ class Indicator:
486
525
  ... shape_config=shape_cfg.model_dump()
487
526
  ... )
488
527
  """
489
- config = {
490
- "type": "MarketProfile",
491
- "anchor_config": anchor_config,
492
- "tick_size": tick_size,
493
- "value_area_pct": value_area_pct,
494
- }
495
- if fields is not None:
496
- config["fields"] = fields
497
- if shape_config is not None:
498
- config["shape_config"] = shape_config
499
- return config
528
+ return MarketProfileIndicator(
529
+ anchor_config=anchor_config,
530
+ tick_size=tick_size,
531
+ value_area_pct=value_area_pct,
532
+ fields=fields,
533
+ shape_config=shape_config,
534
+ )
500
535
 
501
536
  @staticmethod
502
- def cci(period: int = 20) -> Dict[str, Any]:
503
- """創建 CCI 指標配置
537
+ def cci(period: int = 20) -> CCIIndicator:
538
+ """創建 CCI 指標配置(强类型)
504
539
 
505
540
  對應 Rust: Indicator::CCI { period }
506
541
 
@@ -535,11 +570,11 @@ class Indicator:
535
570
  >>> overbought = pl.col("1h_CCI|14") > 100
536
571
  >>> oversold = pl.col("1h_CCI|14") < -100
537
572
  """
538
- return {"type": "CCI", "period": period}
573
+ return CCIIndicator(period=period)
539
574
 
540
575
  @staticmethod
541
- def rsi(period: int = 14, column: str = "close") -> Dict[str, Any]:
542
- """創建 RSI 指標配置
576
+ def rsi(period: int = 14, column: str = "close") -> RSIIndicator:
577
+ """創建 RSI 指標配置(强类型)
543
578
 
544
579
  對應 Rust: Indicator::RSI { period, column }
545
580
 
@@ -579,7 +614,7 @@ class Indicator:
579
614
  >>> entry_long = pl.col("15min_RSI|14") < 30 # RSI < 30 做多
580
615
  >>> entry_short = pl.col("15min_RSI|14") > 70 # RSI > 70 做空
581
616
  """
582
- return {"type": "RSI", "period": period, "column": column}
617
+ return RSIIndicator(period=period, column=column)
583
618
 
584
619
  @staticmethod
585
620
  def bollinger_bands(
@@ -587,8 +622,8 @@ class Indicator:
587
622
  num_std: float = 2.0,
588
623
  column: str = "close",
589
624
  fields: Optional[List[str]] = None,
590
- ) -> Dict[str, Any]:
591
- """創建 Bollinger Bands 指標配置
625
+ ) -> BollingerBandsIndicator:
626
+ """創建 Bollinger Bands 指標配置(强类型)
592
627
 
593
628
  對應 Rust: Indicator::BollingerBands { period, num_std, column, fields }
594
629
 
@@ -645,15 +680,12 @@ class Indicator:
645
680
  >>> breakout_up = pl.col("close") > upper
646
681
  >>> breakout_down = pl.col("close") < lower
647
682
  """
648
- config = {
649
- "type": "BollingerBands",
650
- "period": period,
651
- "num_std": float(num_std),
652
- "column": column,
653
- }
654
- if fields is not None:
655
- config["fields"] = fields
656
- return config
683
+ return BollingerBandsIndicator(
684
+ period=period,
685
+ num_std=float(num_std),
686
+ column=column,
687
+ fields=fields,
688
+ )
657
689
 
658
690
  @staticmethod
659
691
  def macd(
@@ -662,8 +694,8 @@ class Indicator:
662
694
  signal_period: int = 9,
663
695
  column: str = "close",
664
696
  fields: Optional[List[str]] = None,
665
- ) -> Dict[str, Any]:
666
- """創建 MACD 指標配置
697
+ ) -> MACDIndicator:
698
+ """創建 MACD 指標配置(强类型)
667
699
 
668
700
  對應 Rust: Indicator::MACD { fast_period, slow_period, signal_period, column, fields }
669
701
 
@@ -720,24 +752,21 @@ class Indicator:
720
752
  >>> golden_cross = macd_line > signal_line # 金叉(做多)
721
753
  >>> death_cross = macd_line < signal_line # 死叉(做空)
722
754
  """
723
- config = {
724
- "type": "MACD",
725
- "fast_period": fast_period,
726
- "slow_period": slow_period,
727
- "signal_period": signal_period,
728
- "column": column,
729
- }
730
- if fields is not None:
731
- config["fields"] = fields
732
- return config
755
+ return MACDIndicator(
756
+ fast_period=fast_period,
757
+ slow_period=slow_period,
758
+ signal_period=signal_period,
759
+ column=column,
760
+ fields=fields,
761
+ )
733
762
 
734
763
  @staticmethod
735
764
  def stochastic(
736
765
  k_period: int = 14,
737
766
  d_period: int = 3,
738
767
  fields: Optional[List[str]] = None,
739
- ) -> Dict[str, Any]:
740
- """創建 Stochastic 指標配置
768
+ ) -> StochasticIndicator:
769
+ """創建 Stochastic 指標配置(强类型)
741
770
 
742
771
  對應 Rust: Indicator::Stochastic { k_period, d_period, fields }
743
772
 
@@ -790,18 +819,15 @@ class Indicator:
790
819
  >>> golden_cross = k_line > d_line # 金叉(做多)
791
820
  >>> death_cross = k_line < d_line # 死叉(做空)
792
821
  """
793
- config = {
794
- "type": "Stochastic",
795
- "k_period": k_period,
796
- "d_period": d_period,
797
- }
798
- if fields is not None:
799
- config["fields"] = fields
800
- return config
822
+ return StochasticIndicator(
823
+ k_period=k_period,
824
+ d_period=d_period,
825
+ fields=fields,
826
+ )
801
827
 
802
828
  @staticmethod
803
- def adx(period: int = 14, fields: Optional[List[str]] = None) -> Dict[str, Any]:
804
- """創建 ADX 指標配置
829
+ def adx(period: int = 14, fields: Optional[List[str]] = None) -> ADXIndicator:
830
+ """創建 ADX 指標配置(强类型)
805
831
 
806
832
  對應 Rust: Indicator::ADX { period, fields }
807
833
 
@@ -864,17 +890,14 @@ class Indicator:
864
890
  >>> uptrend = (plus_di > minus_di) & (adx_value > 25) # 上升趨勢 + 強度足夠
865
891
  >>> downtrend = (plus_di < minus_di) & (adx_value > 25) # 下降趨勢 + 強度足夠
866
892
  """
867
- config = {
868
- "type": "ADX",
869
- "period": period,
870
- }
871
- if fields is not None:
872
- config["fields"] = fields
873
- return config
893
+ return ADXIndicator(
894
+ period=period,
895
+ fields=fields,
896
+ )
874
897
 
875
898
  @staticmethod
876
- def raw_ohlcv(column: str) -> Dict[str, Any]:
877
- """創建 RawOhlcv 指標配置
899
+ def raw_ohlcv(column: str) -> RawOhlcvIndicator:
900
+ """創建 RawOhlcv 指標配置(强类型)
878
901
 
879
902
  對應 Rust: Indicator::RawOhlcv { column }
880
903
 
@@ -913,7 +936,7 @@ class Indicator:
913
936
  >>> # 輸出列名: "TXF_M1_SHIOAJI_FUTURE_open_1D"
914
937
  >>> # 策略條件: if price > daily_open.col(): ...
915
938
  """
916
- return {"type": "RawOhlcv", "column": column}
939
+ return RawOhlcvIndicator(column=column)
917
940
 
918
941
 
919
942
  # ============================================================================
@@ -1075,17 +1098,47 @@ def create_profile_shape_config(
1075
1098
 
1076
1099
 
1077
1100
  class IndicatorSpec(BaseModel):
1078
- """指標規範"""
1101
+ """指標規範(强类型版本)"""
1079
1102
 
1080
1103
  instrument_id: Optional[str] = Field(
1081
1104
  None, description="交易標的 ID(可選,用於多商品場景)"
1082
1105
  )
1083
- freq: Union[Freq, str] = Field(..., description="頻率 (使用 Freq enum 或自訂字串)")
1106
+ freq: Freq = Field(..., description="頻率 (Freq enum)")
1084
1107
  shift: int = Field(1, description="位移(預設 1)")
1085
- indicator: Dict[str, Any] = Field(
1086
- ..., description="指標配置 (使用 indicator 工廠函數建立)"
1108
+ indicator: IndicatorConfig = Field(
1109
+ ..., description="指標配置(强类型 Pydantic 模型)"
1087
1110
  )
1088
1111
 
1112
+ @field_validator("freq", mode="before")
1113
+ @classmethod
1114
+ def convert_freq(cls, v: Any) -> Freq:
1115
+ """自動轉換字串為 Freq enum(保持 API 兼容性)"""
1116
+ if isinstance(v, str):
1117
+ try:
1118
+ return Freq(v)
1119
+ except ValueError:
1120
+ valid_values = [e.value for e in Freq]
1121
+ raise ValueError(
1122
+ f"Invalid freq: '{v}'. "
1123
+ f"Valid values: {', '.join(valid_values)}"
1124
+ )
1125
+ return v
1126
+
1127
+ @field_validator("indicator", mode="before")
1128
+ @classmethod
1129
+ def convert_indicator(cls, v: Any) -> IndicatorConfig:
1130
+ """自動轉換 Dict 為強類型 Indicator 模型(保持 API 兼容性)
1131
+
1132
+ 接受:
1133
+ - Dict[str, Any]: 從 API 返回或舊代碼(自動轉換)
1134
+ - IndicatorConfig: 新代碼使用強類型模型
1135
+
1136
+ Pydantic 會根據 'type' 字段自動選擇正確的模型。
1137
+ """
1138
+ # Dict 會被 Pydantic 自動轉換為對應的 Indicator 模型
1139
+ # 強類型模型直接返回
1140
+ return v
1141
+
1089
1142
  def short_name(self) -> str:
1090
1143
  """生成指標簡短名稱(與 Rust 的 short_name 一致)
1091
1144
 
@@ -1101,53 +1154,39 @@ class IndicatorSpec(BaseModel):
1101
1154
  Raises:
1102
1155
  ValueError: 未知的指標類型
1103
1156
  """
1104
- indicator_type = self.indicator.get("type")
1157
+ indicator_type = self.indicator.type
1105
1158
 
1106
1159
  if indicator_type == "SMA":
1107
- period = self.indicator.get("period")
1108
- column = self.indicator.get("column", "close")
1109
- return f"SMA|{period}.{column}"
1160
+ return f"SMA|{self.indicator.period}.{self.indicator.column}"
1110
1161
 
1111
1162
  elif indicator_type == "EMA":
1112
- period = self.indicator.get("period")
1113
- column = self.indicator.get("column", "close")
1114
- return f"EMA|{period}.{column}"
1163
+ return f"EMA|{self.indicator.period}.{self.indicator.column}"
1115
1164
 
1116
1165
  elif indicator_type == "SMMA":
1117
- period = self.indicator.get("period")
1118
- column = self.indicator.get("column", "close")
1119
- return f"SMMA|{period}.{column}"
1166
+ return f"SMMA|{self.indicator.period}.{self.indicator.column}"
1120
1167
 
1121
1168
  elif indicator_type == "WMA":
1122
- period = self.indicator.get("period")
1123
- column = self.indicator.get("column", "close")
1124
- return f"WMA|{period}.{column}"
1169
+ return f"WMA|{self.indicator.period}.{self.indicator.column}"
1125
1170
 
1126
1171
  elif indicator_type == "ATR":
1127
- period = self.indicator.get("period")
1128
1172
  # ATR 始終返回單一數值欄位,無 quantile
1129
- return f"ATR|{period}"
1173
+ return f"ATR|{self.indicator.period}"
1130
1174
 
1131
1175
  elif indicator_type == "AtrQuantile":
1132
- atr_column = self.indicator.get("atr_column")
1133
- window = self.indicator.get("window")
1134
- quantile = self.indicator.get("quantile")
1135
1176
  # 將 quantile 轉為百分比(例如 0.5 -> 50)
1136
- quantile_pct = int(quantile * 100)
1177
+ quantile_pct = int(self.indicator.quantile * 100)
1137
1178
  # 格式:ATRQ|{atr_column}_Q{quantile%}_{window}
1138
- return f"ATRQ|{atr_column}_Q{quantile_pct}_{window}"
1179
+ return f"ATRQ|{self.indicator.atr_column}_Q{quantile_pct}_{self.indicator.window}"
1139
1180
 
1140
1181
  elif indicator_type == "SuperTrend":
1141
- multiplier = self.indicator.get("multiplier")
1142
- volatility_column = self.indicator.get("volatility_column")
1143
- return f"ST|{multiplier}x_{volatility_column}"
1182
+ return f"ST|{self.indicator.multiplier}x_{self.indicator.volatility_column}"
1144
1183
 
1145
1184
  elif indicator_type == "MarketProfile":
1146
1185
  # 提取 anchor_config 資訊
1147
- anchor_config = self.indicator.get("anchor_config", {})
1186
+ anchor_config = self.indicator.anchor_config
1148
1187
  end_rule = anchor_config.get("end_rule", {})
1149
1188
  lookback_days = anchor_config.get("lookback_days", 1)
1150
- tick_size = self.indicator.get("tick_size")
1189
+ tick_size = self.indicator.tick_size
1151
1190
 
1152
1191
  # 格式化時間字串(支持新舊兩種格式)
1153
1192
  rule_type = end_rule.get("type")
@@ -1212,37 +1251,26 @@ class IndicatorSpec(BaseModel):
1212
1251
  return f"MP|{time_str}_{lookback_days}_{tick_size}"
1213
1252
 
1214
1253
  elif indicator_type == "CCI":
1215
- period = self.indicator.get("period")
1216
- return f"CCI|{period}"
1254
+ return f"CCI|{self.indicator.period}"
1217
1255
 
1218
1256
  elif indicator_type == "RSI":
1219
- period = self.indicator.get("period")
1220
- return f"RSI|{period}"
1257
+ return f"RSI|{self.indicator.period}"
1221
1258
 
1222
1259
  elif indicator_type == "BollingerBands":
1223
- period = self.indicator.get("period")
1224
- num_std = self.indicator.get("num_std")
1225
- return f"BB|{period}_{num_std}"
1260
+ return f"BB|{self.indicator.period}_{self.indicator.num_std}"
1226
1261
 
1227
1262
  elif indicator_type == "MACD":
1228
- fast_period = self.indicator.get("fast_period")
1229
- slow_period = self.indicator.get("slow_period")
1230
- signal_period = self.indicator.get("signal_period")
1231
- return f"MACD|{fast_period}_{slow_period}_{signal_period}"
1263
+ return f"MACD|{self.indicator.fast_period}_{self.indicator.slow_period}_{self.indicator.signal_period}"
1232
1264
 
1233
1265
  elif indicator_type == "Stochastic":
1234
- k_period = self.indicator.get("k_period")
1235
- d_period = self.indicator.get("d_period")
1236
- return f"STOCH|{k_period}_{d_period}"
1266
+ return f"STOCH|{self.indicator.k_period}_{self.indicator.d_period}"
1237
1267
 
1238
1268
  elif indicator_type == "ADX":
1239
- period = self.indicator.get("period")
1240
- return f"ADX|{period}"
1269
+ return f"ADX|{self.indicator.period}"
1241
1270
 
1242
1271
  elif indicator_type == "RawOhlcv":
1243
1272
  # RawOhlcv 只返回 column 名稱
1244
- column = self.indicator.get("column")
1245
- return column
1273
+ return self.indicator.column
1246
1274
 
1247
1275
  else:
1248
1276
  raise ValueError(f"未知的指標類型: {indicator_type}")
@@ -1312,8 +1340,8 @@ class Trigger(BaseModel):
1312
1340
  """
1313
1341
 
1314
1342
  name: str = Field(..., description="觸發器名稱")
1315
- order_strategy: Union[OrderStrategy, str] = Field(
1316
- ..., description="訂單策略(OrderStrategy enum 或字串)"
1343
+ order_strategy: OrderStrategy = Field(
1344
+ ..., description="訂單策略(OrderStrategy enum"
1317
1345
  )
1318
1346
  priority: int = Field(..., description="優先級")
1319
1347
  note: Optional[str] = Field(None, description="備註")
@@ -1322,6 +1350,21 @@ class Trigger(BaseModel):
1322
1350
  conditions: List[pl.Expr] = Field(..., description="條件列表(Polars Expr)")
1323
1351
  price_expr: pl.Expr = Field(..., description="價格表達式(Polars Expr)")
1324
1352
 
1353
+ @field_validator("order_strategy", mode="before")
1354
+ @classmethod
1355
+ def convert_order_strategy(cls, v: Any) -> OrderStrategy:
1356
+ """自動轉換字串為 OrderStrategy enum(保持 API 兼容性)"""
1357
+ if isinstance(v, str):
1358
+ try:
1359
+ return OrderStrategy(v)
1360
+ except ValueError:
1361
+ valid_values = [e.value for e in OrderStrategy]
1362
+ raise ValueError(
1363
+ f"Invalid order_strategy: '{v}'. "
1364
+ f"Valid values: {', '.join(valid_values)}"
1365
+ )
1366
+ return v
1367
+
1325
1368
  @field_validator("conditions", mode="before")
1326
1369
  @classmethod
1327
1370
  def validate_conditions(cls, v: Any) -> List[pl.Expr]:
@@ -1350,25 +1393,53 @@ class Trigger(BaseModel):
1350
1393
  """序列化 price_expr 為 dict(與服務器格式一致)"""
1351
1394
  return PolarsExprField.serialize(price_expr)
1352
1395
 
1353
- class Config:
1354
- # 允許 pl.Expr 這種自定義類型
1355
- arbitrary_types_allowed = True
1396
+ model_config = ConfigDict(
1397
+ arbitrary_types_allowed=True # 允許 pl.Expr 這種自定義類型
1398
+ )
1356
1399
 
1357
1400
 
1358
1401
  class Blueprint(BaseModel):
1359
1402
  """策略藍圖"""
1360
1403
 
1361
1404
  name: str = Field(..., description="藍圖名稱")
1362
- direction: Union[TradeDirection, str] = Field(..., description="方向")
1363
- trend_type: Union[TrendType, str] = Field(
1364
- ..., description="趨勢類型"
1365
- )
1405
+ direction: TradeDirection = Field(..., description="方向")
1406
+ trend_type: TrendType = Field(..., description="趨勢類型")
1366
1407
  entry_first: bool = Field(..., description="是否優先進場")
1367
1408
  note: str = Field(..., description="備註")
1368
1409
 
1369
1410
  entry_triggers: List[Trigger] = Field(..., description="進場觸發器列表")
1370
1411
  exit_triggers: List[Trigger] = Field(..., description="出場觸發器列表")
1371
1412
 
1413
+ @field_validator("direction", mode="before")
1414
+ @classmethod
1415
+ def convert_direction(cls, v: Any) -> TradeDirection:
1416
+ """自動轉換字串為 TradeDirection enum(保持 API 兼容性)"""
1417
+ if isinstance(v, str):
1418
+ try:
1419
+ return TradeDirection(v)
1420
+ except ValueError:
1421
+ valid_values = [e.value for e in TradeDirection]
1422
+ raise ValueError(
1423
+ f"Invalid direction: '{v}'. "
1424
+ f"Valid values: {', '.join(valid_values)}"
1425
+ )
1426
+ return v
1427
+
1428
+ @field_validator("trend_type", mode="before")
1429
+ @classmethod
1430
+ def convert_trend_type(cls, v: Any) -> TrendType:
1431
+ """自動轉換字串為 TrendType enum(保持 API 兼容性)"""
1432
+ if isinstance(v, str):
1433
+ try:
1434
+ return TrendType(v)
1435
+ except ValueError:
1436
+ valid_values = [e.value for e in TrendType]
1437
+ raise ValueError(
1438
+ f"Invalid trend_type: '{v}'. "
1439
+ f"Valid values: {', '.join(valid_values)}"
1440
+ )
1441
+ return v
1442
+
1372
1443
 
1373
1444
  class StrategyConfig(BaseModel):
1374
1445
  """完整策略配置"""
@@ -1536,7 +1607,7 @@ def create_trigger(
1536
1607
  name: str,
1537
1608
  conditions: List[pl.Expr],
1538
1609
  price_expr: pl.Expr,
1539
- order_strategy: Union[OrderStrategy, str] = OrderStrategy.IMMEDIATE_ENTRY,
1610
+ order_strategy: OrderStrategy = OrderStrategy.IMMEDIATE_ENTRY,
1540
1611
  priority: int = 1,
1541
1612
  note: Optional[str] = None,
1542
1613
  ) -> Trigger:
@@ -1588,10 +1659,10 @@ def create_trigger(
1588
1659
 
1589
1660
  def create_blueprint(
1590
1661
  name: str,
1591
- direction: Union[TradeDirection, str],
1662
+ direction: TradeDirection,
1592
1663
  entry_triggers: List[Trigger],
1593
1664
  exit_triggers: List[Trigger],
1594
- trend_type: Union[TrendType, str] = "Trend",
1665
+ trend_type: TrendType = TrendType.TREND,
1595
1666
  entry_first: bool = True,
1596
1667
  note: str = "",
1597
1668
  ) -> Blueprint:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tradepose-client
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Python client for Tradepose quantitative trading API
5
5
  Author: Tradepose Team
6
6
  License: MIT
@@ -2,7 +2,8 @@ tradepose_client/__init__.py,sha256=51JWgNgRTfKzm52NVCb9VT7rQcGd20f2DoLuF6RzReA,
2
2
  tradepose_client/analysis.py,sha256=RNpk3i9_1uUXM_-K-xYbG3Ckg-MoOwQ6gSExua2N4N8,7563
3
3
  tradepose_client/client.py,sha256=1qD4pnIjCXKgE3VXeB6EgkNuCXvDJEbmPldIjIzAVmk,1798
4
4
  tradepose_client/enums.py,sha256=Dhvwj_diX8r_ByerwZpDSqAsquRI5FMOctm7RxQ1CDA,4887
5
- tradepose_client/models.py,sha256=Fp2Brop-5R5Oo-HmWk_JXvB6oS21wH5bvEM0Akj4FWM,64196
5
+ tradepose_client/indicator_models.py,sha256=PKJAPYgRhlWGUgK346Mlyv7ZKN_Wi_8Hc5hhPKl3QUA,9727
6
+ tradepose_client/models.py,sha256=47D7kkmpBjxvLwLrk2a_Crvuh2BKLx0z10Q46p7FiLs,66669
6
7
  tradepose_client/schema.py,sha256=lPcJU-1xKcGFRoeuvqovrg4LUiOLO5bRZiMVyDkKHUQ,6357
7
8
  tradepose_client/viz.py,sha256=qu9zyAXa_Q7D4rxdgN55JyYiY24ZD3r0jf2SWtqGZYg,22989
8
9
  tradepose_client/api/__init__.py,sha256=ezeMxOBrUDRhlqZNO-X0X9KbNJGccDseKQ95zAvyhFY,218
@@ -14,8 +15,8 @@ tradepose_client/builder/__init__.py,sha256=PIrsZcxP5cEa2_K1Zzpcf2FVc62iavKCNT6D
14
15
  tradepose_client/builder/blueprint_builder.py,sha256=7ssa3J3AMezZlWEyGbkaSfwhIyGPIzgl1bOKY7owZg4,9347
15
16
  tradepose_client/builder/indicator_wrapper.py,sha256=zfVZE1fX-2emUg8bSjlukP9rF47j8s7h2efo_fSVWWM,3581
16
17
  tradepose_client/builder/strategy_builder.py,sha256=gEeNytZepy3t4jmPKFcZZv3lGSBC0SMQ1QCTcv-qOO0,10433
17
- tradepose_client/builder/trading_context.py,sha256=Z8ueMqAyUQmiUQsAQ8qrAipWkOMFdnEkpeazhFpnf1A,5318
18
- tradepose_client-0.1.1.dist-info/METADATA,sha256=ar2oTs6oLE7SWXCMBSP8cGldrjfu3c5Ve6MBgn38-YI,14056
19
- tradepose_client-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
20
- tradepose_client-0.1.1.dist-info/licenses/LICENSE,sha256=6qYWNkUbxmJl1iN7c4DoM1_8ASsZyXemFO1D0wXtiHk,1071
21
- tradepose_client-0.1.1.dist-info/RECORD,,
18
+ tradepose_client/builder/trading_context.py,sha256=6KQEFlMxh8IVT25XoEc-uU65zHpwjCRmkU1NHiy9cck,4340
19
+ tradepose_client-0.1.2.dist-info/METADATA,sha256=Y8jH2qNVBYs1brHfwbExMQQOd-1U92MeTT8IVyqrd4g,14056
20
+ tradepose_client-0.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
21
+ tradepose_client-0.1.2.dist-info/licenses/LICENSE,sha256=6qYWNkUbxmJl1iN7c4DoM1_8ASsZyXemFO1D0wXtiHk,1071
22
+ tradepose_client-0.1.2.dist-info/RECORD,,