quantvn 0.1.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.

Potentially problematic release.


This version of quantvn might be problematic. Click here for more details.

@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+
6
+ class APIKeyNotSetError(ValueError):
7
+ """Raised when API key has not been set."""
8
+
9
+
10
+ class Config:
11
+ """
12
+ Configuration class for managing the API key and providing the API endpoint.
13
+
14
+ Attributes:
15
+ _api_key (str | None): The stored API key. Defaults to None.
16
+ """
17
+
18
+ _api_key: Optional[str] = None
19
+
20
+ @classmethod
21
+ def set_api_key(cls, apikey: str):
22
+ """
23
+ Set the API key without validation.
24
+
25
+ Raises:
26
+ ValueError: Empty key.
27
+ """
28
+ if not isinstance(apikey, str) or not apikey.strip():
29
+ raise ValueError("API key must be a non-empty string.")
30
+ cls._api_key = apikey.strip()
31
+
32
+ @classmethod
33
+ def get_api_key(cls) -> str:
34
+ """
35
+ Retrieve the currently set API key.
36
+
37
+ Raises:
38
+ APIKeyNotSetError: If the API key has not been set.
39
+ """
40
+ if cls._api_key is None:
41
+ raise APIKeyNotSetError(
42
+ "API key is not set. Use client(apikey=...) to set it."
43
+ )
44
+ return cls._api_key
45
+
46
+ @classmethod
47
+ def get_link(cls) -> str:
48
+ """Return the API base URL."""
49
+ return "https://d207hp2u5nyjgn.cloudfront.net"
50
+
51
+
52
+ def client(apikey: str):
53
+ """
54
+ Convenience function to set the API key.
55
+ """
56
+ Config.set_api_key(apikey)
@@ -0,0 +1,4 @@
1
+ from quantvn.vn.metrics.metrics import Metrics
2
+ from quantvn.vn.metrics.backtest import Backtest_Derivates, Backtest_Stock
3
+
4
+ __all__ = ["Metrics", "Backtest_Derivates", "Backtest_Stock"]
@@ -0,0 +1,323 @@
1
+ # ===== Backtest_Stock & helpers (migrated from your “second block”) =====
2
+ import logging
3
+ from abc import abstractmethod
4
+ from typing import TypedDict, List, Dict, Union
5
+ import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ import pandas as pd
8
+
9
+ class Backtest_Derivates:
10
+ """
11
+ A class for backtesting derivatives trading strategies.
12
+
13
+ Parameters
14
+ ----------
15
+ df : pandas.DataFrame
16
+ Dataframe containing historical data with columns ['Date', 'time', 'Close', 'position'].
17
+ pnl_type : str, optional
18
+ Type of PNL calculation ('raw' or 'after_fees'), by default 'after_fees'.
19
+
20
+ Raises
21
+ ------
22
+ ValueError
23
+ If pnl_type is not 'raw' or 'after_fees'.
24
+ """
25
+
26
+ def __init__(self, df, pnl_type="after_fees"):
27
+ """
28
+ Initializes the BacktestDerivates class.
29
+
30
+ Parameters
31
+ ----------
32
+ df : pd.DataFrame
33
+ Data containing trade details.
34
+ pnl_type : str, optional
35
+ Type of PNL calculation ('raw' or 'after_fees'), by default "after_fees".
36
+ """
37
+ if pnl_type not in ["raw", "after_fees"]:
38
+ raise ValueError("Invalid pnl_type. Choose 'raw' or 'after_fees'.")
39
+
40
+ self.df = df.copy()
41
+ self.pnl_type = pnl_type
42
+ self.df["datetime"] = pd.to_datetime(self.df["Date"] + " " + self.df["time"])
43
+ self.df.set_index("datetime", inplace=True)
44
+ self.df.sort_index(inplace=True)
45
+
46
+ # Calculate raw PNL
47
+ self.df["pnl_raw"] = self.df["Close"].diff().shift(-1) * self.df["position"]
48
+ self.df["pnl_raw"].fillna(0, inplace=True)
49
+
50
+ # Calculate PNL after fees
51
+ transaction_fee = 2700 / 100000 # VND per contract
52
+ overnight_fee = 2550 / 100000 # VND per contract per day if held overnight
53
+
54
+ self.df["transaction_fee"] = self.df["position"].diff().abs() * transaction_fee
55
+
56
+ # Identify overnight holdings
57
+ self.df["date"] = self.df.index.date
58
+ self.df["overnight"] = (self.df["position"] > 0) & (
59
+ self.df["date"] != self.df["date"].shift()
60
+ )
61
+ self.df["overnight_fee"] = self.df["overnight"] * overnight_fee
62
+
63
+ self.df["total_fee"] = self.df["transaction_fee"].fillna(0) + self.df[
64
+ "overnight_fee"
65
+ ].fillna(0)
66
+ self.df["pnl_after_fees"] = self.df["pnl_raw"] - self.df["total_fee"]
67
+
68
+ def PNL(self):
69
+ """
70
+ Calculate cumulative PNL based on selected pnl_type.
71
+
72
+ Returns
73
+ -------
74
+ pandas.Series
75
+ Cumulative PNL.
76
+ """
77
+ return self.df[f"pnl_{self.pnl_type}"].cumsum()
78
+
79
+ def daily_PNL(self):
80
+ """
81
+ Calculate daily PNL based on selected pnl_type.
82
+
83
+ Returns
84
+ -------
85
+ pandas.Series
86
+ Daily cumulative PNL.
87
+ """
88
+ daily_pnl = (
89
+ self.df.groupby(self.df.index.date)[f"pnl_{self.pnl_type}"].sum().cumsum()
90
+ )
91
+ return daily_pnl
92
+
93
+ def estimate_minimum_capital(self):
94
+ """
95
+ Estimate the minimum capital required to run the strategy.
96
+
97
+ Returns
98
+ -------
99
+ float
100
+ Minimum capital required.
101
+ """
102
+ self.df["cumulative_pnl"] = (
103
+ self.df[f"pnl_{self.pnl_type}"].cumsum().shift().fillna(0)
104
+ )
105
+ self.df["capital_required"] = (
106
+ self.df["position"].abs() * self.df["Close"]
107
+ ) - self.df["cumulative_pnl"]
108
+
109
+ return max(self.df["capital_required"].max(), 0)
110
+
111
+ def PNL_percentage(self):
112
+ """
113
+ Calculate PNL percentage relative to minimum required capital.
114
+
115
+ Returns
116
+ -------
117
+ float
118
+ PNL percentage.
119
+ """
120
+ min_capital = self.estimate_minimum_capital()
121
+ if min_capital == 0:
122
+ return np.nan # Avoid division by zero
123
+ return self.daily_PNL() / min_capital
124
+
125
+ def avg_pos(self):
126
+ """
127
+ Calculate average daily pos enter
128
+
129
+ Returns
130
+ -------
131
+ float
132
+ Average pos enter per day
133
+ """
134
+ return abs(self.df['position'].diff().dropna()).sum()/len(self.daily_PNL())
135
+
136
+
137
+ class Backtest_Stock:
138
+ """
139
+ Backtest cổ phiếu (long-only):
140
+ - Phí giao dịch 0.1% trên giá trị khớp (mua + bán)
141
+ - Ép giữ tối thiểu 'min_hold_days' phiên (T+2.5 ~ 3 phiên)
142
+ Kỳ vọng input df có cột: ['Date','time','Close','position'].
143
+ 'position' = số lượng cổ phiếu mong muốn (âm sẽ bị cắt về 0).
144
+ """
145
+
146
+ def __init__(self, df: pd.DataFrame, pnl_type: str = "after_fees", min_hold_days: int = 3):
147
+ if pnl_type not in ["raw", "after_fees"]:
148
+ raise ValueError("Invalid pnl_type. Choose 'raw' or 'after_fees'.")
149
+
150
+ self.pnl_type = pnl_type
151
+ self.min_hold_days = int(min_hold_days)
152
+
153
+ # Chuẩn hóa thời gian & index
154
+ self.df = df.copy()
155
+ self.df["datetime"] = pd.to_datetime(self.df["Date"].astype(str) + " " + self.df["time"].astype(str), errors="coerce")
156
+ self.df = self.df.dropna(subset=["datetime"])
157
+ self.df.set_index("datetime", inplace=True)
158
+ self.df.sort_index(inplace=True)
159
+
160
+ # Long-only ý định
161
+ self.df["Close"] = pd.to_numeric(self.df["Close"], errors="coerce")
162
+ self.df = self.df.dropna(subset=["Close"])
163
+ self.df["position_intent"] = pd.to_numeric(self.df["position"], errors="coerce").fillna(0).clip(lower=0).astype(float)
164
+
165
+ # Xây effective position tôn trọng min_hold theo SỐ PHIÊN
166
+ eff_pos, trade_qty = self._build_effective_position_with_min_hold(
167
+ index=self.df.index,
168
+ desired_positions=self.df["position_intent"].to_numpy(dtype=float),
169
+ min_hold_days=self.min_hold_days,
170
+ )
171
+ # Trả về numpy → gán theo vị trí, tránh lệch index
172
+ self.df["effective_position"] = eff_pos
173
+ self.df["trade_qty"] = trade_qty
174
+
175
+ # PnL: giữ vị thế từ bar t -> t+1
176
+ self.df["pnl_raw"] = self.df["Close"].diff().shift(-1).fillna(0) * self.df["effective_position"]
177
+
178
+ # Phí giao dịch: 0.1% notional mỗi lần khớp
179
+ fee_rate = 0.001
180
+ notional_traded = np.abs(self.df["trade_qty"].to_numpy()) * self.df["Close"].to_numpy()
181
+ self.df["transaction_fee"] = notional_traded * fee_rate
182
+
183
+ self.df["pnl_after_fees"] = self.df["pnl_raw"] - self.df["transaction_fee"]
184
+
185
+ @staticmethod
186
+ def _build_effective_position_with_min_hold(
187
+ index: pd.DatetimeIndex,
188
+ desired_positions: np.ndarray,
189
+ min_hold_days: int = 3,
190
+ ) -> tuple[np.ndarray, np.ndarray]:
191
+ """
192
+ Tạo chuỗi vị thế thực sự (long-only) với ràng buộc giữ tối thiểu N phiên.
193
+ Dùng FIFO lots: mỗi lot có (qty_remaining, day_id_entry).
194
+
195
+ - index: DatetimeIndex (để nhóm ngày/phiên)
196
+ - desired_positions: mảng số lượng mong muốn theo bar (>=0)
197
+ - min_hold_days: số phiên tối thiểu cần giữ mới được bán
198
+ """
199
+ n = len(desired_positions)
200
+ if len(index) != n:
201
+ raise ValueError("index and desired_positions must have the same length")
202
+
203
+ # Ánh xạ mỗi timestamp sang mã phiên tăng dần (0,1,2,...) theo NGÀY (calendar day)
204
+ # Nếu cần đúng 'trading days' theo lịch nghỉ lễ, hãy thay bằng lịch giao dịch VN.
205
+ dates = pd.Index(index.date)
206
+ day_change = np.r_[True, dates[1:] != dates[:-1]]
207
+ day_id = np.cumsum(day_change) - 1 # 0-based day id
208
+
209
+ effective_pos = np.zeros(n, dtype=float)
210
+ trade_qty = np.zeros(n, dtype=float)
211
+
212
+ # FIFO lots: list of [qty_remaining, entry_day_id]
213
+ lots: list[list[float | int]] = []
214
+ prev_effective = 0.0
215
+
216
+ for i in range(n):
217
+ desired = float(max(0.0, desired_positions[i]))
218
+
219
+ if i == 0:
220
+ # Mua vào nếu cần ở bar đầu
221
+ buy_qty = max(0.0, desired - prev_effective)
222
+ if buy_qty > 0:
223
+ lots.append([buy_qty, int(day_id[i])])
224
+ trade_qty[i] = buy_qty
225
+ prev_effective += buy_qty
226
+ # Luôn chốt effective_pos cho bar này
227
+ effective_pos[i] = prev_effective
228
+ continue
229
+
230
+ if desired > prev_effective:
231
+ # Cần mua thêm
232
+ buy_qty = desired - prev_effective
233
+ if buy_qty > 1e-12:
234
+ lots.append([buy_qty, int(day_id[i])])
235
+ trade_qty[i] = buy_qty
236
+ prev_effective += buy_qty
237
+
238
+ elif desired < prev_effective:
239
+ # Cần bán bớt, nhưng chỉ bán các lot đã đủ số phiên
240
+ to_sell = prev_effective - desired
241
+ if to_sell > 1e-12:
242
+ # Bán theo FIFO, chỉ những lot đủ điều kiện
243
+ sell_now_total = 0.0
244
+ for lot in lots:
245
+ if to_sell <= 1e-12:
246
+ break
247
+ qty_rem, d_entry = lot
248
+ if qty_rem <= 1e-12:
249
+ continue
250
+ # Đủ điều kiện nếu số PHIÊN đã qua >= min_hold_days
251
+ if (int(day_id[i]) - int(d_entry)) >= min_hold_days:
252
+ sell_amt = min(qty_rem, to_sell)
253
+ lot[0] = qty_rem - sell_amt
254
+ to_sell -= sell_amt
255
+ sell_now_total += sell_amt
256
+ # Dọn các lot trống
257
+ lots = [lot for lot in lots if lot[0] > 1e-12]
258
+ if sell_now_total > 1e-12:
259
+ trade_qty[i] = -sell_now_total
260
+ prev_effective -= sell_now_total
261
+
262
+ # Luôn set effective_pos ở CUỐI vòng lặp
263
+ effective_pos[i] = prev_effective
264
+
265
+ return effective_pos, trade_qty
266
+
267
+ # ======= Các API kết quả =======
268
+ def PNL(self) -> pd.Series:
269
+ return self.df[f"pnl_{self.pnl_type}"].cumsum()
270
+
271
+ def daily_PNL(self) -> pd.Series:
272
+ ser = self.df.groupby(self.df.index.date)[f"pnl_{self.pnl_type}"].sum()
273
+ return ser.cumsum()
274
+
275
+ def estimate_minimum_capital(self) -> float:
276
+ # Ước lượng nhu cầu vốn tối thiểu thô: notional giữ - lũy kế PnL tại mỗi thời điểm
277
+ cum_pnl = self.df[f"pnl_{self.pnl_type}"].cumsum().shift().fillna(0.0)
278
+ capital_required = (self.df["effective_position"].abs() * self.df["Close"]) - cum_pnl
279
+ return float(max(capital_required.max(), 0.0))
280
+
281
+ def PNL_percentage(self) -> pd.Series:
282
+ min_capital = self.estimate_minimum_capital()
283
+ if min_capital == 0:
284
+ return pd.Series(dtype=float)
285
+ return self.daily_PNL() / min_capital
286
+
287
+ def avg_pos(self) -> float:
288
+ # Trung bình thay đổi vị thế theo ngày (từ effective_position)
289
+ d = self.df["effective_position"].diff().abs().dropna().sum()
290
+ days = max(len(self.daily_PNL()), 1)
291
+ return float(d / days)
292
+
293
+ # ======= Plot giống backtest_derivative (giá + vị thế + equity + volume giao dịch) =======
294
+ def plot_PNL(self, daily: bool = False, title: str = "Backtest Stock"):
295
+ """
296
+ Vẽ duy nhất 1 đường equity sau phí.
297
+ - daily=False: tích lũy theo từng bar
298
+ - daily=True : gộp theo ngày rồi mới tích lũy
299
+ """
300
+ if "pnl_after_fees" not in self.df.columns:
301
+ raise ValueError("Chưa có cột 'pnl_after_fees' trong self.df")
302
+
303
+ if daily:
304
+ eq = self.df.groupby(self.df.index.date)["pnl_after_fees"].sum().cumsum()
305
+ x = pd.to_datetime(eq.index)
306
+ y = eq.values
307
+ x_label = "Date"
308
+ else:
309
+ eq = self.df["pnl_after_fees"].cumsum()
310
+ x = eq.index
311
+ y = eq.values
312
+ x_label = "Time"
313
+
314
+ plt.figure(figsize=(10, 4))
315
+ plt.plot(x, y, linewidth=1.4)
316
+ plt.title(title)
317
+ plt.xlabel(x_label)
318
+ plt.ylabel("Cumulative PnL (after fees)")
319
+ plt.grid(True, alpha=0.3)
320
+ plt.tight_layout()
321
+ plt.show()
322
+
323
+
@@ -0,0 +1,185 @@
1
+ import numpy as np
2
+
3
+
4
+ class Metrics:
5
+ """
6
+ A class for calculating performance metrics from backtest results.
7
+
8
+ Parameters
9
+ ----------
10
+ backtest : BacktestDerivatives
11
+ An instance of BacktestDerivatives containing PNL data.
12
+ """
13
+
14
+ def __init__(self, backtest):
15
+ """
16
+ Initializes the Metrics class.
17
+
18
+ Parameters
19
+ ----------
20
+ backtest : BacktestDerivates
21
+ Instance of backtest results.
22
+ """
23
+ self.backtest = backtest
24
+ self.daily_pnl = backtest.daily_PNL().diff().dropna()
25
+
26
+ def avg_loss(self):
27
+ """
28
+ Compute the average loss from daily PNL.
29
+
30
+ Returns
31
+ -------
32
+ float
33
+ Average loss.
34
+ """
35
+ losses = self.daily_pnl[self.daily_pnl < 0]
36
+ return losses.mean()
37
+
38
+ def avg_return(self):
39
+ """
40
+ Compute the average return from daily PNL.
41
+
42
+ Returns
43
+ -------
44
+ float
45
+ Average return.
46
+ """
47
+ return self.daily_pnl.mean()
48
+
49
+ def avg_win(self):
50
+ """
51
+ Compute the average win from daily PNL.
52
+
53
+ Returns
54
+ -------
55
+ float
56
+ Average win.
57
+ """
58
+ wins = self.daily_pnl[self.daily_pnl > 0]
59
+ return wins.mean()
60
+
61
+ def max_drawdown(self):
62
+ """
63
+ Compute the maximum drawdown.
64
+
65
+ Returns
66
+ -------
67
+ float
68
+ Maximum drawdown as a percentage of minimum capital.
69
+ """
70
+ cumulative = self.daily_pnl.cumsum()
71
+ peak = cumulative.cummax()
72
+ drawdown = cumulative - peak
73
+ return drawdown.min() / self.backtest.estimate_minimum_capital()
74
+
75
+ def win_rate(self):
76
+ """
77
+ Compute the win rate.
78
+
79
+ Returns
80
+ -------
81
+ float
82
+ Win rate.
83
+ """
84
+ wins = (self.daily_pnl > 0).sum()
85
+ total = len(self.daily_pnl)
86
+ return wins / total if total > 0 else 0
87
+
88
+ def volatility(self):
89
+ """
90
+ Compute the standard deviation of daily PNL.
91
+
92
+ Returns
93
+ -------
94
+ float
95
+ Volatility.
96
+ """
97
+ return self.daily_pnl.std()
98
+
99
+ def sharpe(self, risk_free_rate=0.0):
100
+ """
101
+ Compute the Sharpe ratio.
102
+
103
+ Returns
104
+ -------
105
+ float
106
+ Sharpe ratio.
107
+ """
108
+ return (self.avg_return() - risk_free_rate) / self.volatility() * np.sqrt(252)
109
+
110
+ def sortino(self):
111
+ """
112
+ Compute the Sortino ratio.
113
+
114
+ Returns
115
+ -------
116
+ float
117
+ Sortino ratio.
118
+ """
119
+ downside_std = self.daily_pnl[self.daily_pnl < 0].std()
120
+ return (
121
+ np.sqrt(252) * self.avg_return() / downside_std
122
+ if downside_std > 0
123
+ else np.nan
124
+ )
125
+
126
+ def calmar(self):
127
+ """
128
+ Compute the Calmar ratio.
129
+
130
+ Returns
131
+ -------
132
+ float
133
+ Calmar ratio.
134
+ """
135
+ return (
136
+ np.sqrt(252) * self.avg_return() / abs(self.max_drawdown())
137
+ if self.max_drawdown() != 0
138
+ else np.nan
139
+ )
140
+
141
+ def profit_factor(self):
142
+ """
143
+ Compute the profit factor.
144
+
145
+ Returns
146
+ -------
147
+ float
148
+ Profit factor.
149
+ """
150
+ total_gain = self.daily_pnl[self.daily_pnl > 0].sum()
151
+ total_loss = abs(self.daily_pnl[self.daily_pnl < 0].sum())
152
+ return total_gain / total_loss if total_loss != 0 else np.nan
153
+
154
+ def risk_of_ruin(self):
155
+ """
156
+ Compute risk of ruin.
157
+
158
+ Returns
159
+ -------
160
+ float
161
+ Risk of ruin.
162
+ """
163
+ win_rate = self.win_rate()
164
+ loss_rate = 1 - win_rate
165
+ return (
166
+ (loss_rate / win_rate) ** (1 / self.avg_loss())
167
+ if self.avg_loss() != 0
168
+ else np.nan
169
+ )
170
+
171
+ def value_at_risk(self, confidence_level=0.05):
172
+ """
173
+ Compute Value at Risk (VaR).
174
+
175
+ Parameters
176
+ ----------
177
+ confidence_level : float, optional
178
+ Confidence level for VaR, by default 0.05.
179
+
180
+ Returns
181
+ -------
182
+ float
183
+ Value at Risk (VaR).
184
+ """
185
+ return self.daily_pnl.quantile(confidence_level)
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.4
2
+ Name: quantvn
3
+ Version: 0.1.0
4
+ Summary: QuantVN API Library for Financial Data Analysis
5
+ Author: quantvn
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Requires-Python: >=3.9
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests
17
+ Requires-Dist: pandas
18
+ Requires-Dist: matplotlib
19
+ Dynamic: author
20
+ Dynamic: classifier
21
+ Dynamic: description-content-type
22
+ Dynamic: license-file
23
+ Dynamic: requires-dist
24
+ Dynamic: requires-python
25
+ Dynamic: summary
@@ -0,0 +1,29 @@
1
+ quantvn/__init__.py,sha256=9YPnywBFZuYcjgMWWMsnRavq1PnxmDylns5KMWjxuBo,56
2
+ quantvn/crypto/__init__.py,sha256=90HTV3rYM6sJ6l6VXXFdffOT0ZTHXnE-UeXJzX1KBGk,32
3
+ quantvn/crypto/data/__init__.py,sha256=7i4ylyHDdyt9XDO9SXre67_nuUCUhQxbHqBq2Cniz8g,685
4
+ quantvn/crypto/data/const.py,sha256=qLTAycWZHRwtj-mSOT2RB9YFbGFPiFUFnNtuknbbUGE,1143
5
+ quantvn/crypto/data/core.py,sha256=ajcV3xRG3CrkfB71-QZjwqsJ5HfMrLwVXciDxO0onYM,2465
6
+ quantvn/crypto/data/derivatives.py,sha256=HsaOScXpRx91w8hDdSueTNyLt0VcdCeSSPgnALMYHgY,806
7
+ quantvn/crypto/data/utils.py,sha256=x2qAmr_00yZ4LpEqN1WJzKvuhfd-NFZCY9aWS8Z5Ieo,3911
8
+ quantvn/metrics/__init__.py,sha256=RkItghoyBNiZ9m7ZUqydlGEnr0z8G80se4yQZnyxX_4,151
9
+ quantvn/metrics/portfolio.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ quantvn/metrics/single_asset.py,sha256=tVrg3Hw8H-lkpUlklvn9LLcE_RrsCOf9csEiGS_nZx8,12265
11
+ quantvn/metrics/st.py,sha256=VTAZXrrnhwdSpwPlufvYWjizLMsnFaGrPQe7GZayekI,22004
12
+ quantvn/paper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ quantvn/paper/portfolio.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ quantvn/paper/single_asset.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ quantvn/vn/__init__.py,sha256=TUU3zltaExQdwUukUPxwGX5XOLQ1kTssdilZkTAiYkM,37
16
+ quantvn/vn/data/__init__.py,sha256=4l2RwFBOjSzn-H1tUNFTWd8QMcxBv9s6BuoA9N3uRvQ,3467
17
+ quantvn/vn/data/const.py,sha256=4owcC4Ik8Fw8kBhXrLEd9Ux9_MPriSh52OOZQWI795E,1144
18
+ quantvn/vn/data/core.py,sha256=zzuBLBBOuplD-cBDIFEICLbRPXy1_sLHhL4vCTS6M78,35631
19
+ quantvn/vn/data/derivatives.py,sha256=h_PeYj8r7pPgS4Z1AJmtZgXiQWiadQL15yKHE0wXx3Q,1698
20
+ quantvn/vn/data/stocks.py,sha256=cTOERdmCEaLx-mG2tXGxV9b1Hmm7u6EPnVV4d36BiCQ,41772
21
+ quantvn/vn/data/utils.py,sha256=mI0gN_zi21WLDXTGkZCv6V57HwS-LDGadMXBHvR_CLo,1376
22
+ quantvn/vn/metrics/__init__.py,sha256=QGxaESHiAnI24yWPjLCYti1O1twU5pU31yKA5L9QPgk,185
23
+ quantvn/vn/metrics/backtest.py,sha256=w9nG1opPcs8J6g_zfbKrkrd3xrCXeqlW0P7_-eOR_FE,12257
24
+ quantvn/vn/metrics/metrics.py,sha256=9NTiucx7PNI2PhGSJ8Jju_R0LOA7fl9i0776OoaM6rg,4216
25
+ quantvn-0.1.0.dist-info/licenses/LICENSE,sha256=JHlulH_fR5VN3pNTj_4p4w-I-HjxDGNjX2i6qAzNEJw,1064
26
+ quantvn-0.1.0.dist-info/METADATA,sha256=L2f56qCncqD0dZAq3fgYe9jRGGE3k0iVlL_Tm7k_LVg,780
27
+ quantvn-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ quantvn-0.1.0.dist-info/top_level.txt,sha256=04ce9JXFm3R7Lpv3jcyrEa8UYAgWpJNDvn2SJzfhkMs,8
29
+ quantvn-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 XNO API
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ quantvn