investfly-sdk 1.4__py3-none-any.whl → 1.6__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.
@@ -9,13 +9,24 @@ class IndicatorApiClient:
9
9
  def __init__(self, restApiClient: RestApiClient) -> None:
10
10
  self.restApiClient = restApiClient
11
11
 
12
- def listIndicators(self) -> List[IndicatorSpec]:
12
+ def listCustomIndicators(self) -> List[IndicatorSpec]:
13
13
  indicatorsListDict = self.restApiClient.doGet('/indicator/list/custom')
14
14
  result: List[IndicatorSpec] = []
15
15
  for indicatorDict in indicatorsListDict:
16
16
  result.append(IndicatorSpec.fromDict(indicatorDict))
17
17
  return result
18
18
 
19
+ def listStandardIndicators(self) -> List[IndicatorSpec]:
20
+ indicatorsListDict = self.restApiClient.doGet('/indicator/list/standard')
21
+ result: List[IndicatorSpec] = []
22
+ for indicatorDict in indicatorsListDict:
23
+ result.append(IndicatorSpec.fromDict(indicatorDict))
24
+ return result
25
+
26
+ def getIndicatorSpec(self, indicatorId: str) -> IndicatorSpec:
27
+ specDict = self.restApiClient.doGet(f'/indicator/{indicatorId}')
28
+ return IndicatorSpec.fromDict(specDict)
29
+
19
30
  def getIndicatorCode(self, indicatorId: str) -> str:
20
31
  return self.restApiClient.doGet(f'/indicator/custom/{indicatorId}/code')
21
32
 
@@ -19,11 +19,12 @@ class RestApiClient:
19
19
 
20
20
  def __init__(self, baseUrl: str) -> None:
21
21
  self.headers: Dict[str, str] = {}
22
+ self.headers['client-mode'] = 'api'
22
23
  self.baseUrl = baseUrl
23
24
  self.log = logging.getLogger(self.__class__.__name__)
24
25
 
25
26
  def login(self, username: str, password: str) -> Session:
26
- res = requests.post(self.baseUrl + "/user/login", auth=(username, password), verify=False)
27
+ res = requests.post(self.baseUrl + "/user/login", auth=(username, password), headers=self.headers, verify=False)
27
28
  if res.status_code == 200:
28
29
  self.headers['investfly-client-id'] = res.headers['investfly-client-id']
29
30
  self.headers['investfly-client-token'] = res.headers['investfly-client-token']
investfly/api/py.typed ADDED
File without changes
@@ -1,6 +1,4 @@
1
1
  import argparse
2
- import pickle
3
- import os.path
4
2
  import time
5
3
  from typing import List, cast
6
4
 
@@ -12,6 +10,8 @@ from investfly import samples
12
10
  import inspect
13
11
  from pathlib import Path
14
12
  import shutil
13
+ import json
14
+ import re
15
15
 
16
16
 
17
17
  class InvestflyCli:
@@ -20,6 +20,15 @@ class InvestflyCli:
20
20
  self.running: bool = True
21
21
  self.investflyApi = InvestflyApiClient()
22
22
 
23
+ @staticmethod
24
+ def extract_class_name(source_code: str, base_class: str) -> str|None:
25
+ class_pattern = re.compile(r'class\s+(\w+)\s*\(\s*' + base_class + r'\s*\)\s*:', re.IGNORECASE)
26
+ for line in source_code.splitlines():
27
+ match = class_pattern.search(line)
28
+ if match:
29
+ return match.group(1)
30
+ return None
31
+
23
32
 
24
33
  def __loginAction(self, args: argparse.Namespace) -> Session:
25
34
  username = args.username
@@ -51,10 +60,12 @@ class InvestflyCli:
51
60
  return "\n".join(strategiesDictList)
52
61
 
53
62
  def __createStrategy(self, args: argparse.Namespace) -> str:
54
- name = args.name
55
63
  path = args.file
56
64
  with open(path, 'r') as source_file:
57
65
  code = source_file.read()
66
+ name = InvestflyCli.extract_class_name(code, "TradingStrategy")
67
+ if name is None:
68
+ return "Provided file does not contain class that extends from TradingStrategy"
58
69
  tradingStrategyModel = TradingStrategyModel(strategyName=name, strategyDesc=name, pythonCode=code)
59
70
  tradingStrategyModel = self.investflyApi.strategyApi.createStrategy(tradingStrategyModel)
60
71
  return f'Created strategy {tradingStrategyModel.strategyId}'
@@ -106,16 +117,28 @@ class InvestflyCli:
106
117
 
107
118
  # ==== INDICATOR COMMAND HANDLERS
108
119
 
109
- def __listIndicators(self, args: argparse.Namespace) -> str:
110
- indicators: List[IndicatorSpec] = self.investflyApi.indicatorApi.listIndicators()
120
+ def __listCustomIndicators(self, args: argparse.Namespace) -> str:
121
+ indicators: List[IndicatorSpec] = self.investflyApi.indicatorApi.listCustomIndicators()
122
+ idList = list(map(lambda spec: spec.indicatorId, indicators))
123
+ return str(idList)
124
+
125
+ def __listStandardIndicators(self, args: argparse.Namespace) -> str:
126
+ indicators: List[IndicatorSpec] = self.investflyApi.indicatorApi.listStandardIndicators()
111
127
  idList = list(map(lambda spec: spec.indicatorId, indicators))
112
128
  return str(idList)
113
129
 
130
+ def __getIndicatorSpec(self, args: argparse.Namespace) -> str:
131
+ spec = self.investflyApi.indicatorApi.getIndicatorSpec(args.id)
132
+ jsonDict = spec.toJsonDict()
133
+ return json.dumps(jsonDict, indent=2)
134
+
114
135
  def __createUpdateIndicator(self, args: argparse.Namespace):
115
136
  path = args.file
116
137
  with open(path, 'r') as source_file:
117
138
  code = source_file.read()
118
- self.investflyApi.indicatorApi.createUpdateIndicator(code)
139
+ spec = self.investflyApi.indicatorApi.createUpdateIndicator(code)
140
+ jsonDict = spec.toJsonDict()
141
+ return json.dumps(jsonDict, indent=2)
119
142
 
120
143
  def __downloadIndicatorCode(self, args: argparse.Namespace):
121
144
  indicatorId = args.id
@@ -149,7 +172,6 @@ class InvestflyCli:
149
172
  parser_listStrategies.set_defaults(func=self.__listStrategies)
150
173
 
151
174
  parser_createStrategy = subparser.add_parser('strategy.create', help='Create a new trading strategy')
152
- parser_createStrategy.add_argument('-n', '--name', required=True, help='Strategy Name')
153
175
  parser_createStrategy.add_argument('-f', '--file', required=True, help='Python File Path relative to the project root that contains strategy code')
154
176
  parser_createStrategy.set_defaults(func=self.__createStrategy)
155
177
 
@@ -177,15 +199,22 @@ class InvestflyCli:
177
199
 
178
200
  # ====== INDICATOR COMMANDS ====
179
201
 
180
- parser_listIndicators = subparser.add_parser('indicator.list', help='List Custom Indicators')
181
- parser_listIndicators.set_defaults(func=self.__listIndicators)
202
+ parser_listCustomIndicators = subparser.add_parser('indicator.listCustom', help='List Custom Indicators')
203
+ parser_listCustomIndicators.set_defaults(func=self.__listCustomIndicators)
204
+
205
+ parser_listStandardIndicators = subparser.add_parser('indicator.listStandard', help='List Custom Indicators')
206
+ parser_listStandardIndicators.set_defaults(func=self.__listStandardIndicators)
207
+
208
+ parser_getIndicatorSpec = subparser.add_parser('indicator.getIndicator', help="Get Indicator Specification")
209
+ parser_getIndicatorSpec.add_argument('-i', '--id', required=True, help='Indicator ID')
210
+ parser_getIndicatorSpec.set_defaults(func=self.__getIndicatorSpec)
182
211
 
183
212
  parser_downloadIndicator = subparser.add_parser('indicator.download', help='Download indicator python code and save it to a file')
184
- parser_downloadIndicator.add_argument('-i', '--id', required=True, help='IndicatorId ID')
213
+ parser_downloadIndicator.add_argument('-i', '--id', required=True, help='Indicator ID')
185
214
  parser_downloadIndicator.add_argument('-f', '--file', required=True, help='File path (with file name) to save indicator python code')
186
215
  parser_downloadIndicator.set_defaults(func=self.__downloadIndicatorCode)
187
216
 
188
- parser_createUpdateIndicator = subparser.add_parser('indicator.update', help='Create or update indicator. Indicator ID is retried from ClassName')
217
+ parser_createUpdateIndicator = subparser.add_parser('indicator.createupdate', help='Create or update indicator. Indicator ID is retried from ClassName')
189
218
  parser_createUpdateIndicator.add_argument('-f', '--file', required=True, help='File path (with file name) that contains indicator code')
190
219
  parser_createUpdateIndicator.set_defaults(func=self.__createUpdateIndicator)
191
220
 
@@ -15,7 +15,8 @@ class ModelUtils:
15
15
 
16
16
  @staticmethod
17
17
  def parseDatetime(date_str: str) -> datetime:
18
- dt = datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%f')
18
+ dateFormat = '%Y-%m-%dT%H:%M:%S.%f' if "." in date_str else '%Y-%m-%dT%H:%M:%S'
19
+ dt = datetime.strptime(date_str, dateFormat)
19
20
  dt = dt.astimezone(ModelUtils.est_tz)
20
21
  return dt
21
22
 
File without changes
@@ -11,7 +11,7 @@ from typing import Any, List, Dict
11
11
  import math
12
12
  import statistics
13
13
  import numpy as np
14
- import talib
14
+ import talib # type: ignore
15
15
  import pandas
16
16
 
17
17
  # ! WARN ! Imports other than listed above are disallowed and won't pass validation
@@ -1,7 +1,7 @@
1
1
  import datetime
2
2
  from typing import Any, List, Dict
3
3
  import numpy as np
4
- import talib
4
+ import talib # type: ignore
5
5
 
6
6
  from investfly.models import *
7
7
  from investfly.utils import *
@@ -14,14 +14,10 @@ from investfly.utils import *
14
14
 
15
15
  class RsiOfSma(Indicator):
16
16
 
17
-
18
- # To implement a price-based indicator (i.e. indicator that uses price bars [open,high,low,close,volume]),
19
- # you extend from PriceBasedIndicator and implement methods shown below
20
-
21
17
  def getIndicatorSpec(self) -> IndicatorSpec:
22
- # In this method, you must construct and return IndicatorDefinition object that specifies
23
- # indicator name, description and any parameters it needs. BarSize parameter is automatically
24
- # added from the parent class
18
+ # In this method, you must construct and return IndicatorSpec object that specifies
19
+ # indicator name, description and any parameters it needs. Stanard parameters like (barinterval, count, lookback)
20
+ # are automatically added
25
21
  indicator = IndicatorSpec("RSI of SMA")
26
22
  indicator.addParam("sma_period", IndicatorParamSpec(ParamType.INTEGER, True, 5, IndicatorParamSpec.PERIOD_VALUES))
27
23
  indicator.addParam("rsi_period", IndicatorParamSpec(ParamType.INTEGER, True, 10, IndicatorParamSpec.PERIOD_VALUES))
@@ -1,7 +1,7 @@
1
1
  from typing import Any, List, Dict
2
2
 
3
3
  import numpy as np
4
- import talib
4
+ import talib # type: ignore
5
5
 
6
6
  from investfly.models import *
7
7
  from investfly.utils import *
@@ -12,13 +12,10 @@ from investfly.utils import *
12
12
 
13
13
  class SmaEmaAverage(Indicator):
14
14
 
15
- # To implement a price-based indicator (i.e indicator that uses price bars [open,high,low,close,volume]) in
16
- # computation you extend from PriceBasedIndicator and implement methods shown below
17
-
18
15
  def getIndicatorSpec(self) -> IndicatorSpec:
19
- # In this method, you must construct and return IndicatorDefinition object that spefies
20
- # indicator name, description and any parameters it needs. BarSize parameter is automatically
21
- # added from the parent class
16
+ # In this method, you must construct and return IndicatorSpec object that specifies
17
+ # indicator name, description and any parameters it needs. Stanard parameters like (barinterval, count, lookback)
18
+ # are automatically added
22
19
  indicator = IndicatorSpec("SMA EMA Average")
23
20
  indicator.addParam('period', IndicatorParamSpec(ParamType.INTEGER, True, 5, IndicatorParamSpec.PERIOD_VALUES))
24
21
  return indicator
@@ -12,7 +12,9 @@ from typing import Any, List, Dict
12
12
  import math
13
13
  import statistics
14
14
  import numpy as np
15
- import talib # https://pypi.org/project/TA-Lib/
15
+
16
+ # https://pypi.org/project/TA-Lib/
17
+ import talib # type: ignore
16
18
  import pandas
17
19
  # ! WARN ! Imports other than listed above are disallowed and won't pass validation
18
20
 
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: investfly-sdk
3
- Version: 1.4
3
+ Version: 1.6
4
4
  Summary: Investfly SDK
5
5
  Author-email: "Investfly.com" <admin@investfly.com>
6
6
  License: The MIT License (MIT)
@@ -46,6 +46,7 @@ Requires-Dist: tzdata ==2023.3
46
46
  Requires-Dist: urllib3 ==1.26.15
47
47
  Requires-Dist: numpy ==1.26.4
48
48
  Requires-Dist: TA-Lib ==0.4.28
49
+ Requires-Dist: mypy ==1.12.1
49
50
 
50
51
  # About
51
52
 
@@ -1,39 +1,42 @@
1
1
  investfly/__init__.py,sha256=Ku0IZu4VYvNGui70Xu-bG1fB8XtVNQxBey6f5nLIKVU,71
2
- investfly/api/IndicatorApiClient.py,sha256=G2bEWaCvcmotp4jx-IoiqHblZZWAdwaUjDNP5ZoaagM,931
2
+ investfly/api/IndicatorApiClient.py,sha256=y9Dr2LpbgxcetY2s45bBGGjzUk6ZT3TzwR-vQ_TU3hw,1447
3
3
  investfly/api/InvestflyApiClient.py,sha256=I_hi1Uw8EGa3jr06cNkrGbhjIT90BPr_YN5EFilykwQ,2111
4
4
  investfly/api/MarketDataApiClient.py,sha256=bOlMzzZzN5A35oo0Iml2xCekV0jOig8Q_L66xi6n0n0,611
5
5
  investfly/api/PortfolioApiClient.py,sha256=llNISIHWSx3wvf83usGhwPhVr-3RYGAndBNpZV9w3W0,1858
6
- investfly/api/RestApiClient.py,sha256=XjvJCqAqUMPa7tXkhxGwaOj1xgHkfLcA8Q0027Z6xm8,3236
6
+ investfly/api/RestApiClient.py,sha256=ZHt_9gGZTW3krQyz2_o4w0I6SfQ6K32RTl2RcG95u3g,3302
7
7
  investfly/api/StrategyApiClient.py,sha256=w5_hQrxtQ7-1C1uu0UKL37ZbcjXmtdBAYXPtEiM-MC4,2087
8
8
  investfly/api/__init__.py,sha256=JeeOmrsPbVk4B26DT3hbvBoxAvkMEhJ-PeKeloNGF08,600
9
- investfly/cli/InvestflyCli.py,sha256=SCRqC4GpaVPB7RDzMAgr7_GpEl0ai4GF1qlBKXsjUws,10565
9
+ investfly/api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ investfly/cli/InvestflyCli.py,sha256=2dnJuGOL_qe3HeWqc0cyLss4H3HxDTXrvsHtJ6sIYSw,12114
10
11
  investfly/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
12
  investfly/models/CommonModels.py,sha256=kbyWsez1voii2K2wyflYCW7i9lYQh49nbrts4bTvvG4,2348
12
13
  investfly/models/Indicator.py,sha256=9v46Ozs2O8UPUjDzVC1MpoGx2iwnA__ZyrySVXxX8Qo,10660
13
14
  investfly/models/MarketData.py,sha256=NbyOh472uIXyHaTW9eYCBbiTu2szSq47iCz8i3qI1v0,6081
14
15
  investfly/models/MarketDataIds.py,sha256=CTNCb7G8NgLb84LYCqqxwlhO8e6RV-AUGwJH9GDv53A,3248
15
- investfly/models/ModelUtils.py,sha256=pnrVIDM26LqK0Wuw7gTs0c97lCLIV_fm0EUtlEfT7j4,964
16
+ investfly/models/ModelUtils.py,sha256=fdKO0hswgRALfSj8UvNSdtQyDo0QzDc10j3YXa0YIIA,1040
16
17
  investfly/models/PortfolioModels.py,sha256=wEHzaxEMEmliNR1OXS0WNzzao7ZA5qhKIPzAjWQuUOM,8658
17
18
  investfly/models/SecurityFilterModels.py,sha256=4baTBBI-XOKh8uTpvqVvk3unD-xHoyooO3dd5lKWbXA,5806
18
19
  investfly/models/SecurityUniverseSelector.py,sha256=N2cYhgRz3PTh6T98liiiTbJNg27SBpaUaIQGgDHFbF4,8645
19
20
  investfly/models/StrategyModels.py,sha256=n9MVOJFPtc_Wkiq5TyhdQnaiTUeXGMYqLmBE9IEiW10,5553
20
21
  investfly/models/TradingStrategy.py,sha256=N1V0dZX6T2buVyg5NZL8inAKlLZSHZnrv-x2g9kxQBk,6362
21
22
  investfly/models/__init__.py,sha256=6uMfJwcYaH1r-T-bbh6gMud0VpnoSQTkPNDVMDE3JXo,1383
23
+ investfly/models/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
24
  investfly/samples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- investfly/samples/indicators/IndicatorTemplate.py,sha256=X0AlStLnL1SBSnTwrtW_sthm00tmuN5V7N-GrtiarmM,4454
25
+ investfly/samples/indicators/IndicatorTemplate.py,sha256=Pn3jGVjMVwhqN1-F5FTAUPm4caFa3ORORNtvL353iEc,4470
24
26
  investfly/samples/indicators/NewsSentiment.py,sha256=fcpAqOcNWmqYsP-xwJquCX_6G7Ntr3A1-m31eJHAUOE,65095
25
- investfly/samples/indicators/RsiOfSma.py,sha256=kiLvMhrsbc_lV0EEpERGW2im19u5XmyJk88aTDGSBis,1719
26
- investfly/samples/indicators/SmaEmaAverage.py,sha256=9pp3TtEKJADD_bfufwrWlwMswlTLoN7Nj6BC_jJ1sRs,1749
27
+ investfly/samples/indicators/RsiOfSma.py,sha256=KewPi89ecZ0H0Ymj0FI72XgfWSvVLebuUHsfkr6WgwU,1557
28
+ investfly/samples/indicators/SmaEmaAverage.py,sha256=daoaKl9F1MWuOIOQInq9_M3P2A9E3bzFHaQ5TgsZ4ds,1576
27
29
  investfly/samples/indicators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
30
  investfly/samples/strategies/SmaCrossOverStrategy.py,sha256=e_gR8gwHJ5m6lU_Ib8jeQrER3cD6q569IIhwglKePew,1680
29
- investfly/samples/strategies/SmaCrossOverTemplate.py,sha256=rLYVT8dYDx7TKx5PFI8nN4JzTHtx4pPlnZ9JvxWnBfo,8181
31
+ investfly/samples/strategies/SmaCrossOverTemplate.py,sha256=5wDg1ecAC75ijnKs8hP6hG5EG9P58jLK_RRKTIrQnCI,8197
30
32
  investfly/samples/strategies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
33
  investfly/utils/CommonUtils.py,sha256=lCXAdI8-6PgbutcXJqUmSfuuLXp82FcnlxNVjCJpqLU,2631
32
34
  investfly/utils/PercentBasedPortfolioAllocator.py,sha256=EHrOyHbaYHLwE-4vUSQCVXwbEgLs-nDjLVRH3cdgslI,1563
33
35
  investfly/utils/__init__.py,sha256=2BqXoOQElv-GIU6wvmf2aaAABAcNny2TETcj7kf9rzM,129
34
- investfly_sdk-1.4.dist-info/LICENSE.txt,sha256=Jmd2U7G7Z1oNdnRERRzFXN6C--bEo_K56j4v9EpJSTg,1090
35
- investfly_sdk-1.4.dist-info/METADATA,sha256=V4g8ckW2EKMQw6XXoyT-MONak8Mrn-KJF113rYrl74E,7507
36
- investfly_sdk-1.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
37
- investfly_sdk-1.4.dist-info/entry_points.txt,sha256=GDRF4baJQXTh90DvdJJx1DeRezWfPt26E567lTs3g6U,66
38
- investfly_sdk-1.4.dist-info/top_level.txt,sha256=dlEJ2OGWA3prqMvXELeydS5RTdpSzh7hz1LwR3NMc7A,10
39
- investfly_sdk-1.4.dist-info/RECORD,,
36
+ investfly/utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ investfly_sdk-1.6.dist-info/LICENSE.txt,sha256=Jmd2U7G7Z1oNdnRERRzFXN6C--bEo_K56j4v9EpJSTg,1090
38
+ investfly_sdk-1.6.dist-info/METADATA,sha256=A1ebvr32y2SEMEd5jnuqHIxZMyn6kcpt9y--QDiu62c,7536
39
+ investfly_sdk-1.6.dist-info/WHEEL,sha256=a7TGlA-5DaHMRrarXjVbQagU3Man_dCnGIWMJr5kRWo,91
40
+ investfly_sdk-1.6.dist-info/entry_points.txt,sha256=GDRF4baJQXTh90DvdJJx1DeRezWfPt26E567lTs3g6U,66
41
+ investfly_sdk-1.6.dist-info/top_level.txt,sha256=dlEJ2OGWA3prqMvXELeydS5RTdpSzh7hz1LwR3NMc7A,10
42
+ investfly_sdk-1.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5