Transactions
Definition
See the Glossary for a definition.
Context¶
The broker/custodian is the master system for transactions and holdings (positions). These need to be kept in sync with InvestSuite. In case of broker integration by the client (see integration architecture), this page describes how.
First transactions, or first orders?
We recommend to first create (update) the transaction, and then update the portfolio holdings.
Concepts¶
erDiagram
User {
string external_id
}
Portfolio {
string external_id
object holdings
}
Optimization {
string id
bool is_recommended
object orders
}
Transaction {
string external_id
string type
string optimization_id
}
Movement {
string external_id
string reference_external_id
string type
string status
string datetime
}
User ||--|{ Portfolio: owns
Portfolio ||--|| Optimization: "optimizations/latest (Robo Advisor only)"
Portfolio ||--|{ Transaction: has
Transaction ||--|{ Movement: movements
Transaction }|--|| Optimization: "optimization_id (Robo Advisor only)"
Transactions¶
Types¶
The following types of transactions exist:
- Order transactions: buying/selling of instruments, containing one or more movements (see example).
- Cash Transfer transaction: a deposit or withdrawal (see example).
- Corporate Action transactions, eg. stock split or distribution of dividends, can but does not need to include one or more movements (see example).
- Security Transfer transaction: a transfer in/out the Portfolio from/to another broker.
- Administrative transaction: eg. a management fee, custody fee, ... (see example).
Status¶
The Status of a Transaction is calculated by the status of the Movements it contains.
This is not editable through the API.
See the Movement status.
Movements¶
- A Transaction has one or more Movements.
- A Transaction has one Primary Movement: the one that 'triggers' the other movement.
Types¶
The following types of movements exist: CASH_DEPOSIT
, CASH_DIVIDEND
, CASH_WITHDRAWAL
, COUPON
, BUY
, SELL
, CORPORATE_ACTION_IN
, CORPORATE_ACTION_OUT
, REVERSE_STOCK_SPLIT
, STOCK_DIVIDEND
, STOCK_SPLIT
, TRANSFER_IN
, TRANSFER_OUT
, CUSTODY_FEE
, INSTRUMENT_ENTRY_EXIT_FEE
, MANAGEMENT_FEE
, OTHER_FEE
, OTHER_TAX
, SERVICE_ENTRY_EXIT_FEE
, TRANSACTION_FEE
, WITHHOLDING_TAX
.
Status¶
A movement has the following statuses: PLANNED
, PENDING
, PLACED
, EXECUTED
, SETTLED
, CANCELLED
, NOT_EXECUTED
, EXPIRED
.
Create transaction¶
Order PLACED
¶
Robo Advisor & Optimization ID
For Robo Advisor, include the optimization_id
field (from Optimization.id
).
This field is used to reconcile whether the Optimization is fully executed. While an Optimization is in progress (ie. the rebalancing process is in progress), the Portfolio is blocked from making withdrawals.
For Orders that originate from an Optimization, include the optimization_id
.
Quantity type
Funds are typically bought per amount, not per unit.
The convention is that the quantity
field is set to 1, the amount in the unit_price
field and (optionally) specify the unit_price_currency
.
See the "Request (Amount)" example below.
Buy¶
POST /portfolios/P01FGVEKTV86PPKQVRK9CHT31JR/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-transaction-id-1",
"optimization_id": "O01ARZ3NDEKTSV4RRFFQ69G5FAV", // Robo Advisor only
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
}
]
}
POST /portfolios/P01FGVEKTV86PPKQVRK9CHT31JR/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-transaction-id-1",
"optimization_id": "O01ARZ3NDEKTSV4RRFFQ69G5FAV",
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 1,
"unit_price": "1000",
"unit_price_currency": "EUR"
}
]
}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Sell¶
Info
A sell transaction is the reverse of a buy order. This example is identical to the Buy transaction example in this page, other than type
is SELL
(not BUY
) and the signs of the cash movement inverted.
POST /portfolios/P01FGVEKTV86PPKQVRK9CHT31JR/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-transaction-id-1",
"optimization_id": "O01ARZ3NDEKTSV4RRFFQ69G5FAV",
"movements": [
{
"type": "SELL",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
}
]
}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "SELL",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Cash transfer¶
Transactions that hold cash movements represent to InvestSuite movements on the investment account. That account is usually different from the current account, which is the account that the client holds with the bank. We expect in other words transactions on your brokerage system, not from your core banking platform.
Funding¶
POST /portfolios/P01FGZK41MJ4NJXKZ27VJC0HGS9/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "P01FHAR57WS6Q8AV1GH5EATYKP1/14031738752",
"movements": [
{
"type": "CASH_DEPOSIT",
"status": "EXECUTED", // or "SETTLED"
"datetime": "2021-10-06T00:00:00+00:00",
"instrument_id": "$USD",
"quantity": 500.0,
}
]
}
Update Portfolio holdings
Also update the Portfolio holdings after this call.
Withdrawal¶
POST /portfolios/P01FGZK41MJ4NJXKZ27VJC0HGS9/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "P01FHAR57WS6Q8AV1GH5EATYKP1/14031738752",
"movements": [
{
"type": "CASH_WITHDRAWAL",
"status": "EXECUTED", // or "SETTLED"
"datetime": "2021-10-06T00:00:00+00:00",
"instrument_id": "$USD",
"quantity": -500.0,
}
]
}
{
"external_id": "P01FHAR57WS6Q8AV1GH5EATYKP1/14031738752",
"movements": [
{
"type": "CASH_WITHDRAWAL",
"status": "SETTLED",
"datetime": "2021-10-06T00:00:00+00:00",
"instrument_id": "$USD",
"quantity": -500.0,
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Update Portfolio holdings
Also update the Portfolio holdings in case of EXECUTED
or SETTLED
.
Corporate Action¶
Corporate actions are changes invoked by a company that affect its stakeholders in particular share and bond holders. They come in various forms and shapes: dividends, stock splits, reverse stock splits ... and are usually approved by a board of directors. Sometimes even by the shareholders who can voluntarily submit a vote.
Corporate actions are registered as transactions as they will lead to movements such as issuing dividends. Note the use of reference_instrument_id
to reference the portfolio position the corporate action refers to.
POST /portfolios/P01FGZK41MJ4NJXKZ27VJC0HGS9/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-corporate-action-1",
"movements": [
{
"type": "CASH_DIVIDEND",
"status": "SETTLED",
"datetime": "2021-10-01T00:00:00+00:00",
"instrument_id": "$USD",
"quantity": 0.0785,
"reference_instrument_id": "LU78464A6727",
}
]
}
{
"external_id": "your-corporate-action-1",
"movements": [
{
"type": "CASH_DIVIDEND",
"status": "SETTLED",
"datetime": "2021-10-01T00:00:00+00:00",
"instrument_id": "$USD",
"quantity": 0.0785,
"reference_instrument_id": "LU78464A6727",
}
],
"id": "T01FGZK41MJ4NJXKZ27VJC0HGS9",
"creation_datetime": "2021-10-02T04:10:15.570586+00:00",
"version": 1,
"version_datetime": "2021-10-02T04:10:15.570586+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Update Portfolio holdings
Also update the Portfolio holdings after this call.
Administrative¶
Costs and fees¶
Costs and charges come in various forms. There are items in the type
Enum that define the sort of charge.
- For instance for management fees and custody fees. For other types use
"type": "OTHER_FEE"
and"description": "{string}"
. Same goes for taxes. - For withholding tax paid to the governement use
"type": "WITHHOLDING_TAX"
. - For other sorts of taxes charged use
"type": "OTHER_TAX"
and"description": "{string}"
.
Costs and charges
These are the costs and charges that are not associated with a transaction.
For costs and charges that are associated with a transaction, see above.
POST /portfolios/P01FGZK41MJ4NJXKZ27VJC0HGS9/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "14031738752",
"movements": [
{
"external_id": "14031738752",
"type": "MANAGEMENT_FEE",
"status": "SETTLED",
"datetime": "2021-10-03T08:00:16.733954+00:00",
"instrument_id": "$USD",
"quantity": -0.1035719,
}
]
}
{
"external_id": "14031738752",
"movements": [
{
"type": "MANAGEMENT_FEE",
"status": "SETTLED",
"datetime": "2021-10-03T08:00:16.733954+00:00",
"instrument_id": "$USD",
"quantity": -0.1035719
}
],
"id": "T01FH2JNYAYQ4CTHQJ1MFDDGXZQ",
"creation_datetime": "2021-10-03T08:00:16.734375+00:00",
"version": 1,
"version_datetime": "2021-10-03T08:00:16.734375+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Update Portfolio holdings
Also update the Portfolio holdings after this call.
Update transaction¶
Transactions can be updated by issuing a PATCH /portfolios/{id}/transactions/{id}
and creating or updating the movements
inside.
Warning
Include the full nested movements
object (ie. including the initial PLACED
movement) in the PATCH
request.
PATCHing with the correct movements
The InvestSuite Transaction model is very flexible to support various scenarios. Special care is needed when PATCHing a Transaction with new movements, or you may not achieve the desired effect.
In the Buy order example described on this page:
- When placed, it will only have one movement with
PLACED
. - When it is executed, PATCH the Transaction with the original
PLACED
movement and an additional movement with statusEXECUTED
. - When it is settled, PATCH the Transaction with the original
PLACED
movement and update the status field of the additional movement toSETTLED
.
In general, use the following diagram to determine which movements to keep in a PATCH. Keep the movement in each group, if it exists.
stateDiagram-v2
direction LR
groupOne: Request Statuses
groupTwo: Execution Statuses
[*] --> groupOne: 0..1
[*] --> groupTwo: 0..n
groupOne --> groupTwo: 0..n
state groupOne {
direction LR
PLANNED --> PENDING
PENDING --> PLACED
}
state groupTwo {
direction LR
[*] --> CANCELLED
[*] --> NOT_EXECUTED
[*] --> EXPIRED
[*] --> EXECUTED
EXECUTED --> SETTLED
}
Order EXECUTED
¶
Optional
Updating the Transaction with this status is optional: a portfolio that has orders in PLACED
status (ie. not yet executed or settled) is not reoptimized. Since settlement can take a couple of days, this may not be desirable.
If the portfolio should be optimized ahead of the settlement of the transactions, update the transaction with the EXECUTED
status.
Order costs and fees
This example also shows the introduction of the transaction fee and the tax associated with the order.
For costs and charges that are not associated with the transaction (eg. monthly fee) see the Costs and Charges Transaction.
What movements
to include?
Note that the original PLACED
movement is included.
See the diagram at the top of this section to understand why.
Correlating related movements
We correlate movements based on the movement.datetime
and (optionally) on movement.external_id
.
-
The
datetime
of the related instrument & cash movements must be identical. Otherwise, the portfolio holdings are inconsistent and the performance (eg. TWR) calculations will not be correct.
In the example below thedatetime
of the Instrument Buy, Cash Sell, Cash Transaction Fee and Cash Other Tax match. -
When the
external_id
of a movement is set, the related cash movements must set thereference_external_id
to theexternal_id
of the respective securities movement.
Whenexternal_id
is not filled, leave thereference_external_id
field empty
This applies to all Execution Statusses: CANCELLED
, NOT_EXECUTED
, EXPIRED
, EXECUTED
, SETTLED
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
},
{
"external_id": "instrument-buy-123",
"type": "BUY",
"status": "EXECUTED",
"datetime": "2022-06-10T07:52:26.341Z",
"instrument_id": "LU78468R1014",
"unit_price": 29.51,
"quantity": 7
},
{
"type": "SELL",
"status": "EXECUTED",
"datetime": "2022-06-10T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -206.57, // This excludes the TRANSACTION_FEE and OTHER_TAX
"reference_external_id": "instrument-buy-123"
},
{
"type": "TRANSACTION_FEE",
"status": "EXECUTED",
"datetime": "2022-06-10T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -3,
"reference_external_id": "instrument-buy-123"
},
{
"type": "OTHER_TAX",
"status": "EXECUTED",
"datetime": "2022-06-10T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -0.15,
"reference_external_id": "instrument-buy-123"
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Order SETTLED
¶
What movements
to include?
Note that
- the original
PLACED
movement is included. - there are no
EXECUTED
movements.
See the diagram at the top of this section to understand why.
Buy¶
PATCH /portfolios/P01FGVEKTV86PPKQVRK9CHT31JR/transactions/T01FHCP1CZ9F1S207KJHNA5V244 HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
},
{
"type": "BUY",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "LU78468R1014",
"unit_price": 29.51,
"quantity": 7
},
{
"type": "SELL",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -206.57
},
{
"type": "TRANSACTION_FEE",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -3
},
{
"type": "OTHER_TAX",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -0.15
}
]
}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": 7
},
{
"type": "BUY",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "LU78468R1014",
"unit_price": 29.51,
"quantity": 7
},
{
"type": "SELL",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -206.57
},
{
"type": "TRANSACTION_FEE",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -3
},
{
"type": "OTHER_TAX",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -0.15
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Sell¶
PATCH /portfolios/P01FGVEKTV86PPKQVRK9CHT31JR/transactions/T01FHCP1CZ9F1S207KJHNA5V244 HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "SELL",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": -7
},
{
"type": "SELL",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "LU78468R1014",
"unit_price": 29.51,
"quantity": -7
},
{
"type": "BUY",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": 206.57
},
{
"type": "TRANSACTION_FEE",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -3
},
{
"type": "OTHER_TAX",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -0.15
}
]
}
{
"external_id": "your-transaction-id-1",
"movements": [
{
"type": "SELL",
"status": "PLACED",
"datetime": "2022-06-10T07:49:26.341Z",
"instrument_id": "LU78468R1014",
"quantity": -7
},
{
"type": "SELL",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "LU78468R1014",
"unit_price": 29.51,
"quantity": -7
},
{
"type": "BUY",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": 206.57
},
{
"type": "TRANSACTION_FEE",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -3
},
{
"type": "OTHER_TAX",
"status": "SETTLED",
"datetime": "2022-06-12T07:52:26.341Z",
"instrument_id": "$USD",
"quantity": -0.15
}
],
"id": "T01FHCP1CZ9F1S207KJHNA5V244",
"creation_datetime": "2021-10-07T06:11:22.217585+00:00",
"version": 2,
"version_datetime": "2021-10-08T06:15:51.106890+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Cancel order¶
To turn a PLACED
into a cancelled one, you patch the movements of the transactions with the current PLACED
movement to a new movement with status CANCELLED
.
Given a current status with a PLACED
order:
{
"id": "T01G53YQW6E5RH7CAOBFUSCATED",
"external_id": "my-transaction-1",
"portfolio_id": "P01G511AYA61Z3Q14OBFUSCATED",
"portfolio_currency": "USD",
"type": "ORDER",
"order_type": "MARKET",
"primary_movement": {
"external_id": "my-movement-1",
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-08T10:15:53.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3"
},
"movements": [
{
"external_id": "my-movement-1",
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-08T10:15:53.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3"
}
]
}
To register a cancellation you add one movement to the current list of movements. Nothing else should be updated, so the payload is as follows. Note that you have to send the existing movements in this list as well, because they would be lost otherwise:
PATCH /portfolios/P01FGZK41MJ4NJXKZ27VJC0HGS9/transactions/ HTTP/1.1
Host: api.sandbox.investsuite.com
Content-Type: application/json
Authorization: Bearer {string}
{
"external_id": "14031738752",
"movements": [
{
"external_id": "my-movement-1",
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-08T10:15:53.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3"
},
{
"external_id": "my-movement-2",
"type": "BUY",
"status": "CANCELLED",
"datetime": "2022-06-08T11:01:54.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3"
}
]
}
{
"external_id": "14031738752",
"movements": [
{
"external_id": "movement-1",
"type": "BUY",
"status": "PLACED",
"datetime": "2022-06-08T10:15:53.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3",
"unit_price": "3.49"
},
{
"external_id": "movement-2",
"type": "BUY",
"status": "CANCELLED",
"datetime": "2022-06-08T11:01:54.000000+00:00",
"instrument_id": "LU2660424076",
"quantity": "3",
"unit_price": "3.49"
}
],
"id": "T01FH2JNYAYQ4CTHQJ1MFDDGXZQ",
"creation_datetime": "2021-10-03T08:00:16.734375+00:00",
"version": 1,
"version_datetime": "2021-10-03T08:00:16.734375+00:00",
"version_authored_by_user_id": "UXXXXXXXXXXXXXXXXXXXXXXXXXX",
"deleted": false
}
Info
Notice that the PLACED
and CANCELLED
movements are identical, apart from their status and their datetime. The datetime is the time the order was PLACED
and CANCELLED
respectively.
Correction¶
See the movement.trade_type
field, with values "CANCEL" "REBOOK" "INSTRUMENT_CHANGE" or "CORRECTION".