orionapi 2.26.0__tar.gz → 2.28.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.4
2
2
  Name: orionapi
3
- Version: 2.26.0
3
+ Version: 2.28.0
4
4
  Summary: A python interface for the Orion Advisors platform web API
5
5
  Author: spencerogden-dsam
6
6
  Author-email: 67068943+spencerogden-dsam@users.noreply.github.com
@@ -1,4 +1,4 @@
1
- __version__ = "2.26.0"
1
+ __version__ = "2.28.0"
2
2
 
3
3
  import logging
4
4
  import re
@@ -56,10 +56,16 @@ TRADE_INSTANCE_SUBTYPES = {
56
56
  }
57
57
 
58
58
  # TradingApplication mappings for the v2 /TradeOrder/Trades endpoints.
59
- # Not published in the Swagger (which lists the ints with no names) -- recovered
60
- # 2026-07-28 from the API's own rejection messages ("Trade Application <Name> is
61
- # not supported."). Only MANUAL_TRADE (11) actually creates trade orders; see
59
+ # Not published in the Swagger (which lists the ints with no names). Names 0-10
60
+ # and 12-18 were recovered 2026-07-28 from the API's own rejection messages
61
+ # ("Trade Application <Name> is not supported."); 11 is QuickTrades, confirmed
62
+ # 2026-08-03 by Orion support and verified live (the string "QuickTrades" and the
63
+ # int 11 return identical results). Only 11 creates trade orders -- see
62
64
  # EclipseV2.create_trades.
65
+ #
66
+ # These names are also accepted as *strings* in the ``application`` field, e.g.
67
+ # ``{"application": "QuickTrades"}``. A name outside this set is rejected with
68
+ # 400 "Error converting value ... to type 'OAS.Eclipse.Core.Enums.TradingApplication'".
63
69
  TRADING_APPLICATIONS = {
64
70
  0: "BuySell",
65
71
  1: "CashNeeds",
@@ -72,7 +78,7 @@ TRADING_APPLICATIONS = {
72
78
  8: "TaxLossHarvesting",
73
79
  9: "TickerSwap",
74
80
  10: "TradeToTargetPercent",
75
- 11: "ManualTrade",
81
+ 11: "QuickTrades",
76
82
  12: "TacticalTrades",
77
83
  13: "PartialShare",
78
84
  14: "(unnamed, accepted but creates nothing)",
@@ -86,7 +92,12 @@ TRADING_APPLICATIONS = {
86
92
  # POST /api/v2/TradeOrder/Trades. Every other value except 14 returns
87
93
  # 400 "Trade Application <Name> is not supported."; 14 returns 200 with an
88
94
  # empty ``tradeId`` list and creates nothing. Live-verified 2026-07-28.
89
- TRADING_APPLICATION_MANUAL = 11
95
+ TRADING_APPLICATION_QUICK_TRADES = 11
96
+
97
+ # Deprecated alias. 2.26.0 shipped this as TRADING_APPLICATION_MANUAL on the
98
+ # mistaken belief that 11 was named "ManualTrade"; the API rejects that name.
99
+ # Kept so 2.26.0/2.27.0 callers keep working -- the value is unchanged.
100
+ TRADING_APPLICATION_MANUAL = TRADING_APPLICATION_QUICK_TRADES
90
101
 
91
102
  # Set-Aside Cash Constants (from Eclipse API)
92
103
  CASH_TYPE_DOLLAR = 1
@@ -159,6 +170,31 @@ BILLING_BILL_TYPES = [
159
170
  "AdvanceCreditDebit",
160
171
  ]
161
172
 
173
+ # Fields of the Orion API ReceiveableSummaryDto, the body of
174
+ # POST /Billing/SyncCashtoEclipse. Identical to the CashFundingGridDto rows
175
+ # returned by get_cash_funding(), except the grid's "id" is "accountId" here.
176
+ RECEIVABLE_SUMMARY_FIELDS = (
177
+ "accountId",
178
+ "registrationName",
179
+ "registrationId",
180
+ "fundFamily",
181
+ "productName",
182
+ "moneyMarketBalance",
183
+ "balanceDue",
184
+ "difference",
185
+ "payMethod",
186
+ "clientId",
187
+ "clientLastName",
188
+ "clientName",
189
+ "accountType",
190
+ "accountNumber",
191
+ "managementStyle",
192
+ "accountIsActive",
193
+ "representativeName",
194
+ "feeReqSrc",
195
+ "instanceType",
196
+ )
197
+
162
198
 
163
199
  class OrionAPIError(Exception):
164
200
  """Base exception for Orion API errors."""
@@ -178,6 +214,17 @@ class NotFoundError(OrionAPIError):
178
214
  pass
179
215
 
180
216
 
217
+ class AmbiguousAccountError(OrionAPIError):
218
+ """Raised when an account search matches more than one account.
219
+
220
+ The message lists the candidate accounts (id, name, accountNumber) so the
221
+ caller can retry with an exact internal id, a full account number, or the
222
+ deliberate fuzzy workflow (``search_accounts_number_and_name``).
223
+ """
224
+
225
+ pass
226
+
227
+
181
228
  class RateLimiter:
182
229
  """Thread-safe rate limiter to prevent API abuse.
183
230
 
@@ -1655,10 +1702,13 @@ class OrionAPI(BaseAPI):
1655
1702
  if not isinstance(take, int) or take < 1:
1656
1703
  raise ValueError("take must be a positive integer")
1657
1704
 
1705
+ # forecast is a boolean: Orion's own docs claim "1 = Forecast Bill,
1706
+ # 0 = Live Bill", but the endpoint 400s on 0/1 and only accepts
1707
+ # true/false.
1658
1708
  params = {
1659
1709
  "startDate": start_date,
1660
1710
  "endDate": end_date,
1661
- "forecast": 1 if is_forecast else 0,
1711
+ "forecast": "true" if is_forecast else "false",
1662
1712
  "take": take,
1663
1713
  }
1664
1714
  if skip > 0:
@@ -1699,6 +1749,42 @@ class OrionAPI(BaseAPI):
1699
1749
  res = self.api_request(url, requests.post)
1700
1750
  return res.json()
1701
1751
 
1752
+ def sync_cash_to_eclipse(self, account):
1753
+ """Export one cash funding row to Eclipse as a cash set-aside.
1754
+
1755
+ This is the API equivalent of the Cash Funding grid's export action:
1756
+ Orion creates (or updates) a set-aside in Eclipse for the account's
1757
+ balance due, so the fee cash is reserved from trading.
1758
+
1759
+ Orion documents this as a draft endpoint that takes a single account
1760
+ per call, so exporting a whole report means looping over the rows of
1761
+ get_cash_funding().
1762
+
1763
+ Args:
1764
+ account: A cash funding row as returned by get_cash_funding().
1765
+ The row's ``id`` is the Orion account ID and is sent as
1766
+ ``accountId``. A dict already keyed ``accountId`` is also
1767
+ accepted. Keys outside ReceiveableSummaryDto are dropped.
1768
+
1769
+ Returns:
1770
+ dict: The created/updated Eclipse set-aside
1771
+ (EclipseCashFundingResponse) with id, accountId, cashAmount,
1772
+ cashAmountTypeId, description, expirationTypeId, etc.
1773
+ """
1774
+ if not isinstance(account, dict):
1775
+ raise ValueError("account must be a cash funding row dict")
1776
+
1777
+ account_id = account.get("accountId", account.get("id"))
1778
+ if account_id is None:
1779
+ raise ValueError("account must have an 'id' (or 'accountId') field")
1780
+
1781
+ payload = {k: account[k] for k in RECEIVABLE_SUMMARY_FIELDS if k in account}
1782
+ payload["accountId"] = account_id
1783
+
1784
+ url = f"{self.base_url}/Billing/SyncCashtoEclipse"
1785
+ res = self.api_request(url, requests.post, json=payload)
1786
+ return res.json()
1787
+
1702
1788
  # -------------------------------------------------------------------------
1703
1789
  # Bill Management
1704
1790
  # -------------------------------------------------------------------------
@@ -2284,30 +2370,71 @@ class EclipseBase(BaseAPI):
2284
2370
  res = self.api_request(f"{self.base_url}/account/accounts/simple")
2285
2371
  return res.json()
2286
2372
 
2287
- def get_internal_account_id(self, search_param):
2373
+ def get_internal_account_id(self, search_param, fuzzy=False):
2288
2374
  """Get internal Eclipse account ID from a search parameter.
2289
2375
 
2290
- Searches across id, accountName, accountNumber, and portfolioName.
2291
- Best use is to pass a full custodian account number.
2376
+ Searches across id, accountName, accountNumber, and portfolioName,
2377
+ then resolves in tiers:
2378
+
2379
+ 1. **Exact match:** a result whose internal ``id`` or ``accountNumber``
2380
+ equals ``search_param`` is returned immediately. If an exact-id and
2381
+ an exact-account-number match point at *different* accounts, raises
2382
+ ``AmbiguousAccountError`` rather than guessing.
2383
+ 2. A single remaining candidate is returned.
2384
+ 3. Multiple candidates raise ``AmbiguousAccountError`` listing them,
2385
+ unless ``fuzzy=True``, which returns the first match (the pre-2.28
2386
+ behavior).
2387
+
2388
+ Best use is to pass a full custodian account number. Callers that
2389
+ already hold the internal id should pass it via the
2390
+ ``internal_account_id=`` keyword on scoped methods instead of
2391
+ resolving here. For deliberate partial-number + name resolution use
2392
+ :meth:`search_accounts_number_and_name`.
2292
2393
 
2293
2394
  Args:
2294
2395
  search_param: Account number, name, or ID to search for
2396
+ fuzzy: When True, silently return the first match when multiple
2397
+ candidates remain (default False)
2295
2398
 
2296
2399
  Returns:
2297
2400
  int: The internal Eclipse account ID
2298
2401
 
2299
2402
  Raises:
2300
2403
  NotFoundError: If no matching account is found
2301
-
2302
- Note:
2303
- Returns the first matching result, which may not be expected
2304
- if multiple accounts match the search parameter.
2404
+ AmbiguousAccountError: If multiple accounts match and no exact
2405
+ match singles one out (and ``fuzzy`` is False)
2305
2406
  """
2306
2407
  res = self.search_accounts(search_param)
2307
2408
  logging.debug("search_accounts result: %s", res)
2308
2409
  if not res:
2309
2410
  raise NotFoundError(f"No account found for search: {search_param}")
2310
- return res[0]["id"]
2411
+
2412
+ def _candidates(accounts):
2413
+ return [{k: a.get(k) for k in ("id", "name", "accountNumber")} for a in accounts]
2414
+
2415
+ search_str = str(search_param)
2416
+ id_exact = [a for a in res if str(a.get("id")) == search_str]
2417
+ number_exact = [a for a in res if a.get("accountNumber") == search_str]
2418
+ exact = id_exact + [a for a in number_exact if a not in id_exact]
2419
+ if len(exact) == 1:
2420
+ return exact[0]["id"]
2421
+ if len(exact) > 1:
2422
+ raise AmbiguousAccountError(
2423
+ f"Search '{search_param}' exactly matches both an internal id and "
2424
+ f"other account numbers; pass internal_account_id= or a full "
2425
+ f"account number. Candidates: {_candidates(exact)}"
2426
+ )
2427
+
2428
+ if len(res) == 1:
2429
+ return res[0]["id"]
2430
+ if fuzzy:
2431
+ return res[0]["id"]
2432
+ raise AmbiguousAccountError(
2433
+ f"Search '{search_param}' matched {len(res)} accounts with no exact "
2434
+ f"id/accountNumber match. Pass an exact identifier, use "
2435
+ f"search_accounts_number_and_name(), or pass fuzzy=True to accept "
2436
+ f"the first match. Candidates: {_candidates(res)}"
2437
+ )
2311
2438
 
2312
2439
  def search_accounts(self, search_param):
2313
2440
  """Search for accounts by various criteria.
@@ -2403,7 +2530,7 @@ class EclipseV1(EclipseBase):
2403
2530
  res = self.api_request(f"{self.base_url}/admin/authorization/user")
2404
2531
  return res.json()["userLoginId"]
2405
2532
 
2406
- def get_set_asides(self, account_id, active_only=False):
2533
+ def get_set_asides(self, account_id=None, active_only=False, *, internal_account_id=None):
2407
2534
  """Get set-aside cash settings for a specific account (v1 surface).
2408
2535
 
2409
2536
  Returns the raw v1 records from ``GET /account/accounts/{id}/asidecash``.
@@ -2415,11 +2542,25 @@ class EclipseV1(EclipseBase):
2415
2542
  account_id: Account ID, number, or name (resolved via search).
2416
2543
  active_only: When True, keep only records where ``isActive`` is truthy.
2417
2544
  Defaults to False (includes expired/inactive).
2545
+ internal_account_id: Eclipse internal account id, used verbatim with
2546
+ no search resolution — the deterministic path for callers that
2547
+ already hold the id. Mutually exclusive with ``account_id``.
2418
2548
 
2419
2549
  Returns:
2420
2550
  list: Raw v1 set-aside records for the account.
2421
- """
2422
- internal_id = self.get_internal_account_id(account_id)
2551
+
2552
+ Raises:
2553
+ ValueError: If both or neither of ``account_id`` /
2554
+ ``internal_account_id`` are given.
2555
+ """
2556
+ if account_id is not None and internal_account_id is not None:
2557
+ raise ValueError("pass account_id (search) or internal_account_id, not both")
2558
+ if internal_account_id is not None:
2559
+ internal_id = int(internal_account_id)
2560
+ elif account_id is not None:
2561
+ internal_id = self.get_internal_account_id(account_id)
2562
+ else:
2563
+ raise ValueError("account_id or internal_account_id is required")
2423
2564
  res = self.api_request(f"{self.base_url}/account/accounts/{internal_id}/asidecash")
2424
2565
  records = res.json()
2425
2566
  if active_only:
@@ -2428,8 +2569,8 @@ class EclipseV1(EclipseBase):
2428
2569
 
2429
2570
  def create_set_aside(
2430
2571
  self,
2431
- account_number,
2432
- amount,
2572
+ account_number=None,
2573
+ amount=None,
2433
2574
  min_amount=0.0,
2434
2575
  max_amount=0.0,
2435
2576
  description=None,
@@ -2442,11 +2583,14 @@ class EclipseV1(EclipseBase):
2442
2583
  deplete_over_time=False,
2443
2584
  percent_calc_type=0,
2444
2585
  sync=True,
2586
+ *,
2587
+ internal_account_id=None,
2445
2588
  ):
2446
2589
  """Create a set-aside cash reservation for an account.
2447
2590
 
2448
2591
  Args:
2449
- account_number: Full custodial account number
2592
+ account_number: Full custodial account number (resolved via search).
2593
+ Mutually exclusive with ``internal_account_id``.
2450
2594
  amount: Cash amount to set aside
2451
2595
  min_amount: Minimum cash amount (default 0.0)
2452
2596
  max_amount: Maximum cash amount (default 0.0)
@@ -2463,11 +2607,27 @@ class EclipseV1(EclipseBase):
2463
2607
  percent_calc_type: 0='Use Default/Managed Value', 1='Use Total Value',
2464
2608
  2='Use Excluded Value' (default 0)
2465
2609
  sync: Wait for analytics to complete (default True)
2610
+ internal_account_id: Eclipse internal account id, used verbatim with
2611
+ no search resolution — the deterministic path for callers that
2612
+ already hold the id. Mutually exclusive with ``account_number``.
2466
2613
 
2467
2614
  Returns:
2468
2615
  dict: Created set-aside details
2469
- """
2470
- account_id = self.get_internal_account_id(account_number)
2616
+
2617
+ Raises:
2618
+ ValueError: If both or neither of ``account_number`` /
2619
+ ``internal_account_id`` are given, or ``amount`` is missing.
2620
+ """
2621
+ if account_number is not None and internal_account_id is not None:
2622
+ raise ValueError("pass account_number (search) or internal_account_id, not both")
2623
+ if amount is None:
2624
+ raise ValueError("amount is required")
2625
+ if internal_account_id is not None:
2626
+ account_id = int(internal_account_id)
2627
+ elif account_number is not None:
2628
+ account_id = self.get_internal_account_id(account_number)
2629
+ else:
2630
+ raise ValueError("account_number or internal_account_id is required")
2471
2631
 
2472
2632
  cash_type_map = {
2473
2633
  # end point account/accounts/asideCashAmountType
@@ -2969,6 +3129,13 @@ class EclipseV1(EclipseBase):
2969
3129
  :meth:`EclipseV2.create_trades` handles buys *and* sells and honours
2970
3130
  share count and dollar amount exactly.
2971
3131
 
3132
+ Orion support's answer to the sell-500 case (2026-08-03) was to use
3133
+ the **v2** wrapper body (``application`` / ``trades[]`` / ``action`` /
3134
+ ``tradeShares``). That body is not accepted here -- posting it to this
3135
+ v1 endpoint returns ``422 "Invalid trade order type id"`` for buys and
3136
+ sells alike -- so it is a redirect to the v2 surface, not a fix for
3137
+ this one. The v1 sell 500 remains unexplained upstream.
3138
+
2972
3139
  The generic per-trade creator behind Eclipse's Quick Trade / journal flows,
2973
3140
  as opposed to the ``/tradetool/`` generators (:meth:`cash_needs_trade`,
2974
3141
  :meth:`rebalance_trade`, ...) which operate on whole portfolios.
@@ -6561,7 +6728,7 @@ class EclipseV2(EclipseBase):
6561
6728
  "end_date": end_date,
6562
6729
  }
6563
6730
 
6564
- def get_set_asides(self, account_id=None, active_only=False):
6731
+ def get_set_asides(self, account_id=None, active_only=False, *, internal_account_id=None):
6565
6732
  """Get set-aside cash reservations, including the Eclipse set-aside id.
6566
6733
 
6567
6734
  Uses the v2 batch endpoint, which accepts a list of internal account IDs
@@ -6573,15 +6740,27 @@ class EclipseV2(EclipseBase):
6573
6740
 
6574
6741
  Args:
6575
6742
  account_id: Account ID, number, or name to restrict to a single
6576
- account. When None (default), returns set-asides firm-wide by
6577
- issuing one batch POST over all accounts.
6743
+ account (resolved via search). When neither this nor
6744
+ ``internal_account_id`` is given, returns set-asides firm-wide
6745
+ by issuing one batch POST over all accounts.
6578
6746
  active_only: When True, return only currently-active set-asides.
6579
6747
  Defaults to False (includes expired/inactive).
6748
+ internal_account_id: Eclipse internal account id, used verbatim with
6749
+ no search resolution — the deterministic path for callers that
6750
+ already hold the id. Mutually exclusive with ``account_id``.
6580
6751
 
6581
6752
  Returns:
6582
6753
  list: Augmented set-aside records
6583
- """
6584
- if account_id is not None:
6754
+
6755
+ Raises:
6756
+ ValueError: If both ``account_id`` and ``internal_account_id`` are
6757
+ given.
6758
+ """
6759
+ if account_id is not None and internal_account_id is not None:
6760
+ raise ValueError("pass account_id (search) or internal_account_id, not both")
6761
+ if internal_account_id is not None:
6762
+ account_ids = [int(internal_account_id)]
6763
+ elif account_id is not None:
6585
6764
  account_ids = [self.get_internal_account_id(account_id)]
6586
6765
  else:
6587
6766
  account_ids = [a["id"] for a in self.get_all_accounts()]
@@ -7264,7 +7443,7 @@ class EclipseV2(EclipseBase):
7264
7443
  def validate_trades(
7265
7444
  self,
7266
7445
  trades,
7267
- application=TRADING_APPLICATION_MANUAL,
7446
+ application=TRADING_APPLICATION_QUICK_TRADES,
7268
7447
  trade_tool_selection=2,
7269
7448
  trade_instance_type=5,
7270
7449
  trade_instance_sub_type=11,
@@ -7291,10 +7470,13 @@ class EclipseV2(EclipseBase):
7291
7470
  Size each with ``shares`` (share count) or ``amount`` (dollars) --
7292
7471
  see :meth:`build_trade` for the full parameter table.
7293
7472
  application: ``TradingApplication`` -- see :data:`TRADING_APPLICATIONS`.
7294
- Defaults to 11 (ManualTrade), the only value that creates orders on
7295
- :meth:`create_trades`. This endpoint tolerates other values, but
7296
- keeping the two in step avoids validating under one application and
7297
- creating under another.
7473
+ Defaults to 11 (``QuickTrades``), the only value that creates orders
7474
+ on :meth:`create_trades`. The enum name is also accepted as a string
7475
+ (``"QuickTrades"``) and behaves identically. This endpoint tolerates
7476
+ other values, but they are not equivalent: ``application=1``
7477
+ (CashNeeds) returns ``isValid: False`` with ``tradeAmount: 0`` rather
7478
+ than pricing the trade, so validating under one application and
7479
+ creating under another gives a meaningless pre-flight.
7298
7480
  trade_tool_selection: 1=Portfolio, 2=Account, 3=Model, ... (default 2)
7299
7481
  trade_instance_type: See :data:`TRADE_INSTANCE_TYPES` (default 5, Quick Trade)
7300
7482
  trade_instance_sub_type: See :data:`TRADE_INSTANCE_SUBTYPES`
@@ -7325,7 +7507,7 @@ class EclipseV2(EclipseBase):
7325
7507
  def create_trades(
7326
7508
  self,
7327
7509
  trades,
7328
- application=TRADING_APPLICATION_MANUAL,
7510
+ application=TRADING_APPLICATION_QUICK_TRADES,
7329
7511
  trade_tool_selection=2,
7330
7512
  trade_instance_type=5,
7331
7513
  trade_instance_sub_type=11,
@@ -7363,7 +7545,8 @@ class EclipseV2(EclipseBase):
7363
7545
  Size each with ``shares`` (share count) or ``amount`` (dollars);
7364
7546
  see :meth:`build_trade` for the full parameter table.
7365
7547
  application: ``TradingApplication`` -- see :data:`TRADING_APPLICATIONS`.
7366
- **Only 11 (ManualTrade) creates orders**, and it is the default.
7548
+ **Only 11 (``QuickTrades``) creates orders**, and it is the default.
7549
+ The enum name is also accepted as a string (``"QuickTrades"``).
7367
7550
  Every other value except 14 returns 400 "Trade Application <Name>
7368
7551
  is not supported."; 14 returns 200 with an empty ``tradeId`` list
7369
7552
  and creates nothing, so it is rejected here rather than allowed to
@@ -7391,11 +7574,11 @@ class EclipseV2(EclipseBase):
7391
7574
  >>> e.create_trades([t])["tradeId"]
7392
7575
  [72495]
7393
7576
  """
7394
- if application != TRADING_APPLICATION_MANUAL:
7577
+ if application != TRADING_APPLICATION_QUICK_TRADES:
7395
7578
  name = TRADING_APPLICATIONS.get(application, "unknown")
7396
7579
  raise ValueError(
7397
7580
  f"application={application} ({name}) does not create trade orders on "
7398
- f"this endpoint; only {TRADING_APPLICATION_MANUAL} (ManualTrade) does. "
7581
+ f"this endpoint; only {TRADING_APPLICATION_QUICK_TRADES} (QuickTrades) does. "
7399
7582
  "Other values return 400, and 14 returns 200 while creating nothing."
7400
7583
  )
7401
7584
  for i, t in enumerate(trades or []):
@@ -11484,13 +11667,19 @@ class Eclipse(EclipseBase):
11484
11667
 
11485
11668
  # --- v2-preferred overrides (documented best-of-both choices) ---------------
11486
11669
 
11487
- def get_set_asides(self, account_id=None, active_only=False):
11670
+ def get_set_asides(self, account_id=None, active_only=False, *, internal_account_id=None):
11488
11671
  """Set-asides via the v2 batch endpoint (best: firm-wide + set-aside id).
11489
11672
 
11490
11673
  Delegates to :meth:`EclipseV2.get_set_asides`. Use ``self.v1.get_set_asides``
11491
- for the raw per-account v1 form.
11674
+ for the raw per-account v1 form. Pass ``internal_account_id=`` when you
11675
+ already hold the Eclipse internal id — it is used verbatim, with no
11676
+ search resolution.
11492
11677
  """
11493
- return self.v2.get_set_asides(account_id=account_id, active_only=active_only)
11678
+ return self.v2.get_set_asides(
11679
+ account_id=account_id,
11680
+ active_only=active_only,
11681
+ internal_account_id=internal_account_id,
11682
+ )
11494
11683
 
11495
11684
 
11496
11685
  class EclipseAPI(Eclipse):
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "orionapi"
3
- version = "2.26.0"
3
+ version = "2.28.0"
4
4
  description = "A python interface for the Orion Advisors platform web API"
5
5
  authors = ["spencerogden-dsam <67068943+spencerogden-dsam@users.noreply.github.com>"]
6
6
 
File without changes