Savings Plans: Recurring Investments using Workflows
Learn how to set up recurring investment schemes using workflows to automate Dollar Cost Averaging (savings plans).
Recurring investments are one of the pillars of investment success. In fact, it's so popular that different regions have established totally different terms for the same thing: Dollar Cost Averaging (US) or Savings Plans (DE; Sparpläne) are popular names; however, they all share the same fundamental properties: For each of these, a fixed amount of money is invested in the same security periodically, e.g. every month.
At lemon.markets, we manage programmatic investment instructions like this using workflows. A workflow is a building block allowing automatic creation of our existing order types. As such, it complements the existing API as a pure add-on.
Create a Workflow
Workflows support two cadences:
weekly: Execute weekly on a set day. Useday_of_weekto set a custom weekday (monday–friday); otherwise defaults to the first trading day of the week.monthly: Execute monthly on a set day. Useday_of_monthto set a custom day (1–31); otherwise defaults to the first trading day of the month.
Monthly Workflow
The following example creates a workflow for account cusa_12345 to invest 12.50 € every month into an ETF:
curl -sS -H 'LMG-Data-Privacy-Access-Principal: backend-cusa_12345' \
-H 'LMG-Data-Privacy-Access-Justification: app.create_workflow' \
-H 'Authorization: Bearer <your-api-key>' \
-H 'Content-Type: application/json; charset: utf-8' \
'https://sandbox.api.lemon.markets/v1/accounts/cusa_12345/workflows?unified_orders=true' \
--data-binary '
{
"action": {
"type": "order.create_and_confirm",
"order": {
"amount": "12.5",
"currency": "EUR",
"fee": "0",
"instrument": "LU0290358497",
"side": "buy",
"type": "batch"
}
},
"trigger": {
"cadence": "monthly",
"type": "schedule"
}
}' | jqTo execute on a specific day of the month — e.g. the 15th — add day_of_month to the trigger:
"trigger": {
"type": "schedule",
"cadence": "monthly",
"day_of_month": 15
}If the chosen day exceeds the number of days in a given month, it is clamped to the last day of a month (e.g. day 31 in February → 28/29). If the resolved day falls on a weekend or trading holiday, execution rolls forward to the next trading day.
As a result, you'll see a response like this:
{
"id": "wf_c34bf754daa34de1ba7ee668a06788c9",
"trigger": {
"type": "schedule",
"cadence": "monthly"
},
"action": {
"type": "order.create_and_confirm",
"order": {
"side": "buy",
"instrument": "LU0290358497",
"amount": "12.50",
"fee": "0.00",
"currency": "EUR",
"type": "batch"
}
},
"regulatory_disclosures": {
"kid": "https://kid.dev-sandbox.lemon.markets/2023-11-17/LU0290358497-ac81558ef3e50c8eb4a01f7a68800963.pdf",
"estimated_price": "141.3010",
"estimated_quantity": "0.08846",
"estimated_amount": "12.50",
"service_costs_total": "0.00",
"service_costs_total_pct": "0.03",
"entry_costs": "0.00",
"entry_costs_pct": "0.00",
"…": "…"
},
"sca": {
"required": false,
"challenge": "aREfflZxxtFqKlxqgSAAHA=="
},
"appropriateness_consent": {
"required": false
},
"history": [
{
"status": "created",
"timestamp": "2024-04-22T14:39:34.292276+00:00"
}
]
}Weekly Workflow
To invest every week instead, set cadence to "weekly":
curl -sS -H 'LMG-Data-Privacy-Access-Principal: backend-cusa_12345' \
-H 'LMG-Data-Privacy-Access-Justification: app.create_workflow' \
-H 'Authorization: Bearer <your-api-key>' \
-H 'Content-Type: application/json; charset: utf-8' \
'https://sandbox.api.lemon.markets/v1/accounts/cusa_12345/workflows?unified_orders=true' \
--data-binary '
{
"action": {
"type": "order.create_and_confirm",
"order": {
"amount": "12.5",
"currency": "EUR",
"fee": "0",
"instrument": "LU0290358497",
"side": "buy",
"type": "batch"
}
},
"trigger": {
"cadence": "weekly",
"type": "schedule"
}
}' | jqTo execute on a specific weekday — e.g. every Wednesday — add day_of_week to the trigger:
"trigger": {
"type": "schedule",
"cadence": "weekly",
"day_of_week": "wednesday"
}Valid values for day_of_week are monday, tuesday, wednesday, thursday, and friday. If the resolved day falls on a trading holiday, execution rolls forward to the next trading day.
The response follows the same structure as the monthly workflow above, with cadence set to "weekly".
Confirm a Workflow
The created workflow requires confirmation by the customer. You can confirm a workflow on the sandbox without strong customer authentication (SCA) like this:
curl -sS -H 'LMG-Data-Privacy-Access-Principal: backend-erika.mustermann%40example.com' \
-H 'LMG-Data-Privacy-Access-Justification: create_workflow' \
-H 'Authorization: Bearer <your-api-key>' \
-H 'Content-Type: application/json; charset: utf-8' \
'https://sandbox.api.lemon.markets/v1/accounts/cusa_12345/workflows/wf_c34bf754daa34de1ba7ee668a06788c9/confirm' \
--data-binary '{}'The response contains the updated workflow. Note that regulatory disclosures, strong customer authentication, and appropriateness consent are omitted once the workflow is confirmed. The history shows the updated status, and next_run_date the actual date the workflow will run, adjusted for non-trading days.
{
"id": "wf_c34bf754daa34de1ba7ee668a06788c9",
"trigger": {
"type": "schedule",
"cadence": "monthly",
"next_run_date": "2024-05-02"
},
"action": {
"type": "order.create_and_confirm",
"order": {
"side": "buy",
"instrument": "LU0290358497",
"amount": "12.50",
"fee": "0.00",
"currency": "EUR",
"type": "batch"
}
},
"history": [
{
"status": "confirmed",
"timestamp": "2024-04-22T14:46:07.907882+00:00"
},
{
"status": "created",
"timestamp": "2024-04-22T14:39:34.292276+00:00"
}
]
}Order Creation
On the next execution day, there are three relevant points in time:
-
Workflow Cut-Off Time: In the morning of the trading day, the lemon.markets platform prepares all savings plans for execution by creating and confirming new batch orders. You will see events of type
order.created,order.confirmed, and eitherorder.acceptedororder.rejectedfor this.How cash availability is handled at this point depends on your cash setup:
- Bundled cash offering: lemon.markets checks the customer's cash balance during order confirmation. If funds are insufficient, the order is rejected.
- Unbundled cash offering: cash availability is managed on your side. Subscribe to the
order.createdwebhook and create a cash disposition for the savings plan order. If the disposition fails (e.g. due to insufficient funds), cancel the order before the batch order cut-off time.
NOTE: At this point in time, cancelling a workflow will not cancel the order that was created. Instead, both the workflow and the created order have to be canceled separately.
-
Batch Order Cut-Off Time: Later, all confirmed customer batch orders are aggregated into a single institutional order that is routed to the market. After this cut-off, individual batch orders can no longer be cancelled.
-
Execution Time: The institutional order is executed on the market (e.g. via Tradegate). The corresponding executions are reflected on the underlying customer batch orders.
As the workflow captures information like regulatory disclosures, strong customer authentication and appropriateness consent, this information will not be exposed on orders created by a workflow. Instead, the order will refer to the workflow using the workflow property:
{
"id": "bord_abd426e1a0e5478e851fca7379ea7765",
"workflow": "wf_c34bf754daa34de1ba7ee668a06788c9",
"created_at": "2024-04-23T07:00:03.740574+00:00",
"side": "buy",
"instrument": "LU0290358497",
"amount": "12.50",
"quantity": null,
"fee": "0.00",
"currency": "EUR",
"status": "accepted",
"history": [
{
"status": "accepted",
"timestamp": "2024-04-23T07:00:06.607181+00:00"
},
{
"status": "created",
"timestamp": "2024-04-23T07:00:03.740574+00:00"
}
]
}Sequence Diagram
sequenceDiagram
participant User
participant App
participant Backend
participant 🍋.markets
App->>User: Configure Plan
User->>App: Select Instrument,<br/>Amount, Schedule
App->>Backend: Create Plan
Backend->>🍋.markets: POST /workflows
🍋.markets->>Backend: 201 Created<br/>{id: wf_1234}
Backend->>App: Plan Created
App->>User: Prompt Confirmation:<br/>Instrument, Amount,<br/>Schedule, …
User->>App: Confirm Plan
App->>Backend: Confirm Plan
Backend->>🍋.markets: POST<br/>/workflows/wf_1234/confirm
🍋.markets->>Backend: 200 Success
Backend->>App: Success
App->>User: Present Confirmed Plan
Current Restrictions
- Execution is restricted to a
monthlyorweeklyschedule. The sandbox also provides adailyschedule for testing. - Workflows are immutable. If the amount should be changed, cancel the old workflow and create a new one.
- Workflows can only buy shares for now. Divestment plans are not supported yet.
If your use-case exceeds these capabilities, feel free to contact us. We love expanding our product with new partners.
Updated 7 days ago