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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p123api
3
- Version: 1.9.2
3
+ Version: 2.0.0
4
4
  Summary: Portfolio123 API wrapper
5
5
  Home-page: https://github.com/portfolio-123/p123api-py
6
6
  Author: Portfolio123
@@ -3,8 +3,7 @@ import requests
3
3
  import time
4
4
  import pandas
5
5
  from string import Template
6
- import mimetypes
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 empty str")
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 empty str")
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) + f"?start={start}&end={end}",
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
- file_path: str,
558
- update_existing: bool = False,
559
- make_rebal_dt_curr: bool = False,
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
- with open(file_path, "rb") as data:
570
- get_params = []
571
- if update_existing:
572
- get_params.append("updateExisting=yes")
573
- if make_rebal_dt_curr:
574
- get_params.append("makeRebalDtCurr=yes")
575
- get_params = "?" + "&".join(get_params) if len(get_params) else ""
576
- content_type, _ = mimetypes.guess_type(file_path)
577
- headers = {'Content-Type': content_type} if content_type else {}
578
- return self._req_with_auth_fallback(
579
- name="strategy transaction import",
580
- url=self._endpoint + STRATEGY_TRANS_PATH.substitute(id=strategy_id) + get_params,
581
- data=data,
582
- headers=headers,
583
- ).json()
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
- (f"?date={date}" if date is not None else ""),
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 + STRATEGY_REBALANCE_COMMIT_PATH.substitute(id=strategy_id),
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
- file: str,
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 file:
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
- with open(file, "rb") as data:
686
- get_params = []
687
- if column_separator is not None:
688
- get_params.append(f"columnSeparator={column_separator}")
689
- if existing_data is not None:
690
- get_params.append(f"existingData={existing_data}")
691
- if date_format is not None:
692
- get_params.append(f"dateFormat={date_format}")
693
- if decimal_separator is not None:
694
- get_params.append(f"decimalSeparator={decimal_separator}")
695
- if ignore_errors is not None:
696
- get_params.append(
697
- "onError={}".format("continue" if ignore_errors else "stop")
698
- )
699
- if ignore_duplicates is not None:
700
- get_params.append(
701
- "onDuplicates={}".format(
702
- "continue" if ignore_duplicates else "stop"
703
- )
704
- )
705
- get_params = "?" + "&".join(get_params) if len(get_params) else ""
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
- file: str,
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
- with open(file, "rb") as data:
762
- get_params = []
763
- if existing_data is not None:
764
- get_params.append(f"existingData={existing_data}")
765
- if date_format is not None:
766
- get_params.append(f"dateFormat={date_format}")
767
- if decimal_separator is not None:
768
- get_params.append(f"decimalSeparator={decimal_separator}")
769
- if ignore_errors is not None:
770
- get_params.append(
771
- "onError={}".format("continue" if ignore_errors else "stop")
772
- )
773
- if ignore_duplicates is not None:
774
- get_params.append(
775
- "onDuplicates={}".format(
776
- "continue" if ignore_duplicates else "stop"
777
- )
778
- )
779
- if contains_header_row is not None:
780
- get_params.append(f"headerRow={contains_header_row}")
781
- get_params = "?" + "&".join(get_params) if len(get_params) else ""
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: p123api
3
- Version: 1.9.2
3
+ Version: 2.0.0
4
4
  Summary: Portfolio123 API wrapper
5
5
  Home-page: https://github.com/portfolio-123/p123api-py
6
6
  Author: Portfolio123
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="p123api",
8
- version="1.9.2",
8
+ version="2.0.0",
9
9
  author="Portfolio123",
10
10
  author_email="info@portfolio123.com",
11
11
  description="Portfolio123 API wrapper",
File without changes
File without changes
File without changes
File without changes