orionapi 2.24.0__tar.gz → 2.26.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,4 +1,4 @@
|
|
|
1
|
-
__version__ = "2.
|
|
1
|
+
__version__ = "2.26.0"
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
@@ -55,6 +55,39 @@ TRADE_INSTANCE_SUBTYPES = {
|
|
|
55
55
|
22: "Liquidate",
|
|
56
56
|
}
|
|
57
57
|
|
|
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
|
|
62
|
+
# EclipseV2.create_trades.
|
|
63
|
+
TRADING_APPLICATIONS = {
|
|
64
|
+
0: "BuySell",
|
|
65
|
+
1: "CashNeeds",
|
|
66
|
+
2: "GlobalTrades",
|
|
67
|
+
3: "ProrateCash",
|
|
68
|
+
4: "RaiseCash",
|
|
69
|
+
5: "Rebalance",
|
|
70
|
+
6: "SpendCash",
|
|
71
|
+
7: "(unnamed)",
|
|
72
|
+
8: "TaxLossHarvesting",
|
|
73
|
+
9: "TickerSwap",
|
|
74
|
+
10: "TradeToTargetPercent",
|
|
75
|
+
11: "ManualTrade",
|
|
76
|
+
12: "TacticalTrades",
|
|
77
|
+
13: "PartialShare",
|
|
78
|
+
14: "(unnamed, accepted but creates nothing)",
|
|
79
|
+
15: "TradeImport",
|
|
80
|
+
16: "RaiseCashByFund",
|
|
81
|
+
17: "OptionTrades",
|
|
82
|
+
18: "OutsourcedTradeExecution",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# The only TradingApplication value that creates trade orders on
|
|
86
|
+
# POST /api/v2/TradeOrder/Trades. Every other value except 14 returns
|
|
87
|
+
# 400 "Trade Application <Name> is not supported."; 14 returns 200 with an
|
|
88
|
+
# empty ``tradeId`` list and creates nothing. Live-verified 2026-07-28.
|
|
89
|
+
TRADING_APPLICATION_MANUAL = 11
|
|
90
|
+
|
|
58
91
|
# Set-Aside Cash Constants (from Eclipse API)
|
|
59
92
|
CASH_TYPE_DOLLAR = 1
|
|
60
93
|
CASH_TYPE_PERCENT = 2
|
|
@@ -2855,6 +2888,14 @@ class EclipseV1(EclipseBase):
|
|
|
2855
2888
|
Writes nothing. Use this as the pre-flight check for :meth:`create_trade`,
|
|
2856
2889
|
which has no preview / view-only mode of its own.
|
|
2857
2890
|
|
|
2891
|
+
.. deprecated:: 2.26.0
|
|
2892
|
+
Use :meth:`EclipseV2.validate_trades` instead; this emits a
|
|
2893
|
+
``DeprecationWarning``. ``action_id=2`` returns HTTP 500 while a
|
|
2894
|
+
byte-identical buy succeeds — the same upstream defect that affects
|
|
2895
|
+
:meth:`create_trade`. Pairing this with the deprecated
|
|
2896
|
+
:meth:`create_trade` also means validating and creating on the same
|
|
2897
|
+
unreliable surface.
|
|
2898
|
+
|
|
2858
2899
|
Args:
|
|
2859
2900
|
action_id: 1=Buy, 2=Sell, 3=Journal In, 4=Journal Out
|
|
2860
2901
|
account_id: Optional account ID
|
|
@@ -2869,6 +2910,12 @@ class EclipseV1(EclipseBase):
|
|
|
2869
2910
|
dict: ``message``, ``tradeAmount``, ``cashValuePostTrade`` and a
|
|
2870
2911
|
``warningMessage`` list (e.g. warnings about overspending cash)
|
|
2871
2912
|
"""
|
|
2913
|
+
warnings.warn(
|
|
2914
|
+
"EclipseV1.validate_trade is deprecated: the endpoint 500s on every sell. "
|
|
2915
|
+
"Use EclipseV2.validate_trades (Eclipse().validate_trades).",
|
|
2916
|
+
DeprecationWarning,
|
|
2917
|
+
stacklevel=2,
|
|
2918
|
+
)
|
|
2872
2919
|
payload = {
|
|
2873
2920
|
"actionId": action_id,
|
|
2874
2921
|
"accountId": account_id,
|
|
@@ -2902,6 +2949,26 @@ class EclipseV1(EclipseBase):
|
|
|
2902
2949
|
):
|
|
2903
2950
|
"""Generate trade orders (``POST /tradeorder/trades``).
|
|
2904
2951
|
|
|
2952
|
+
.. deprecated:: 2.26.0
|
|
2953
|
+
Use :meth:`EclipseV2.create_trades` instead; this emits a
|
|
2954
|
+
``DeprecationWarning``. The endpoint is unreliable in two independent
|
|
2955
|
+
ways -- only ``dollar_amount`` buys actually work. All live-verified
|
|
2956
|
+
2026-07-28 and reported to Orion:
|
|
2957
|
+
|
|
2958
|
+
=================== ===============================================
|
|
2959
|
+
Input Result
|
|
2960
|
+
=================== ===============================================
|
|
2961
|
+
``action_id=2`` HTTP 500 for **every** sell, across 5 securities,
|
|
2962
|
+
all amount modes and every documented body shape
|
|
2963
|
+
``quantity=N`` HTTP 200 ``"No Trade is generated."`` -- creates
|
|
2964
|
+
nothing; this wrapper now rejects it up front
|
|
2965
|
+
``percentage=N`` May create a zero-quantity, *disabled* order
|
|
2966
|
+
``dollar_amount=N`` Works
|
|
2967
|
+
=================== ===============================================
|
|
2968
|
+
|
|
2969
|
+
:meth:`EclipseV2.create_trades` handles buys *and* sells and honours
|
|
2970
|
+
share count and dollar amount exactly.
|
|
2971
|
+
|
|
2905
2972
|
The generic per-trade creator behind Eclipse's Quick Trade / journal flows,
|
|
2906
2973
|
as opposed to the ``/tradetool/`` generators (:meth:`cash_needs_trade`,
|
|
2907
2974
|
:meth:`rebalance_trade`, ...) which operate on whole portfolios.
|
|
@@ -2943,9 +3010,25 @@ class EclipseV1(EclipseBase):
|
|
|
2943
3010
|
Raises:
|
|
2944
3011
|
ValueError: If the enum values are unknown, option trading is
|
|
2945
3012
|
requested (unsupported by Eclipse), neither an account nor a
|
|
2946
|
-
portfolio is given,
|
|
3013
|
+
portfolio is given, ``quantity`` is used (silently discarded
|
|
3014
|
+
upstream), or a buy/sell does not specify exactly one of
|
|
2947
3015
|
``dollar_amount`` / ``quantity`` / ``percentage``.
|
|
3016
|
+
OrionAPIError: If Eclipse reports ``"No Trade is generated."`` -- a
|
|
3017
|
+
200 response that created nothing.
|
|
2948
3018
|
"""
|
|
3019
|
+
warnings.warn(
|
|
3020
|
+
"EclipseV1.create_trade is deprecated: the endpoint 500s on every sell and "
|
|
3021
|
+
"silently discards share-quantity buys. Use EclipseV2.create_trades "
|
|
3022
|
+
"(Eclipse().create_trades), which handles both and honours shares/amount.",
|
|
3023
|
+
DeprecationWarning,
|
|
3024
|
+
stacklevel=2,
|
|
3025
|
+
)
|
|
3026
|
+
if quantity is not None:
|
|
3027
|
+
raise ValueError(
|
|
3028
|
+
"quantity is silently ignored by this endpoint -- it returns 200 with "
|
|
3029
|
+
'"No Trade is generated." and creates nothing. Use dollar_amount here, '
|
|
3030
|
+
"or EclipseV2.create_trades(build_trade(..., shares=N)) for share counts."
|
|
3031
|
+
)
|
|
2949
3032
|
if action_id not in (1, 2, 3, 4):
|
|
2950
3033
|
raise ValueError(
|
|
2951
3034
|
"action_id must be 1 (Buy), 2 (Sell), 3 (Journal In) or 4 (Journal Out)"
|
|
@@ -2998,6 +3081,13 @@ class EclipseV1(EclipseBase):
|
|
|
2998
3081
|
|
|
2999
3082
|
res = self.api_request(f"{self.base_url}/tradeorder/trades", requests.post, json=payload)
|
|
3000
3083
|
result = res.json()
|
|
3084
|
+
# A 200 carrying this message means nothing was created. Do not let a
|
|
3085
|
+
# silent no-op look like a successful write.
|
|
3086
|
+
if isinstance(result, dict) and result.get("message") == "No Trade is generated.":
|
|
3087
|
+
raise OrionAPIError(
|
|
3088
|
+
"Eclipse returned 'No Trade is generated.' — the request succeeded but "
|
|
3089
|
+
"created no trade orders. Use EclipseV2.create_trades instead."
|
|
3090
|
+
)
|
|
3001
3091
|
self._maybe_wait_for_analytics(sync)
|
|
3002
3092
|
return result
|
|
3003
3093
|
|
|
@@ -7046,6 +7136,287 @@ class EclipseV2(EclipseBase):
|
|
|
7046
7136
|
res = self.api_request(f"{self.base_url_v2}/Trading/ActiveBatchJobs", params=params)
|
|
7047
7137
|
return res.json()
|
|
7048
7138
|
|
|
7139
|
+
# --- TradeOrder / Trades (v2): batch create + validate -----------------------
|
|
7140
|
+
# The v2 counterparts of EclipseV1.create_trade / validate_trade. Named in the
|
|
7141
|
+
# plural because v2 takes a *list* of trades in one wrapper -- which also keeps
|
|
7142
|
+
# them distinct from the v1 singular methods on the Eclipse unifier, whose
|
|
7143
|
+
# __getattr__ resolves v1 first.
|
|
7144
|
+
#
|
|
7145
|
+
# Prefer these over the v1 pair for SELLS: the v1 endpoints return HTTP 500 for
|
|
7146
|
+
# every sell (see EclipseV1.create_trade's docstring). Live-verified 2026-07-28:
|
|
7147
|
+
# v2 validates sells correctly, returning isValid/tradeAmount/tradeShares.
|
|
7148
|
+
|
|
7149
|
+
@staticmethod
|
|
7150
|
+
def build_trade(
|
|
7151
|
+
account_id,
|
|
7152
|
+
portfolio_id,
|
|
7153
|
+
security_id,
|
|
7154
|
+
action,
|
|
7155
|
+
shares=None,
|
|
7156
|
+
amount=None,
|
|
7157
|
+
percent=None,
|
|
7158
|
+
**extra,
|
|
7159
|
+
):
|
|
7160
|
+
"""Build one entry for the ``trades`` list of :meth:`validate_trades` /
|
|
7161
|
+
:meth:`create_trades`.
|
|
7162
|
+
|
|
7163
|
+
Saves the caller from having to know the v2 ``ValidateTradeDto`` key names.
|
|
7164
|
+
|
|
7165
|
+
Exactly one of ``shares`` / ``amount`` / ``percent`` sets the trade size:
|
|
7166
|
+
|
|
7167
|
+
========= =================== =================================================
|
|
7168
|
+
Parameter Sent as Meaning
|
|
7169
|
+
========= =================== =================================================
|
|
7170
|
+
``shares`` ``tradeShares`` **Share count.** A whole or fractional number of
|
|
7171
|
+
shares. Honoured exactly -- live-verified
|
|
7172
|
+
``shares=1`` produced an order of qty 1.0.
|
|
7173
|
+
``amount`` ``tradeAmount`` **Dollar amount.** Eclipse divides by the current
|
|
7174
|
+
price and books the resulting (fractional) share
|
|
7175
|
+
count -- ``amount=500`` on a $393 stock produced
|
|
7176
|
+
qty 1.2711.
|
|
7177
|
+
``percent`` ``tradePercent`` Percent of the *existing position*. Only
|
|
7178
|
+
meaningful for sells/trims, and a percent that
|
|
7179
|
+
rounds to <1 share can yield a zero-quantity,
|
|
7180
|
+
disabled order. Prefer ``shares`` or ``amount``.
|
|
7181
|
+
========= =================== =================================================
|
|
7182
|
+
|
|
7183
|
+
Args:
|
|
7184
|
+
account_id: Account ID (``accountId``)
|
|
7185
|
+
portfolio_id: Portfolio ID (``portfolioId``). **Required** -- see the
|
|
7186
|
+
warning on :meth:`validate_trades`; omitting it makes the API
|
|
7187
|
+
silently return an empty list.
|
|
7188
|
+
security_id: Security ID (``securityId``)
|
|
7189
|
+
action: 1=Buy, 2=Sell, 3=Journal In, 4=Journal Out (``action``)
|
|
7190
|
+
shares: Number of shares to buy/sell (``tradeShares``) -- preferred
|
|
7191
|
+
amount: Dollar amount to buy/sell (``tradeAmount``) -- preferred
|
|
7192
|
+
percent: Percent of the position (``tradePercent``); see caveat above
|
|
7193
|
+
**extra: Any other ``ValidateTradeDto`` field, passed through verbatim
|
|
7194
|
+
|
|
7195
|
+
Returns:
|
|
7196
|
+
dict: A single trade entry
|
|
7197
|
+
|
|
7198
|
+
Raises:
|
|
7199
|
+
ValueError: If ``portfolio_id`` is missing, ``action`` is out of range,
|
|
7200
|
+
or not exactly one of ``shares`` / ``amount`` / ``percent`` is set.
|
|
7201
|
+
|
|
7202
|
+
Example:
|
|
7203
|
+
>>> e.build_trade(account_id=686, portfolio_id=209, security_id=73,
|
|
7204
|
+
... action=1, shares=1)
|
|
7205
|
+
{'accountId': 686, 'portfolioId': 209, 'securityId': 73,
|
|
7206
|
+
'action': 1, 'tradeShares': 1}
|
|
7207
|
+
"""
|
|
7208
|
+
if portfolio_id is None:
|
|
7209
|
+
raise ValueError(
|
|
7210
|
+
"portfolio_id is required; without portfolioId the v2 trade endpoints "
|
|
7211
|
+
"return an empty list instead of validating"
|
|
7212
|
+
)
|
|
7213
|
+
if action not in (1, 2, 3, 4):
|
|
7214
|
+
raise ValueError("action must be 1 (Buy), 2 (Sell), 3 (Journal In) or 4 (Journal Out)")
|
|
7215
|
+
sizes = [s for s in (shares, amount, percent) if s]
|
|
7216
|
+
if len(sizes) != 1:
|
|
7217
|
+
raise ValueError("exactly one of shares, amount or percent must be set")
|
|
7218
|
+
|
|
7219
|
+
trade = {
|
|
7220
|
+
"accountId": account_id,
|
|
7221
|
+
"portfolioId": portfolio_id,
|
|
7222
|
+
"securityId": security_id,
|
|
7223
|
+
"action": action,
|
|
7224
|
+
}
|
|
7225
|
+
if shares is not None:
|
|
7226
|
+
trade["tradeShares"] = shares
|
|
7227
|
+
if amount is not None:
|
|
7228
|
+
trade["tradeAmount"] = amount
|
|
7229
|
+
if percent is not None:
|
|
7230
|
+
trade["tradePercent"] = percent
|
|
7231
|
+
trade.update(extra)
|
|
7232
|
+
return trade
|
|
7233
|
+
|
|
7234
|
+
def _trade_wrapper(
|
|
7235
|
+
self,
|
|
7236
|
+
trades,
|
|
7237
|
+
application,
|
|
7238
|
+
trade_tool_selection,
|
|
7239
|
+
trade_instance_type,
|
|
7240
|
+
trade_instance_sub_type,
|
|
7241
|
+
instance_notes,
|
|
7242
|
+
):
|
|
7243
|
+
"""Build and sanity-check the ``ValidateTradeWrapperDto`` body."""
|
|
7244
|
+
if not trades:
|
|
7245
|
+
raise ValueError("trades must be a non-empty list of trade dicts")
|
|
7246
|
+
for i, t in enumerate(trades):
|
|
7247
|
+
if t.get("portfolioId") is None:
|
|
7248
|
+
raise ValueError(
|
|
7249
|
+
f"trades[{i}] is missing portfolioId; without it the v2 trade "
|
|
7250
|
+
"endpoints silently return an empty list instead of validating. "
|
|
7251
|
+
"Use build_trade() to construct entries."
|
|
7252
|
+
)
|
|
7253
|
+
body = {
|
|
7254
|
+
"application": application,
|
|
7255
|
+
"trades": trades,
|
|
7256
|
+
"tradeToolSelection": trade_tool_selection,
|
|
7257
|
+
"tradeInstanceType": trade_instance_type,
|
|
7258
|
+
"tradeInstanceSubType": trade_instance_sub_type,
|
|
7259
|
+
}
|
|
7260
|
+
if instance_notes is not None:
|
|
7261
|
+
body["instanceNotes"] = instance_notes
|
|
7262
|
+
return body
|
|
7263
|
+
|
|
7264
|
+
def validate_trades(
|
|
7265
|
+
self,
|
|
7266
|
+
trades,
|
|
7267
|
+
application=TRADING_APPLICATION_MANUAL,
|
|
7268
|
+
trade_tool_selection=2,
|
|
7269
|
+
trade_instance_type=5,
|
|
7270
|
+
trade_instance_sub_type=11,
|
|
7271
|
+
instance_notes=None,
|
|
7272
|
+
):
|
|
7273
|
+
"""Validate a batch of trades (``POST /TradeOrder/Trades/Action/Validate``).
|
|
7274
|
+
|
|
7275
|
+
Writes nothing. The v2 counterpart of :meth:`EclipseV1.validate_trade`, and
|
|
7276
|
+
the one to use for **sells** -- the v1 endpoint 500s on every sell.
|
|
7277
|
+
|
|
7278
|
+
.. warning::
|
|
7279
|
+
Every entry in ``trades`` must carry ``portfolioId``. Without it the API
|
|
7280
|
+
returns ``200 []`` -- an empty list, not an error -- so an unvalidated
|
|
7281
|
+
batch looks indistinguishable from a clean one. This method raises
|
|
7282
|
+
``ValueError`` rather than let that pass silently.
|
|
7283
|
+
|
|
7284
|
+
Note:
|
|
7285
|
+
The response is not 1:1 with the input. Eclipse expands each requested
|
|
7286
|
+
trade across the underlying tax lots / sleeve accounts, so one entry can
|
|
7287
|
+
come back as several rows (live-verified: a 99,999-share sell returned 4).
|
|
7288
|
+
|
|
7289
|
+
Args:
|
|
7290
|
+
trades: List of trade dicts; build them with :meth:`build_trade`.
|
|
7291
|
+
Size each with ``shares`` (share count) or ``amount`` (dollars) --
|
|
7292
|
+
see :meth:`build_trade` for the full parameter table.
|
|
7293
|
+
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.
|
|
7298
|
+
trade_tool_selection: 1=Portfolio, 2=Account, 3=Model, ... (default 2)
|
|
7299
|
+
trade_instance_type: See :data:`TRADE_INSTANCE_TYPES` (default 5, Quick Trade)
|
|
7300
|
+
trade_instance_sub_type: See :data:`TRADE_INSTANCE_SUBTYPES`
|
|
7301
|
+
(default 11, Quick Trade)
|
|
7302
|
+
instance_notes: Optional notes for the trade instance
|
|
7303
|
+
|
|
7304
|
+
Returns:
|
|
7305
|
+
list: Validated trade dicts with ``isValid``, ``tradeAmount``,
|
|
7306
|
+
``tradeShares``, ``tradePercent``, ``warningMessage``,
|
|
7307
|
+
``zeroTradeReasons`` and gain/loss detail
|
|
7308
|
+
|
|
7309
|
+
Raises:
|
|
7310
|
+
ValueError: If ``trades`` is empty or any entry lacks ``portfolioId``
|
|
7311
|
+
"""
|
|
7312
|
+
body = self._trade_wrapper(
|
|
7313
|
+
trades,
|
|
7314
|
+
application,
|
|
7315
|
+
trade_tool_selection,
|
|
7316
|
+
trade_instance_type,
|
|
7317
|
+
trade_instance_sub_type,
|
|
7318
|
+
instance_notes,
|
|
7319
|
+
)
|
|
7320
|
+
res = self.api_request(
|
|
7321
|
+
f"{self.base_url_v2}/TradeOrder/Trades/Action/Validate", requests.post, json=body
|
|
7322
|
+
)
|
|
7323
|
+
return res.json()
|
|
7324
|
+
|
|
7325
|
+
def create_trades(
|
|
7326
|
+
self,
|
|
7327
|
+
trades,
|
|
7328
|
+
application=TRADING_APPLICATION_MANUAL,
|
|
7329
|
+
trade_tool_selection=2,
|
|
7330
|
+
trade_instance_type=5,
|
|
7331
|
+
trade_instance_sub_type=11,
|
|
7332
|
+
instance_notes=None,
|
|
7333
|
+
):
|
|
7334
|
+
"""Create a batch of trade orders (``POST /TradeOrder/Trades``).
|
|
7335
|
+
|
|
7336
|
+
**The recommended way to create trades.** Prefer this over
|
|
7337
|
+
:meth:`EclipseV1.create_trade`, which is deprecated: v1 500s on every sell
|
|
7338
|
+
and silently discards share-count buys.
|
|
7339
|
+
|
|
7340
|
+
Size each trade with **``shares`` (share count)** or **``amount``
|
|
7341
|
+
(dollars)** via :meth:`build_trade` -- both are honoured exactly here.
|
|
7342
|
+
Live-verified 2026-07-28: ``shares=1`` created an order of qty 1.0, and
|
|
7343
|
+
``amount=500`` on a $393 stock created qty 1.2711.
|
|
7344
|
+
|
|
7345
|
+
.. warning::
|
|
7346
|
+
This writes. Call :meth:`validate_trades` first and check ``isValid`` /
|
|
7347
|
+
``warningMessage`` on every returned row; the same ``portfolioId``
|
|
7348
|
+
requirement applies here.
|
|
7349
|
+
|
|
7350
|
+
Unlike the v1 endpoint there is no ``isSendImmediately`` field on this
|
|
7351
|
+
surface at all, so created orders are always staged as pending. Setting
|
|
7352
|
+
``approvalStatus`` or ``orderStatus`` on a trade entry is rejected: that
|
|
7353
|
+
would amount to creating and approving in one call, which this wrapper
|
|
7354
|
+
deliberately does not allow. Review and execute in Eclipse.
|
|
7355
|
+
|
|
7356
|
+
Note:
|
|
7357
|
+
No ``sync`` parameter: the analytics-wait helpers live on
|
|
7358
|
+
:class:`EclipseV1`. Call ``Eclipse().v1.wait_for_analytics()`` if you
|
|
7359
|
+
need to block until analytics settle.
|
|
7360
|
+
|
|
7361
|
+
Args:
|
|
7362
|
+
trades: List of trade dicts; build them with :meth:`build_trade`.
|
|
7363
|
+
Size each with ``shares`` (share count) or ``amount`` (dollars);
|
|
7364
|
+
see :meth:`build_trade` for the full parameter table.
|
|
7365
|
+
application: ``TradingApplication`` -- see :data:`TRADING_APPLICATIONS`.
|
|
7366
|
+
**Only 11 (ManualTrade) creates orders**, and it is the default.
|
|
7367
|
+
Every other value except 14 returns 400 "Trade Application <Name>
|
|
7368
|
+
is not supported."; 14 returns 200 with an empty ``tradeId`` list
|
|
7369
|
+
and creates nothing, so it is rejected here rather than allowed to
|
|
7370
|
+
look like a success.
|
|
7371
|
+
trade_tool_selection: 1=Portfolio, 2=Account, 3=Model, ... (default 2)
|
|
7372
|
+
trade_instance_type: See :data:`TRADE_INSTANCE_TYPES` (default 5, Quick Trade)
|
|
7373
|
+
trade_instance_sub_type: See :data:`TRADE_INSTANCE_SUBTYPES`
|
|
7374
|
+
(default 11, Quick Trade)
|
|
7375
|
+
instance_notes: Optional notes for the trade instance
|
|
7376
|
+
|
|
7377
|
+
Returns:
|
|
7378
|
+
dict: ``instanceId``, ``tradeId`` (the created trade order IDs),
|
|
7379
|
+
``trades`` and ``excludeTrades``
|
|
7380
|
+
|
|
7381
|
+
Raises:
|
|
7382
|
+
ValueError: If ``trades`` is empty, any entry lacks ``portfolioId``, any
|
|
7383
|
+
entry sets ``approvalStatus`` / ``orderStatus``, or ``application``
|
|
7384
|
+
is one that cannot create orders.
|
|
7385
|
+
|
|
7386
|
+
Example:
|
|
7387
|
+
>>> t = e.build_trade(account_id=686, portfolio_id=209, security_id=73,
|
|
7388
|
+
... action=2, shares=1) # SELL 1 share
|
|
7389
|
+
>>> [r["isValid"] for r in e.validate_trades([t])]
|
|
7390
|
+
[True]
|
|
7391
|
+
>>> e.create_trades([t])["tradeId"]
|
|
7392
|
+
[72495]
|
|
7393
|
+
"""
|
|
7394
|
+
if application != TRADING_APPLICATION_MANUAL:
|
|
7395
|
+
name = TRADING_APPLICATIONS.get(application, "unknown")
|
|
7396
|
+
raise ValueError(
|
|
7397
|
+
f"application={application} ({name}) does not create trade orders on "
|
|
7398
|
+
f"this endpoint; only {TRADING_APPLICATION_MANUAL} (ManualTrade) does. "
|
|
7399
|
+
"Other values return 400, and 14 returns 200 while creating nothing."
|
|
7400
|
+
)
|
|
7401
|
+
for i, t in enumerate(trades or []):
|
|
7402
|
+
for banned in ("approvalStatus", "orderStatus"):
|
|
7403
|
+
if t.get(banned) is not None:
|
|
7404
|
+
raise ValueError(
|
|
7405
|
+
f"trades[{i}] sets {banned}; creating and approving/executing a "
|
|
7406
|
+
"trade in one call is not supported by this wrapper. Create the "
|
|
7407
|
+
"orders here, then action them in Eclipse."
|
|
7408
|
+
)
|
|
7409
|
+
body = self._trade_wrapper(
|
|
7410
|
+
trades,
|
|
7411
|
+
application,
|
|
7412
|
+
trade_tool_selection,
|
|
7413
|
+
trade_instance_type,
|
|
7414
|
+
trade_instance_sub_type,
|
|
7415
|
+
instance_notes,
|
|
7416
|
+
)
|
|
7417
|
+
res = self.api_request(f"{self.base_url_v2}/TradeOrder/Trades", requests.post, json=body)
|
|
7418
|
+
return res.json()
|
|
7419
|
+
|
|
7049
7420
|
# --- Optimization (v2-only): per-batch / per-account detail reads ------------
|
|
7050
7421
|
|
|
7051
7422
|
def get_optimization_accounts(self, batch_id):
|
|
File without changes
|