p123api 2.4.2__tar.gz → 3.0.0a2__tar.gz
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.
- {p123api-2.4.2 → p123api-3.0.0a2}/PKG-INFO +11 -11
- p123api-3.0.0a2/p123api/__init__.py +2 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/p123api/client.py +238 -142
- p123api-3.0.0a2/p123api/types.py +110 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/p123api.egg-info/PKG-INFO +11 -11
- {p123api-2.4.2 → p123api-3.0.0a2}/p123api.egg-info/SOURCES.txt +0 -1
- p123api-3.0.0a2/p123api.egg-info/requires.txt +5 -0
- p123api-3.0.0a2/pyproject.toml +48 -0
- p123api-2.4.2/p123api/__init__.py +0 -1
- p123api-2.4.2/p123api/types.py +0 -53
- p123api-2.4.2/p123api.egg-info/requires.txt +0 -2
- p123api-2.4.2/pyproject.toml +0 -12
- p123api-2.4.2/setup.py +0 -19
- {p123api-2.4.2 → p123api-3.0.0a2}/LICENSE +0 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/README.md +0 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/p123api.egg-info/dependency_links.txt +0 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/p123api.egg-info/top_level.txt +0 -0
- {p123api-2.4.2 → p123api-3.0.0a2}/setup.cfg +0 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: p123api
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0a2
|
|
4
4
|
Summary: Portfolio123 API wrapper
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
5
|
+
Author-email: Portfolio123 <info@portfolio123.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/portfolio-123/p123api-py
|
|
10
8
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
9
|
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
14
11
|
Description-Content-Type: text/markdown
|
|
15
12
|
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: typing_extensions
|
|
15
|
+
Provides-Extra: pandas
|
|
16
|
+
Requires-Dist: pandas; extra == "pandas"
|
|
17
|
+
Dynamic: license-file
|
|
16
18
|
|
|
17
19
|
# Portfolio123 API Wrapper
|
|
18
20
|
|
|
@@ -25,5 +27,3 @@ with p123api.Client(api_id='your api id', api_key='your api key') as client:
|
|
|
25
27
|
except p123api.ClientException as e:
|
|
26
28
|
print(e)
|
|
27
29
|
```
|
|
28
|
-
|
|
29
|
-
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
from collections import
|
|
1
|
+
from collections.abc import Callable
|
|
2
2
|
import requests
|
|
3
3
|
import time
|
|
4
|
-
import pandas
|
|
5
4
|
from string import Template
|
|
6
|
-
from typing import IO,
|
|
5
|
+
from typing import IO, Any, Literal, overload
|
|
7
6
|
from typing_extensions import deprecated
|
|
8
|
-
import numpy
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from .types import (
|
|
8
|
+
from p123api.types import (
|
|
13
9
|
DataSeriesInfoResult,
|
|
14
10
|
DataSeriesResult,
|
|
15
11
|
IdResult,
|
|
@@ -59,16 +55,15 @@ AIFACTOR_PREDICT_PATH = Template("/aiFactor/predict/$id")
|
|
|
59
55
|
|
|
60
56
|
|
|
61
57
|
class ClientException(Exception):
|
|
62
|
-
def __init__(self, message, *, resp:
|
|
58
|
+
def __init__(self, message, *, resp: requests.Response | None = None):
|
|
63
59
|
super().__init__(message)
|
|
64
60
|
self._resp = resp
|
|
65
|
-
self._exception = exception
|
|
66
61
|
|
|
67
62
|
def get_resp(self):
|
|
68
63
|
return self._resp
|
|
69
64
|
|
|
70
65
|
def get_cause(self):
|
|
71
|
-
return self.
|
|
66
|
+
return self.__cause__
|
|
72
67
|
|
|
73
68
|
@staticmethod
|
|
74
69
|
def build(message, resp: requests.Response):
|
|
@@ -87,19 +82,19 @@ class Client:
|
|
|
87
82
|
class for interfacing with P123 API
|
|
88
83
|
"""
|
|
89
84
|
|
|
90
|
-
def __init__(self, *, api_id, api_key, auth_extra={}, endpoint=ENDPOINT, verify_requests=True):
|
|
85
|
+
def __init__(self, *, api_id: str | int, api_key: str, auth_extra={}, endpoint=ENDPOINT, verify_requests=True):
|
|
91
86
|
self._endpoint = endpoint
|
|
92
87
|
self._verify_requests = verify_requests
|
|
93
88
|
self._max_req_retries = 5
|
|
94
89
|
self._timeout = 300
|
|
95
90
|
self._token = None
|
|
96
91
|
|
|
97
|
-
if not isinstance(api_id, str)
|
|
98
|
-
raise ClientException("api_id
|
|
99
|
-
if not isinstance(api_key, str)
|
|
100
|
-
raise ClientException("api_key
|
|
92
|
+
if not isinstance(api_id, (str, int)):
|
|
93
|
+
raise ClientException("api_id must be str or int")
|
|
94
|
+
if not isinstance(api_key, str):
|
|
95
|
+
raise ClientException("api_key must be str")
|
|
101
96
|
|
|
102
|
-
self._auth_params = {"apiId": api_id, "apiKey": api_key, **auth_extra}
|
|
97
|
+
self._auth_params = {"apiId": str(api_id), "apiKey": api_key, **auth_extra}
|
|
103
98
|
self._session = requests.Session()
|
|
104
99
|
self._method_map = {"GET": self._session.get, "POST": self._session.post, "DELETE": self._session.delete}
|
|
105
100
|
|
|
@@ -112,12 +107,12 @@ class Client:
|
|
|
112
107
|
def close(self):
|
|
113
108
|
self._session.close()
|
|
114
109
|
|
|
115
|
-
def set_max_request_retries(self, retries):
|
|
110
|
+
def set_max_request_retries(self, retries: int):
|
|
116
111
|
if not isinstance(retries, int) or retries < 1 or retries > 10:
|
|
117
112
|
raise ClientException("retries needs to be an int between 1 and 10")
|
|
118
113
|
self._max_req_retries = retries
|
|
119
114
|
|
|
120
|
-
def set_timeout(self, timeout):
|
|
115
|
+
def set_timeout(self, timeout: int):
|
|
121
116
|
if not isinstance(timeout, int) or timeout < 1:
|
|
122
117
|
raise ClientException("timeout needs to be an int greater than 0")
|
|
123
118
|
self._timeout = timeout
|
|
@@ -161,7 +156,15 @@ class Client:
|
|
|
161
156
|
raise ClientException(f"API authentication failed{message}", resp=resp)
|
|
162
157
|
|
|
163
158
|
def _req_with_auth_fallback(
|
|
164
|
-
self,
|
|
159
|
+
self,
|
|
160
|
+
*,
|
|
161
|
+
method: Literal["GET", "POST", "DELETE"] = "POST",
|
|
162
|
+
url: str,
|
|
163
|
+
json: Any = None,
|
|
164
|
+
params: list[tuple[str, Any]] | None = None,
|
|
165
|
+
data: str | IO | None = None,
|
|
166
|
+
headers: dict[str, str] | None = None,
|
|
167
|
+
result_type: type | None = None,
|
|
165
168
|
):
|
|
166
169
|
"""
|
|
167
170
|
Request with authentication fallback, used by all requests (except authentication)
|
|
@@ -190,7 +193,8 @@ class Client:
|
|
|
190
193
|
) as resp:
|
|
191
194
|
|
|
192
195
|
if resp.status_code == 200:
|
|
193
|
-
|
|
196
|
+
json = resp.json()
|
|
197
|
+
return result_type(json) if result_type is not None else json
|
|
194
198
|
|
|
195
199
|
if resp.status_code == 401 or resp.status_code == 403:
|
|
196
200
|
del self._session.headers["Authorization"]
|
|
@@ -215,6 +219,8 @@ class Client:
|
|
|
215
219
|
ret = self._req_with_auth_fallback(url=self._endpoint + SCREEN_ROLLING_BACKTEST_PATH, json=params)
|
|
216
220
|
|
|
217
221
|
if to_pandas:
|
|
222
|
+
import pandas
|
|
223
|
+
|
|
218
224
|
rows = ret["rows"]
|
|
219
225
|
ret["average"][0] = "Average"
|
|
220
226
|
rows.append(ret["average"])
|
|
@@ -236,6 +242,8 @@ class Client:
|
|
|
236
242
|
ret = self._req_with_auth_fallback(url=self._endpoint + SCREEN_BACKTEST_PATH, json=params)
|
|
237
243
|
|
|
238
244
|
if to_pandas:
|
|
245
|
+
import pandas
|
|
246
|
+
|
|
239
247
|
columns = [
|
|
240
248
|
"",
|
|
241
249
|
"Total Return",
|
|
@@ -314,6 +322,8 @@ class Client:
|
|
|
314
322
|
ret = self._req_with_auth_fallback(url=self._endpoint + SCREEN_RUN_PATH, json=params)
|
|
315
323
|
|
|
316
324
|
if to_pandas:
|
|
325
|
+
import pandas
|
|
326
|
+
|
|
317
327
|
ret = pandas.DataFrame(data=ret["rows"], columns=ret["columns"])
|
|
318
328
|
|
|
319
329
|
return ret
|
|
@@ -344,6 +354,8 @@ class Client:
|
|
|
344
354
|
ret = self._req_with_auth_fallback(url=self._endpoint + DATA_PATH, json=params)
|
|
345
355
|
|
|
346
356
|
if to_pandas:
|
|
357
|
+
import pandas
|
|
358
|
+
|
|
347
359
|
raw_obj = dict(ret)
|
|
348
360
|
with_cusips = params.get("cusips") is not None
|
|
349
361
|
with_name = params.get("includeNames")
|
|
@@ -380,28 +392,38 @@ class Client:
|
|
|
380
392
|
ret = self._req_with_auth_fallback(url=self._endpoint + DATA_UNIVERSE_PATH, json=params)
|
|
381
393
|
|
|
382
394
|
if to_pandas:
|
|
395
|
+
import pandas
|
|
396
|
+
|
|
383
397
|
raw_obj = ret
|
|
384
|
-
names = params.get("names")
|
|
385
398
|
f_indices = range(len(params["formulas"]))
|
|
399
|
+
|
|
400
|
+
names = params.get("names")
|
|
401
|
+
if names is None:
|
|
402
|
+
names = [f"formula{i + 1}" for i in f_indices]
|
|
403
|
+
|
|
386
404
|
if params.get("asOfDt"):
|
|
387
405
|
for formula_idx in f_indices:
|
|
388
|
-
name = names[formula_idx]
|
|
406
|
+
name = names[formula_idx]
|
|
389
407
|
ret[name] = ret["data"][formula_idx]
|
|
390
408
|
del ret["dt"], ret["cost"], ret["quotaRemaining"], ret["data"]
|
|
391
409
|
ret = pandas.DataFrame(ret)
|
|
392
410
|
else:
|
|
411
|
+
includeNames = bool(params.get("includeNames"))
|
|
412
|
+
includeFigi = bool(params.get("figi"))
|
|
413
|
+
|
|
393
414
|
data = {"dates": [], "p123Uids": [], "tickers": []}
|
|
394
|
-
includeNames
|
|
395
|
-
if params.get("includeNames"):
|
|
415
|
+
if includeNames:
|
|
396
416
|
data["names"] = []
|
|
397
|
-
|
|
398
|
-
includeFigi = False
|
|
399
|
-
if params.get("figi"):
|
|
417
|
+
if includeFigi:
|
|
400
418
|
data["figi"] = []
|
|
401
|
-
|
|
402
|
-
|
|
419
|
+
|
|
420
|
+
date_data = [[] for _ in f_indices]
|
|
421
|
+
|
|
422
|
+
for formula_idx in f_indices:
|
|
423
|
+
data[names[formula_idx]] = []
|
|
424
|
+
|
|
403
425
|
for dtObj in ret["dates"]:
|
|
404
|
-
data["dates"].extend(dtObj["dt"]
|
|
426
|
+
data["dates"].extend([dtObj["dt"]] * len(dtObj["p123Uids"]))
|
|
405
427
|
data["p123Uids"].extend(dtObj["p123Uids"])
|
|
406
428
|
data["tickers"].extend(dtObj["tickers"])
|
|
407
429
|
if includeNames:
|
|
@@ -409,10 +431,7 @@ class Client:
|
|
|
409
431
|
if includeFigi:
|
|
410
432
|
data["figi"].extend(dtObj["figi"])
|
|
411
433
|
for formula_idx in f_indices:
|
|
412
|
-
|
|
413
|
-
for formula_idx in f_indices:
|
|
414
|
-
name = names[formula_idx] if names is not None else f"formula{formula_idx + 1}"
|
|
415
|
-
data[name] = formulas[formula_idx]
|
|
434
|
+
date_data[formula_idx].extend(dtObj["data"][formula_idx])
|
|
416
435
|
ret = pandas.DataFrame(data)
|
|
417
436
|
ret.attrs["raw_obj"] = raw_obj
|
|
418
437
|
|
|
@@ -428,6 +447,8 @@ class Client:
|
|
|
428
447
|
ret = self._req_with_auth_fallback(url=self._endpoint + RANK_RANKS_PATH, json=params)
|
|
429
448
|
|
|
430
449
|
if to_pandas:
|
|
450
|
+
import pandas
|
|
451
|
+
|
|
431
452
|
names = dict()
|
|
432
453
|
raw_obj = dict(ret)
|
|
433
454
|
del ret["cost"], ret["quotaRemaining"], ret["dt"]
|
|
@@ -577,8 +598,10 @@ class Client:
|
|
|
577
598
|
"""
|
|
578
599
|
...
|
|
579
600
|
|
|
580
|
-
def rank_get(self, *, id:
|
|
581
|
-
return self._req_with_auth_fallback(
|
|
601
|
+
def rank_get(self, *, id: int | None = None, name: str | None = None) -> RankInfoResult:
|
|
602
|
+
return self._req_with_auth_fallback(
|
|
603
|
+
method="GET", url=self._endpoint + RANK_PATH, params=[("id", id), ("name", name)], result_type=RankInfoResult
|
|
604
|
+
)
|
|
582
605
|
|
|
583
606
|
def strategy(self, strategy_id: int):
|
|
584
607
|
"""
|
|
@@ -612,7 +635,10 @@ class Client:
|
|
|
612
635
|
}
|
|
613
636
|
"""
|
|
614
637
|
return self._req_with_auth_fallback(
|
|
615
|
-
method="POST",
|
|
638
|
+
method="POST",
|
|
639
|
+
url=self._endpoint + STRATEGY_COPY_PATH.substitute(id=id),
|
|
640
|
+
json={"name": name, "type": type},
|
|
641
|
+
result_type=IdResult,
|
|
616
642
|
)
|
|
617
643
|
|
|
618
644
|
def book_copy(self, id: int, name: str, type: Literal["BOOK", "BOOKSIM"]) -> IdResult:
|
|
@@ -638,7 +664,7 @@ class Client:
|
|
|
638
664
|
}
|
|
639
665
|
"""
|
|
640
666
|
return self._req_with_auth_fallback(
|
|
641
|
-
method="POST", url=self._endpoint + BOOK_COPY_PATH.substitute(id=id), json={"name": name, "type": type}
|
|
667
|
+
method="POST", url=self._endpoint + BOOK_COPY_PATH.substitute(id=id), json={"name": name, "type": type}, result_type=IdResult
|
|
642
668
|
)
|
|
643
669
|
|
|
644
670
|
def strategy_transactions(self, strategy_id: int, start: str, end: str, to_pandas=False):
|
|
@@ -653,12 +679,18 @@ class Client:
|
|
|
653
679
|
ret = self._req_with_auth_fallback(
|
|
654
680
|
method="GET", url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id), params=[("start", start), ("end", end)]
|
|
655
681
|
)
|
|
656
|
-
|
|
682
|
+
|
|
683
|
+
if to_pandas:
|
|
684
|
+
import pandas
|
|
685
|
+
|
|
686
|
+
return pandas.DataFrame(ret["trans"])
|
|
687
|
+
|
|
688
|
+
return ret
|
|
657
689
|
|
|
658
690
|
def strategy_transaction_import(
|
|
659
691
|
self,
|
|
660
692
|
strategy_id: int,
|
|
661
|
-
data:
|
|
693
|
+
data: str | IO[str],
|
|
662
694
|
content_type: Literal["text/csv", "text/tsv"] = "text/csv",
|
|
663
695
|
update_existing=False,
|
|
664
696
|
make_rebal_dt_curr=False,
|
|
@@ -686,7 +718,7 @@ class Client:
|
|
|
686
718
|
headers={"Content-Type": content_type},
|
|
687
719
|
)
|
|
688
720
|
|
|
689
|
-
def strategy_transaction_delete(self, strategy_id: int, params:
|
|
721
|
+
def strategy_transaction_delete(self, strategy_id: int, params: list[int]):
|
|
690
722
|
"""
|
|
691
723
|
Strategy transaction delete
|
|
692
724
|
:param strategy_id:
|
|
@@ -697,7 +729,7 @@ class Client:
|
|
|
697
729
|
method="DELETE", url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id), json=params
|
|
698
730
|
)
|
|
699
731
|
|
|
700
|
-
def strategy_holdings(self, strategy_id: int, date:
|
|
732
|
+
def strategy_holdings(self, strategy_id: int, date: str | None = None, to_pandas=False):
|
|
701
733
|
"""
|
|
702
734
|
Strategy holdings
|
|
703
735
|
:param strategy_id:
|
|
@@ -711,7 +743,12 @@ class Client:
|
|
|
711
743
|
method="GET", url=self._endpoint + STRATEGY_HOLDINGS_PATH.substitute(id=strategy_id), params=get_params
|
|
712
744
|
)
|
|
713
745
|
|
|
714
|
-
|
|
746
|
+
if to_pandas:
|
|
747
|
+
import pandas
|
|
748
|
+
|
|
749
|
+
return pandas.DataFrame(ret["holdings"])
|
|
750
|
+
|
|
751
|
+
return ret
|
|
715
752
|
|
|
716
753
|
def strategy_trading_system(self, strategy_id: int):
|
|
717
754
|
"""
|
|
@@ -789,39 +826,70 @@ class Client:
|
|
|
789
826
|
def stock_factor_upload(
|
|
790
827
|
self,
|
|
791
828
|
factor_id: int,
|
|
792
|
-
data:
|
|
793
|
-
column_separator:
|
|
794
|
-
existing_data:
|
|
795
|
-
date_format
|
|
796
|
-
decimal_separator:
|
|
797
|
-
ignore_errors
|
|
798
|
-
ignore_duplicates
|
|
829
|
+
data: str | IO[str],
|
|
830
|
+
column_separator: Literal[",", ";", "\t"] = ",",
|
|
831
|
+
existing_data: Literal["overwrite", "skip", "delete"] = "overwrite",
|
|
832
|
+
date_format="yyyy-mm-dd",
|
|
833
|
+
decimal_separator: Literal[".", ","] = ".",
|
|
834
|
+
ignore_errors=False,
|
|
835
|
+
ignore_duplicates=False,
|
|
799
836
|
):
|
|
800
837
|
"""
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
838
|
+
Upload stock factor data.
|
|
839
|
+
|
|
840
|
+
Uploads delimited content to the specified stock factor.
|
|
841
|
+
|
|
842
|
+
The uploaded content must contain a header row.
|
|
843
|
+
Only the first three columns are processed and must contain ``date``, ``<identifier>``, and ``value``.
|
|
844
|
+
|
|
845
|
+
``<identifier>`` may be one of ``id`` (Portfolio123 stock ID), ``ticker`` (Portfolio123 ticker), ``gvkey``, ``cik``, or ``figi``.
|
|
846
|
+
|
|
847
|
+
The ``value`` column may specify ``na``, ``nan``, or ``null`` (case-insensitive) to clear a prior value on an observation date.
|
|
848
|
+
|
|
849
|
+
Example input::
|
|
850
|
+
|
|
851
|
+
date,ticker,value
|
|
852
|
+
2026-01-31,AAPL:USA,1.25
|
|
853
|
+
2026-01-31,MSFT:USA,0.83
|
|
854
|
+
2026-02-28,MSFT:USA,na
|
|
855
|
+
|
|
856
|
+
Note that some types of identifiers may resolve to multiple stocks.
|
|
857
|
+
For example, the FIGI ``BBG001SG1LP6`` resolves to both ``UMC:USA`` and ``UMCB:DEU``.
|
|
858
|
+
|
|
859
|
+
Identifier resolution is performed at the time of the upload. If identifier relationships ever change or coverage expands,
|
|
860
|
+
the stock factor data will still reflect the original resolution.
|
|
861
|
+
|
|
862
|
+
Args:
|
|
863
|
+
factor_id: Unique identifier of the stock factor.
|
|
864
|
+
data: Delimited content string or file-like containing delimited content. Must not exceed 100 MB or 5 million lines.
|
|
865
|
+
column_separator: Separator character between columns. Defaults to comma.
|
|
866
|
+
existing_data: Policy for dealing with collisions against stored (date, stock ID) pairs. Defaults to ``overwrite``.
|
|
867
|
+
- ``overwrite``: Overwrite stored values.
|
|
868
|
+
- ``skip``: Retaine stored values.
|
|
869
|
+
- ``delete``: Clear before storing uploaded data.
|
|
870
|
+
date_format: Date format. Defaults to ``yyyy-mm-dd``.
|
|
871
|
+
decimal_separator: Decimal separator. Defaults to period. If comma is used, the thousands separator, if used, is assumed to be period.
|
|
872
|
+
ignore_errors: If ``True``, lines in the data with errors will be silently discarded.
|
|
873
|
+
ignore_duplicates: If ``True``, additional occurrences of a (date, identifier) pair in the data are skipped.
|
|
874
|
+
"""
|
|
875
|
+
|
|
876
|
+
# COMPAT: column_separator originally accepted 'comma', 'semicolon', 'tab' which matches the API.
|
|
877
|
+
actual_column_separator: str = column_separator
|
|
878
|
+
if column_separator == ",":
|
|
879
|
+
actual_column_separator = "comma"
|
|
880
|
+
elif column_separator == ";":
|
|
881
|
+
actual_column_separator = "semicolon"
|
|
882
|
+
elif column_separator == "\t":
|
|
883
|
+
actual_column_separator = "tab"
|
|
884
|
+
|
|
885
|
+
get_params = [
|
|
886
|
+
("columnSeparator", actual_column_separator),
|
|
887
|
+
("existingData", existing_data),
|
|
888
|
+
("dateFormat", date_format),
|
|
889
|
+
("decimalSeparator", decimal_separator),
|
|
890
|
+
("onError", "continue" if ignore_errors else "stop"),
|
|
891
|
+
("onDuplicates", "continue" if ignore_duplicates else "stop"),
|
|
892
|
+
]
|
|
825
893
|
return self._req_with_auth_fallback(
|
|
826
894
|
url=self._endpoint + STOCK_FACTOR_UPLOAD_PATH.substitute(id=factor_id), params=get_params, data=data
|
|
827
895
|
)
|
|
@@ -832,7 +900,9 @@ class Client:
|
|
|
832
900
|
:param params:
|
|
833
901
|
:return:
|
|
834
902
|
"""
|
|
835
|
-
return self._req_with_auth_fallback(
|
|
903
|
+
return self._req_with_auth_fallback(
|
|
904
|
+
url=self._endpoint + STOCK_FACTOR_CREATE_UPDATE_PATH, json=params, result_type=StockFactorResult
|
|
905
|
+
)
|
|
836
906
|
|
|
837
907
|
def stock_factor_delete(self, factor_id: int):
|
|
838
908
|
"""
|
|
@@ -845,39 +915,51 @@ class Client:
|
|
|
845
915
|
def data_series_upload(
|
|
846
916
|
self,
|
|
847
917
|
series_id: int,
|
|
848
|
-
data:
|
|
849
|
-
existing_data:
|
|
850
|
-
date_format
|
|
851
|
-
decimal_separator:
|
|
852
|
-
ignore_errors
|
|
853
|
-
ignore_duplicates
|
|
854
|
-
contains_header_row
|
|
918
|
+
data: str | IO[str],
|
|
919
|
+
existing_data: Literal["overwrite", "skip", "delete"] = "overwrite",
|
|
920
|
+
date_format="yyyy-mm-dd",
|
|
921
|
+
decimal_separator: Literal[".", ","] = ".",
|
|
922
|
+
ignore_errors=False,
|
|
923
|
+
ignore_duplicates=False,
|
|
924
|
+
contains_header_row=True,
|
|
855
925
|
):
|
|
856
926
|
"""
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
927
|
+
Upload data series data.
|
|
928
|
+
|
|
929
|
+
Uploads delimited content to the specified data series.
|
|
930
|
+
|
|
931
|
+
The data must contain dates in the first column and values in the second column.
|
|
932
|
+
If the data includes a header row, the names are not processed.
|
|
933
|
+
|
|
934
|
+
The ``value`` column may specify ``na``, ``nan``, or ``null`` (case-insensitive) to clear a prior value on an observation date.
|
|
935
|
+
|
|
936
|
+
Example input::
|
|
937
|
+
|
|
938
|
+
date,value
|
|
939
|
+
2026-01-31,1.25
|
|
940
|
+
2026-02-28,na
|
|
941
|
+
|
|
942
|
+
Args:
|
|
943
|
+
series_id: Unique identifier of the data series.
|
|
944
|
+
data: Delimited content string or file-like containing delimited content. Must not exceed 100 MB.
|
|
945
|
+
existing_data: Policy for dealing with collisions against stored dates. Defaults to ``overwrite``.
|
|
946
|
+
- ``overwrite``: Overwrite stored values.
|
|
947
|
+
- ``skip``: Retaine stored values.
|
|
948
|
+
- ``delete``: Clear before storing uploaded data.
|
|
949
|
+
date_format: Date format. Defaults to ``yyyy-mm-dd``.
|
|
950
|
+
decimal_separator: Decimal separator. Defaults to period. If comma is used, the thousands separator, if used, is assumed to be period.
|
|
951
|
+
ignore_errors: If ``True``, lines in the data with errors will be silently discarded.
|
|
952
|
+
ignore_duplicates: If ``True``, additional occurrences of a date in the data are skipped.
|
|
953
|
+
contains_header_row: If ``True``, the first line of the uploaded data will be skipped.
|
|
954
|
+
"""
|
|
955
|
+
get_params = [
|
|
956
|
+
("existingData", existing_data),
|
|
957
|
+
("dateFormat", date_format),
|
|
958
|
+
("decimalSeparator", decimal_separator),
|
|
959
|
+
("onError", "continue" if ignore_errors else "stop"),
|
|
960
|
+
("onDuplicates", "continue" if ignore_duplicates else "stop"),
|
|
961
|
+
("headerRow", contains_header_row),
|
|
962
|
+
]
|
|
881
963
|
return self._req_with_auth_fallback(
|
|
882
964
|
url=self._endpoint + DATA_SERIES_UPLOAD_PATH.substitute(id=series_id), params=get_params, data=data
|
|
883
965
|
)
|
|
@@ -888,7 +970,7 @@ class Client:
|
|
|
888
970
|
:param params:
|
|
889
971
|
:return:
|
|
890
972
|
"""
|
|
891
|
-
return self._req_with_auth_fallback(url=self._endpoint + DATA_SERIES_CREATE_UPDATE_PATH, json=params)
|
|
973
|
+
return self._req_with_auth_fallback(url=self._endpoint + DATA_SERIES_CREATE_UPDATE_PATH, json=params, result_type=DataSeriesResult)
|
|
892
974
|
|
|
893
975
|
def data_series_delete(self, series_id: int):
|
|
894
976
|
"""
|
|
@@ -911,6 +993,8 @@ class Client:
|
|
|
911
993
|
ret = self._req_with_auth_fallback(url=self._endpoint + AIFACTOR_PREDICT_PATH.substitute(id=predictor_id), json=params)
|
|
912
994
|
|
|
913
995
|
if to_pandas:
|
|
996
|
+
import pandas
|
|
997
|
+
|
|
914
998
|
data = {"p123Uid": ret["p123Uids"], "ticker": ret["tickers"]}
|
|
915
999
|
if "names" in ret:
|
|
916
1000
|
data["name"] = ret["names"]
|
|
@@ -939,7 +1023,7 @@ class Client:
|
|
|
939
1023
|
"""
|
|
940
1024
|
return self._req_with_auth_fallback(method="GET", url=self._endpoint + STOCK_FACTOR_DOWNLOAD_PATH.substitute(id=factor_id))
|
|
941
1025
|
|
|
942
|
-
def data_prices(self, identifier:
|
|
1026
|
+
def data_prices(self, identifier: int | str, start: str, end: str | None, to_pandas=False):
|
|
943
1027
|
""" """
|
|
944
1028
|
get_params = [("start", start)]
|
|
945
1029
|
if end is not None:
|
|
@@ -947,7 +1031,13 @@ class Client:
|
|
|
947
1031
|
ret = self._req_with_auth_fallback(
|
|
948
1032
|
method="GET", url=self._endpoint + DATA_PRICES_PATH.substitute(identifier=identifier), params=get_params
|
|
949
1033
|
)
|
|
950
|
-
|
|
1034
|
+
|
|
1035
|
+
if to_pandas:
|
|
1036
|
+
import pandas
|
|
1037
|
+
|
|
1038
|
+
return pandas.DataFrame(ret["prices"])
|
|
1039
|
+
|
|
1040
|
+
return ret
|
|
951
1041
|
|
|
952
1042
|
@overload
|
|
953
1043
|
def stock_factor_info(self, *, id: int) -> StockFactorInfoResult:
|
|
@@ -963,9 +1053,9 @@ class Client:
|
|
|
963
1053
|
Examples:
|
|
964
1054
|
>>> client.stock_factor_info(id=123)
|
|
965
1055
|
{
|
|
966
|
-
factorId: 123,
|
|
967
|
-
name: 'Stock factor name',
|
|
968
|
-
description: 'Stock factor description'
|
|
1056
|
+
'factorId': 123,
|
|
1057
|
+
'name': 'Stock factor name',
|
|
1058
|
+
'description': 'Stock factor description'
|
|
969
1059
|
}
|
|
970
1060
|
"""
|
|
971
1061
|
...
|
|
@@ -984,9 +1074,9 @@ class Client:
|
|
|
984
1074
|
Examples:
|
|
985
1075
|
>>> client.stock_factor_info(name='Stock factor name')
|
|
986
1076
|
{
|
|
987
|
-
factorId: 123,
|
|
988
|
-
name: 'Stock factor name',
|
|
989
|
-
description: 'Stock factor description'
|
|
1077
|
+
'factorId': 123,
|
|
1078
|
+
'name': 'Stock factor name',
|
|
1079
|
+
'description': 'Stock factor description'
|
|
990
1080
|
}
|
|
991
1081
|
"""
|
|
992
1082
|
...
|
|
@@ -1009,23 +1099,23 @@ class Client:
|
|
|
1009
1099
|
Examples:
|
|
1010
1100
|
>>> client.stock_factor_info(factor_id=123)
|
|
1011
1101
|
{
|
|
1012
|
-
factorId: 123,
|
|
1013
|
-
name: 'Stock factor name',
|
|
1014
|
-
description: 'Stock factor description'
|
|
1102
|
+
'factorId': 123,
|
|
1103
|
+
'name': 'Stock factor name',
|
|
1104
|
+
'description': 'Stock factor description'
|
|
1015
1105
|
}
|
|
1016
1106
|
"""
|
|
1017
1107
|
...
|
|
1018
1108
|
|
|
1019
|
-
def stock_factor_info(
|
|
1020
|
-
self, *, id: Optional[int] = None, factor_id: Optional[int] = None, name: Optional[str] = None
|
|
1021
|
-
) -> StockFactorInfoResult:
|
|
1109
|
+
def stock_factor_info(self, *, id: int | None = None, factor_id: int | None = None, name: str | None = None) -> StockFactorInfoResult:
|
|
1022
1110
|
if id is not None:
|
|
1023
|
-
params =
|
|
1111
|
+
params = [("id", id)]
|
|
1024
1112
|
elif factor_id is not None:
|
|
1025
|
-
params =
|
|
1113
|
+
params = [("id", factor_id)]
|
|
1026
1114
|
else:
|
|
1027
|
-
params =
|
|
1028
|
-
return self._req_with_auth_fallback(
|
|
1115
|
+
params = [("name", name)]
|
|
1116
|
+
return self._req_with_auth_fallback(
|
|
1117
|
+
method="GET", url=self._endpoint + STOCK_FACTOR_INFO_PATH, params=params, result_type=StockFactorInfoResult
|
|
1118
|
+
)
|
|
1029
1119
|
|
|
1030
1120
|
@overload
|
|
1031
1121
|
def data_series_info(self, *, id: int) -> DataSeriesInfoResult:
|
|
@@ -1041,9 +1131,9 @@ class Client:
|
|
|
1041
1131
|
Examples:
|
|
1042
1132
|
>>> client.data_series_info(id=123)
|
|
1043
1133
|
{
|
|
1044
|
-
dataSeriesId: 123,
|
|
1045
|
-
name: 'Data series name',
|
|
1046
|
-
description: 'Data series description'
|
|
1134
|
+
'dataSeriesId': 123,
|
|
1135
|
+
'name': 'Data series name',
|
|
1136
|
+
'description': 'Data series description'
|
|
1047
1137
|
}
|
|
1048
1138
|
"""
|
|
1049
1139
|
...
|
|
@@ -1062,16 +1152,19 @@ class Client:
|
|
|
1062
1152
|
Examples:
|
|
1063
1153
|
>>> client.data_series_info(name='Data series name')
|
|
1064
1154
|
{
|
|
1065
|
-
dataSeriesId: 123,
|
|
1066
|
-
name: 'Data series name',
|
|
1067
|
-
description: 'Data series description'
|
|
1155
|
+
'dataSeriesId': 123,
|
|
1156
|
+
'name': 'Data series name',
|
|
1157
|
+
'description': 'Data series description'
|
|
1068
1158
|
}
|
|
1069
1159
|
"""
|
|
1070
1160
|
...
|
|
1071
1161
|
|
|
1072
|
-
def data_series_info(self, *, id:
|
|
1162
|
+
def data_series_info(self, *, id: int | None = None, name: str | None = None) -> DataSeriesInfoResult:
|
|
1073
1163
|
return self._req_with_auth_fallback(
|
|
1074
|
-
method="GET",
|
|
1164
|
+
method="GET",
|
|
1165
|
+
url=self._endpoint + DATA_SERIES_INFO_PATH,
|
|
1166
|
+
params=[("name", name)] if id is None else [("id", id)],
|
|
1167
|
+
result_type=DataSeriesInfoResult,
|
|
1075
1168
|
)
|
|
1076
1169
|
|
|
1077
1170
|
@overload
|
|
@@ -1088,9 +1181,9 @@ class Client:
|
|
|
1088
1181
|
Examples:
|
|
1089
1182
|
>>> client.strategy_info(id=123)
|
|
1090
1183
|
{
|
|
1091
|
-
strategyId: 123,
|
|
1092
|
-
name: 'Strategy name',
|
|
1093
|
-
description: 'Strategy description'
|
|
1184
|
+
'strategyId': 123,
|
|
1185
|
+
'name': 'Strategy name',
|
|
1186
|
+
'description': 'Strategy description'
|
|
1094
1187
|
}
|
|
1095
1188
|
"""
|
|
1096
1189
|
...
|
|
@@ -1109,16 +1202,19 @@ class Client:
|
|
|
1109
1202
|
Examples:
|
|
1110
1203
|
>>> client.strategy_info(name='Strategy name')
|
|
1111
1204
|
{
|
|
1112
|
-
strategyId: 123,
|
|
1113
|
-
name: 'Strategy name',
|
|
1114
|
-
description: 'Strategy description'
|
|
1205
|
+
'strategyId': 123,
|
|
1206
|
+
'name': 'Strategy name',
|
|
1207
|
+
'description': 'Strategy description'
|
|
1115
1208
|
}
|
|
1116
1209
|
"""
|
|
1117
1210
|
...
|
|
1118
1211
|
|
|
1119
|
-
def strategy_info(self, *, id:
|
|
1212
|
+
def strategy_info(self, *, id: int | None = None, name: str | None = None) -> StrategyInfoResult:
|
|
1120
1213
|
return self._req_with_auth_fallback(
|
|
1121
|
-
method="GET",
|
|
1214
|
+
method="GET",
|
|
1215
|
+
url=self._endpoint + STRATEGY_INFO_PATH,
|
|
1216
|
+
params=[("name", name)] if id is None else [("id", id)],
|
|
1217
|
+
result_type=StrategyInfoResult,
|
|
1122
1218
|
)
|
|
1123
1219
|
|
|
1124
1220
|
|
|
@@ -1140,4 +1236,4 @@ def req_with_retry(req: Callable[..., requests.Response], max_tries=5, **kwargs)
|
|
|
1140
1236
|
tries += 1
|
|
1141
1237
|
if tries >= max_tries:
|
|
1142
1238
|
break
|
|
1143
|
-
raise ClientException("Cannot connect to API"
|
|
1239
|
+
raise ClientException("Cannot connect to API") from exception
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from enum import Enum, IntEnum
|
|
2
|
+
import inspect
|
|
3
|
+
import typing
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _create_fn(name, args, body, globals_dict=None):
|
|
8
|
+
"""Executes a string of code to build a highly optimized function."""
|
|
9
|
+
args_str = ", ".join(args)
|
|
10
|
+
body_str = "\n ".join(body)
|
|
11
|
+
|
|
12
|
+
txt = f"""
|
|
13
|
+
def __builder__():
|
|
14
|
+
def {name}({args_str}):
|
|
15
|
+
{body_str}
|
|
16
|
+
return {name}
|
|
17
|
+
"""
|
|
18
|
+
namespace = {}
|
|
19
|
+
exec(txt, globals_dict or {}, namespace)
|
|
20
|
+
return namespace["__builder__"]()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _slow_init(self, d: dict):
|
|
24
|
+
cls = type(self)
|
|
25
|
+
annotations = typing.get_type_hints(cls)
|
|
26
|
+
|
|
27
|
+
globals_dict = {}
|
|
28
|
+
for hint in annotations.values():
|
|
29
|
+
if inspect.isclass(hint) and issubclass(hint, Enum):
|
|
30
|
+
globals_dict[hint.__name__] = hint
|
|
31
|
+
|
|
32
|
+
init_args = ["self", "d"]
|
|
33
|
+
init_body = []
|
|
34
|
+
|
|
35
|
+
for key, expected_type in annotations.items():
|
|
36
|
+
if inspect.isclass(expected_type) and issubclass(expected_type, Enum):
|
|
37
|
+
init_body.append(f"self.{key} = {expected_type.__name__}(v) if (v := d.get({key!r})) is not None else None")
|
|
38
|
+
else:
|
|
39
|
+
init_body.append(f"self.{key} = d.get({key!r})")
|
|
40
|
+
|
|
41
|
+
init = cls.__init__ = _create_fn("__init__", init_args, init_body, globals_dict)
|
|
42
|
+
init(self, d)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _slow_repr(self):
|
|
46
|
+
cls = type(self)
|
|
47
|
+
annotations = typing.get_type_hints(cls)
|
|
48
|
+
|
|
49
|
+
repr = cls.__repr__ = _create_fn(
|
|
50
|
+
"__repr__", ["self"], [f"return f'{cls.__name__}({', '.join(f'{k}={{self.{k}!r}}' for k in annotations)})'"]
|
|
51
|
+
)
|
|
52
|
+
return repr(self)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def api_result(cls):
|
|
56
|
+
cls.__init__ = _slow_init
|
|
57
|
+
cls.__repr__ = _slow_repr
|
|
58
|
+
return cls
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@api_result
|
|
62
|
+
class IdResult:
|
|
63
|
+
id: int
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@api_result
|
|
67
|
+
class DataSeriesResult:
|
|
68
|
+
dataSeriesId: int
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@api_result
|
|
72
|
+
class DataSeriesInfoResult:
|
|
73
|
+
dataSeriesId: int
|
|
74
|
+
name: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@api_result
|
|
78
|
+
class StockFactorResult:
|
|
79
|
+
factorId: int
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@api_result
|
|
83
|
+
class StockFactorInfoResult:
|
|
84
|
+
factorId: int
|
|
85
|
+
name: str
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class RankingMethod(IntEnum):
|
|
89
|
+
PERCENTILE_NA_NEGATIVE = 2
|
|
90
|
+
PERCENTILE_NA_NEUTRAL = 4
|
|
91
|
+
NORMAL_DISTRIBUTION = 1
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@api_result
|
|
95
|
+
class RankInfoResult:
|
|
96
|
+
name: str
|
|
97
|
+
id: int
|
|
98
|
+
xml: str
|
|
99
|
+
currency: str
|
|
100
|
+
rankingMethod: RankingMethod
|
|
101
|
+
type: Literal["Stock", "ETF"]
|
|
102
|
+
description: str | None
|
|
103
|
+
groupUid: int
|
|
104
|
+
resolveGroupUid: int
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@api_result
|
|
108
|
+
class StrategyInfoResult:
|
|
109
|
+
strategyId: int
|
|
110
|
+
name: str
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: p123api
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0a2
|
|
4
4
|
Summary: Portfolio123 API wrapper
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: UNKNOWN
|
|
9
|
-
Platform: UNKNOWN
|
|
5
|
+
Author-email: Portfolio123 <info@portfolio123.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/portfolio-123/p123api-py
|
|
10
8
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
12
9
|
Classifier: Operating System :: OS Independent
|
|
13
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
14
11
|
Description-Content-Type: text/markdown
|
|
15
12
|
License-File: LICENSE
|
|
13
|
+
Requires-Dist: requests
|
|
14
|
+
Requires-Dist: typing_extensions
|
|
15
|
+
Provides-Extra: pandas
|
|
16
|
+
Requires-Dist: pandas; extra == "pandas"
|
|
17
|
+
Dynamic: license-file
|
|
16
18
|
|
|
17
19
|
# Portfolio123 API Wrapper
|
|
18
20
|
|
|
@@ -25,5 +27,3 @@ with p123api.Client(api_id='your api id', api_key='your api key') as client:
|
|
|
25
27
|
except p123api.ClientException as e:
|
|
26
28
|
print(e)
|
|
27
29
|
```
|
|
28
|
-
|
|
29
|
-
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "p123api"
|
|
7
|
+
version = "3.0.0a2"
|
|
8
|
+
description = "Portfolio123 API wrapper"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Portfolio123", email = "info@portfolio123.com"}
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
dependencies = [
|
|
17
|
+
"requests",
|
|
18
|
+
"typing_extensions",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
pandas = [
|
|
28
|
+
"pandas",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/portfolio-123/p123api-py"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
include = ["p123api*"]
|
|
36
|
+
|
|
37
|
+
[tool.black]
|
|
38
|
+
line-length = 140
|
|
39
|
+
skip-magic-trailing-comma = true
|
|
40
|
+
|
|
41
|
+
[tool.pyright]
|
|
42
|
+
pythonVersion = "3.10"
|
|
43
|
+
typeCheckingMode = "basic"
|
|
44
|
+
deprecateTypingAliases = true
|
|
45
|
+
disableBytesTypePromotions = true
|
|
46
|
+
strictDictionaryInference = true
|
|
47
|
+
strictListInference = true
|
|
48
|
+
strictSetInference = true
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .client import Client, ClientException, ClientItemNotFoundException
|
p123api-2.4.2/p123api/types.py
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
from enum import IntEnum
|
|
2
|
-
from typing import Literal, Optional, TypedDict
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class SharedResult(TypedDict):
|
|
6
|
-
cost: int
|
|
7
|
-
quotaRemaining: str
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class IdResult(SharedResult):
|
|
11
|
-
id: int
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class DataSeriesResult(SharedResult):
|
|
15
|
-
dataSeriesId: int
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class DataSeriesInfoResult(DataSeriesResult):
|
|
19
|
-
name: str
|
|
20
|
-
description: str
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class StockFactorResult(SharedResult):
|
|
24
|
-
factorId: int
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class StockFactorInfoResult(StockFactorResult):
|
|
28
|
-
name: str
|
|
29
|
-
description: str
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class RankInfoResult(SharedResult):
|
|
33
|
-
name: str
|
|
34
|
-
id: int
|
|
35
|
-
xml: str
|
|
36
|
-
currency: str
|
|
37
|
-
rankingMethod: int
|
|
38
|
-
type: Literal["Stock", "ETF"]
|
|
39
|
-
description: Optional[str]
|
|
40
|
-
groupUid: int
|
|
41
|
-
resolveGroupUid: int
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class RankingMethod(IntEnum):
|
|
45
|
-
PERCENTILE_NA_NEGATIVE = 2
|
|
46
|
-
PERCENTILE_NA_NEUTRAL = 4
|
|
47
|
-
NORMAL_DISTRIBUTION = 1
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class StrategyInfoResult(SharedResult):
|
|
51
|
-
strategyId: int
|
|
52
|
-
name: str
|
|
53
|
-
description: str
|
p123api-2.4.2/pyproject.toml
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
[tool.black]
|
|
2
|
-
line-length = 140
|
|
3
|
-
skip-magic-trailing-comma = true
|
|
4
|
-
|
|
5
|
-
[tool.pyright]
|
|
6
|
-
pythonVersion = "3.6"
|
|
7
|
-
typeCheckingMode = "basic"
|
|
8
|
-
deprecateTypingAliases = true
|
|
9
|
-
disableBytesTypePromotions = true
|
|
10
|
-
strictDictionaryInference = true
|
|
11
|
-
strictListInference = true
|
|
12
|
-
strictSetInference = true
|
p123api-2.4.2/setup.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import setuptools
|
|
2
|
-
|
|
3
|
-
with open("README.md", "r") as fh:
|
|
4
|
-
long_description = fh.read()
|
|
5
|
-
|
|
6
|
-
setuptools.setup(
|
|
7
|
-
name="p123api",
|
|
8
|
-
version="2.4.2",
|
|
9
|
-
author="Portfolio123",
|
|
10
|
-
author_email="info@portfolio123.com",
|
|
11
|
-
description="Portfolio123 API wrapper",
|
|
12
|
-
long_description=long_description,
|
|
13
|
-
long_description_content_type="text/markdown",
|
|
14
|
-
url="https://github.com/portfolio-123/p123api-py",
|
|
15
|
-
packages=setuptools.find_packages(),
|
|
16
|
-
classifiers=["Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent"],
|
|
17
|
-
python_requires=">=3.6",
|
|
18
|
-
install_requires=["pandas", "requests"],
|
|
19
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|