p123api 1.9.2__tar.gz → 2.0.0__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-1.9.2 → p123api-2.0.0}/PKG-INFO +1 -1
- {p123api-1.9.2 → p123api-2.0.0}/p123api/client.py +93 -113
- {p123api-1.9.2 → p123api-2.0.0}/p123api.egg-info/PKG-INFO +1 -1
- {p123api-1.9.2 → p123api-2.0.0}/setup.py +1 -1
- {p123api-1.9.2 → p123api-2.0.0}/LICENSE +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/README.md +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/p123api/__init__.py +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/p123api.egg-info/SOURCES.txt +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/p123api.egg-info/dependency_links.txt +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/p123api.egg-info/requires.txt +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/p123api.egg-info/top_level.txt +0 -0
- {p123api-1.9.2 → p123api-2.0.0}/setup.cfg +0 -0
|
@@ -3,8 +3,7 @@ import requests
|
|
|
3
3
|
import time
|
|
4
4
|
import pandas
|
|
5
5
|
from string import Template
|
|
6
|
-
import
|
|
7
|
-
from typing import List
|
|
6
|
+
from typing import IO, Callable, List, Literal, Optional, Union
|
|
8
7
|
|
|
9
8
|
|
|
10
9
|
ENDPOINT = "https://api.portfolio123.com"
|
|
@@ -62,9 +61,9 @@ class Client:
|
|
|
62
61
|
self._token = None
|
|
63
62
|
|
|
64
63
|
if not isinstance(api_id, str) or not api_id:
|
|
65
|
-
raise ClientException("api_id needs to be a non
|
|
64
|
+
raise ClientException("api_id needs to be a non-empty str")
|
|
66
65
|
if not isinstance(api_key, str) or not api_key:
|
|
67
|
-
raise ClientException("api_key needs to be a non
|
|
66
|
+
raise ClientException("api_key needs to be a non-empty str")
|
|
68
67
|
|
|
69
68
|
self._auth_params = {"apiId": api_id, "apiKey": api_key, **auth_extra}
|
|
70
69
|
self._session = requests.Session()
|
|
@@ -135,7 +134,7 @@ class Client:
|
|
|
135
134
|
data=None,
|
|
136
135
|
headers=None,
|
|
137
136
|
stop: bool = False,
|
|
138
|
-
):
|
|
137
|
+
) -> Optional[requests.Response]:
|
|
139
138
|
"""
|
|
140
139
|
Request with authentication fallback, used by all requests (except authentication)
|
|
141
140
|
:param name: request action
|
|
@@ -524,18 +523,15 @@ class Client:
|
|
|
524
523
|
:param strategy_id:
|
|
525
524
|
:return:
|
|
526
525
|
"""
|
|
526
|
+
|
|
527
527
|
return self._req_with_auth_fallback(
|
|
528
528
|
name="strategy details",
|
|
529
529
|
method="GET",
|
|
530
530
|
url=self._endpoint + STRATEGY_DETAILS_PATH.substitute(id=strategy_id),
|
|
531
531
|
).json()
|
|
532
|
-
|
|
532
|
+
|
|
533
533
|
def strategy_transactions(
|
|
534
|
-
self,
|
|
535
|
-
strategy_id: int,
|
|
536
|
-
start: str,
|
|
537
|
-
end: str,
|
|
538
|
-
to_pandas=False
|
|
534
|
+
self, strategy_id: int, start: str, end: str, to_pandas=False
|
|
539
535
|
):
|
|
540
536
|
"""
|
|
541
537
|
Strategy transactions
|
|
@@ -544,19 +540,22 @@ class Client:
|
|
|
544
540
|
:param end: end date in YYYY-MM-DD format
|
|
545
541
|
:return:
|
|
546
542
|
"""
|
|
543
|
+
|
|
547
544
|
ret = self._req_with_auth_fallback(
|
|
548
545
|
name="strategy transactions",
|
|
549
546
|
method="GET",
|
|
550
|
-
url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id)
|
|
547
|
+
url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id),
|
|
548
|
+
params=[("start", start), ("end", end)],
|
|
551
549
|
).json()
|
|
552
550
|
return pandas.DataFrame(ret["trans"]) if to_pandas else ret
|
|
553
|
-
|
|
551
|
+
|
|
554
552
|
def strategy_transaction_import(
|
|
555
553
|
self,
|
|
556
554
|
strategy_id: int,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
555
|
+
data: Union[str, IO[str]],
|
|
556
|
+
content_type: Literal["text/csv", "text/tsv"] = "text/csv",
|
|
557
|
+
update_existing=False,
|
|
558
|
+
make_rebal_dt_curr=False,
|
|
560
559
|
):
|
|
561
560
|
"""
|
|
562
561
|
Strategy transaction import
|
|
@@ -566,22 +565,22 @@ class Client:
|
|
|
566
565
|
:param make_rebal_dt_curr: if True, the rebalancing date will be set to the current date
|
|
567
566
|
:return:
|
|
568
567
|
"""
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
get_params
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
568
|
+
|
|
569
|
+
get_params = []
|
|
570
|
+
if update_existing:
|
|
571
|
+
get_params.append(("updateExisting", "1"))
|
|
572
|
+
|
|
573
|
+
if make_rebal_dt_curr:
|
|
574
|
+
get_params.append(("makeRebalDtCurr", "1"))
|
|
575
|
+
|
|
576
|
+
return self._req_with_auth_fallback(
|
|
577
|
+
name="strategy transaction import",
|
|
578
|
+
url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id),
|
|
579
|
+
params=get_params,
|
|
580
|
+
data=data,
|
|
581
|
+
headers={"Content-Type": content_type},
|
|
582
|
+
).json()
|
|
583
|
+
|
|
585
584
|
def strategy_transaction_delete(
|
|
586
585
|
self,
|
|
587
586
|
strategy_id: int,
|
|
@@ -599,12 +598,9 @@ class Client:
|
|
|
599
598
|
url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id),
|
|
600
599
|
params=params,
|
|
601
600
|
).json()
|
|
602
|
-
|
|
601
|
+
|
|
603
602
|
def strategy_holdings(
|
|
604
|
-
self,
|
|
605
|
-
strategy_id: int,
|
|
606
|
-
date: str = None,
|
|
607
|
-
to_pandas=False
|
|
603
|
+
self, strategy_id: int, date: Optional[str] = None, to_pandas=False
|
|
608
604
|
):
|
|
609
605
|
"""
|
|
610
606
|
Strategy holdings
|
|
@@ -612,26 +608,26 @@ class Client:
|
|
|
612
608
|
:param date: date in YYYY-MM-DD format, if None, current date is used
|
|
613
609
|
:return:
|
|
614
610
|
"""
|
|
611
|
+
|
|
612
|
+
get_params = [("date", date)] if date is not None else []
|
|
613
|
+
|
|
615
614
|
ret = self._req_with_auth_fallback(
|
|
616
615
|
name="strategy hldings",
|
|
617
616
|
method="GET",
|
|
618
|
-
url=self._endpoint + STRATEGY_HOLDINGS_PATH.substitute(id=strategy_id)
|
|
619
|
-
|
|
617
|
+
url=self._endpoint + STRATEGY_HOLDINGS_PATH.substitute(id=strategy_id),
|
|
618
|
+
params=get_params,
|
|
620
619
|
).json()
|
|
621
620
|
|
|
622
|
-
return pandas.DataFrame(ret) if to_pandas else ret
|
|
623
|
-
|
|
624
|
-
def strategy_rebalance(
|
|
625
|
-
self,
|
|
626
|
-
strategy_id: int,
|
|
627
|
-
params: dict
|
|
628
|
-
):
|
|
621
|
+
return pandas.DataFrame(ret["holdings"]) if to_pandas else ret
|
|
622
|
+
|
|
623
|
+
def strategy_rebalance(self, strategy_id: int, params: dict):
|
|
629
624
|
"""
|
|
630
625
|
Strategy rebalance
|
|
631
626
|
:param strategy_id:
|
|
632
627
|
:param params:
|
|
633
628
|
:return:
|
|
634
629
|
"""
|
|
630
|
+
|
|
635
631
|
ret = self._req_with_auth_fallback(
|
|
636
632
|
name="strategy rebalance",
|
|
637
633
|
url=self._endpoint + STRATEGY_REBALANCE_PATH.substitute(id=strategy_id),
|
|
@@ -639,21 +635,19 @@ class Client:
|
|
|
639
635
|
).json()
|
|
640
636
|
|
|
641
637
|
return ret
|
|
642
|
-
|
|
643
|
-
def strategy_rebalance_commit(
|
|
644
|
-
self,
|
|
645
|
-
strategy_id: int,
|
|
646
|
-
params: dict
|
|
647
|
-
):
|
|
638
|
+
|
|
639
|
+
def strategy_rebalance_commit(self, strategy_id: int, params: dict):
|
|
648
640
|
"""
|
|
649
641
|
Strategy rebalance commit
|
|
650
642
|
:param strategy_id:
|
|
651
643
|
:param params:
|
|
652
644
|
:return:
|
|
653
645
|
"""
|
|
646
|
+
|
|
654
647
|
ret = self._req_with_auth_fallback(
|
|
655
648
|
name="strategy rebalance commit",
|
|
656
|
-
url=self._endpoint
|
|
649
|
+
url=self._endpoint
|
|
650
|
+
+ STRATEGY_REBALANCE_COMMIT_PATH.substitute(id=strategy_id),
|
|
657
651
|
params=params,
|
|
658
652
|
).json()
|
|
659
653
|
|
|
@@ -662,7 +656,7 @@ class Client:
|
|
|
662
656
|
def stock_factor_upload(
|
|
663
657
|
self,
|
|
664
658
|
factor_id: int,
|
|
665
|
-
|
|
659
|
+
data: Union[str, IO[str]],
|
|
666
660
|
column_separator: str = None,
|
|
667
661
|
existing_data: str = None,
|
|
668
662
|
date_format: str = None,
|
|
@@ -673,7 +667,7 @@ class Client:
|
|
|
673
667
|
"""
|
|
674
668
|
Stock factor data upload
|
|
675
669
|
:param factor_id:
|
|
676
|
-
:param
|
|
670
|
+
:param data:
|
|
677
671
|
:param column_separator: comma, semicolon or tab
|
|
678
672
|
:param existing_data: overwrite, skip or delete
|
|
679
673
|
:param date_format: dd for day, mm for month and yyyy for year, any separator allowed (defaults to yyyy-mm-dd)
|
|
@@ -682,34 +676,27 @@ class Client:
|
|
|
682
676
|
:param ignore_duplicates:
|
|
683
677
|
:return:
|
|
684
678
|
"""
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
if ignore_errors
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
)
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return self._req_with_auth_fallback(
|
|
707
|
-
name="stock factor data upload",
|
|
708
|
-
url=self._endpoint
|
|
709
|
-
+ STOCK_FACTOR_UPLOAD_PATH.substitute(id=factor_id)
|
|
710
|
-
+ get_params,
|
|
711
|
-
data=data,
|
|
712
|
-
).json()
|
|
679
|
+
get_params = []
|
|
680
|
+
if column_separator is not None:
|
|
681
|
+
get_params.append(("columnSeparator", column_separator))
|
|
682
|
+
if existing_data is not None:
|
|
683
|
+
get_params.append(("existingData", existing_data))
|
|
684
|
+
if date_format is not None:
|
|
685
|
+
get_params.append(("dateFormat", date_format))
|
|
686
|
+
if decimal_separator is not None:
|
|
687
|
+
get_params.append(("decimalSeparator", decimal_separator))
|
|
688
|
+
if ignore_errors is not None:
|
|
689
|
+
get_params.append(("onError", "continue" if ignore_errors else "stop"))
|
|
690
|
+
if ignore_duplicates is not None:
|
|
691
|
+
get_params.append(
|
|
692
|
+
("onDuplicates", "continue" if ignore_duplicates else "stop")
|
|
693
|
+
)
|
|
694
|
+
return self._req_with_auth_fallback(
|
|
695
|
+
name="stock factor data upload",
|
|
696
|
+
url=self._endpoint + STOCK_FACTOR_UPLOAD_PATH.substitute(id=factor_id),
|
|
697
|
+
params=get_params,
|
|
698
|
+
data=data,
|
|
699
|
+
).json()
|
|
713
700
|
|
|
714
701
|
def stock_factor_create_update(self, params: dict):
|
|
715
702
|
"""
|
|
@@ -738,7 +725,7 @@ class Client:
|
|
|
738
725
|
def data_series_upload(
|
|
739
726
|
self,
|
|
740
727
|
series_id: int,
|
|
741
|
-
|
|
728
|
+
data: Union[str, IO[str]],
|
|
742
729
|
existing_data: str = None,
|
|
743
730
|
date_format: str = None,
|
|
744
731
|
decimal_separator: str = None,
|
|
@@ -758,34 +745,27 @@ class Client:
|
|
|
758
745
|
:param contains_header_row:
|
|
759
746
|
:return:
|
|
760
747
|
"""
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
if ignore_errors
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
)
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
return self._req_with_auth_fallback(
|
|
783
|
-
name="data series upload",
|
|
784
|
-
url=self._endpoint
|
|
785
|
-
+ DATA_SERIES_UPLOAD_PATH.substitute(id=series_id)
|
|
786
|
-
+ get_params,
|
|
787
|
-
data=data,
|
|
788
|
-
).json()
|
|
748
|
+
get_params = []
|
|
749
|
+
if existing_data is not None:
|
|
750
|
+
get_params.append(("existingData", existing_data))
|
|
751
|
+
if date_format is not None:
|
|
752
|
+
get_params.append(("dateFormat", date_format))
|
|
753
|
+
if decimal_separator is not None:
|
|
754
|
+
get_params.append(("decimalSeparator", decimal_separator))
|
|
755
|
+
if ignore_errors is not None:
|
|
756
|
+
get_params.append(("onError", "continue" if ignore_errors else "stop"))
|
|
757
|
+
if ignore_duplicates is not None:
|
|
758
|
+
get_params.append(
|
|
759
|
+
("onDuplicates", "continue" if ignore_duplicates else "stop")
|
|
760
|
+
)
|
|
761
|
+
if contains_header_row is not None:
|
|
762
|
+
get_params.append(("headerRow", contains_header_row))
|
|
763
|
+
return self._req_with_auth_fallback(
|
|
764
|
+
name="data series upload",
|
|
765
|
+
url=self._endpoint + DATA_SERIES_UPLOAD_PATH.substitute(id=series_id),
|
|
766
|
+
params=get_params,
|
|
767
|
+
data=data,
|
|
768
|
+
).json()
|
|
789
769
|
|
|
790
770
|
def data_series_create_update(self, params: dict):
|
|
791
771
|
"""
|
|
@@ -870,7 +850,7 @@ class Client:
|
|
|
870
850
|
).json()
|
|
871
851
|
|
|
872
852
|
|
|
873
|
-
def req_with_retry(req, max_tries=None, **kwargs):
|
|
853
|
+
def req_with_retry(req: Callable[..., requests.Response], max_tries=None, **kwargs):
|
|
874
854
|
tries = 0
|
|
875
855
|
if max_tries is None:
|
|
876
856
|
max_tries = 5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|