@rivascva/dt-idl 1.1.191 → 1.1.193
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.
- package/docs/deployment.md +136 -0
- package/go/models/constants.go +100 -0
- package/go/models/structs.go +12 -0
- package/go/utils/context.go +5 -0
- package/go/utils/market.go +19 -0
- package/package.json +1 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The platform consists of four Go microservices deployed as AWS Lambda functions in the **us-east-2** region. The mobile app communicates directly with each Lambda's function URL. Asynchronous background flows are orchestrated via AWS EventBridge Schedules and AWS Step Functions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Diagram
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
┌─────────────┐ ┌─────────────────────────────┐
|
|
13
|
+
│ Mobile App │───────────▶│ AWS Lambda Function URLs │
|
|
14
|
+
└─────────────┘ └─────────────────────────────┘
|
|
15
|
+
│
|
|
16
|
+
┌──────────────┬─────────────────────┼──────────────────┐
|
|
17
|
+
│ │ │ │
|
|
18
|
+
▼ ▼ ▼ ▼
|
|
19
|
+
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
20
|
+
│ dt-market- │ │ dt-asset- │ │ dt-trade- │ │ dt-user- │
|
|
21
|
+
│ service │ │ service │ │ service │ │ service │
|
|
22
|
+
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
|
|
23
|
+
│ │ │ │
|
|
24
|
+
▼ ▼ ▼ ▼
|
|
25
|
+
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
|
|
26
|
+
│ Upstash │ │ AWS S3 │ │ Supabase │ │ Supabase │
|
|
27
|
+
│ (Redis) │ │ (Images) │ │ (Postgres)│ │ (Postgres)│
|
|
28
|
+
└───────────┘ └───────────┘ └───────────┘ └───────────┘
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Services & Lambda Functions
|
|
34
|
+
|
|
35
|
+
All Lambda functions share these settings:
|
|
36
|
+
|
|
37
|
+
- **Runtime:** Amazon Linux 2023
|
|
38
|
+
- **Architecture:** ARM64
|
|
39
|
+
- **Role:** `lambda-execution-role`
|
|
40
|
+
- **Timeout:** 30 seconds
|
|
41
|
+
- **Function URL:** Enabled w/ no auth
|
|
42
|
+
|
|
43
|
+
| Service | Lambda Function(s) |
|
|
44
|
+
| ----------------- | --------------------------------------------------- |
|
|
45
|
+
| dt-market-service | `dt-market-service-prod` |
|
|
46
|
+
| | `dt-market-service-refresh-realtime-quotes-prod` |
|
|
47
|
+
| | `dt-market-service-refresh-performance-quotes-prod` |
|
|
48
|
+
| dt-trade-service | `dt-trade-service-prod` |
|
|
49
|
+
| | `dt-trade-service-execute-pending-orders-prod` |
|
|
50
|
+
| | `dt-trade-service-sync-portfolio-history-prod` |
|
|
51
|
+
| dt-user-service | `dt-user-service-prod` |
|
|
52
|
+
| dt-asset-service | `dt-asset-service-prod` |
|
|
53
|
+
|
|
54
|
+
All functions exit early if run outside of market hours (with a grace period).
|
|
55
|
+
|
|
56
|
+
> **Console:** [Lambda Functions (us-east-2)](https://us-east-2.console.aws.amazon.com/lambda/home?region=us-east-2#/functions)
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## CI/CD Pipeline
|
|
61
|
+
|
|
62
|
+
Each service has a `.github/workflows/lambda-deploy.yaml` that triggers on:
|
|
63
|
+
|
|
64
|
+
- Push to `main`
|
|
65
|
+
- Manual dispatch (`workflow_dispatch`)
|
|
66
|
+
|
|
67
|
+
### Build Process
|
|
68
|
+
|
|
69
|
+
1. Checkout code
|
|
70
|
+
2. Set up Go (version from `go.mod`)
|
|
71
|
+
3. Configure Git for private modules via `GH_ACCESS_TOKEN`
|
|
72
|
+
4. Cross-compile: `GOOS=linux GOARCH=arm64 go build -o bootstrap <path>`
|
|
73
|
+
5. Zip: `zip function.zip bootstrap`
|
|
74
|
+
6. Configure AWS credentials (us-east-2)
|
|
75
|
+
7. Update Lambda environment variables
|
|
76
|
+
8. Wait for config propagation
|
|
77
|
+
9. Deploy new function code
|
|
78
|
+
10. Wait for code propagation
|
|
79
|
+
|
|
80
|
+
Services with asynchronous flows use a **matrix strategy** to deploy multiple Lambda functions from a single workflow run.
|
|
81
|
+
|
|
82
|
+
### Environment Variables
|
|
83
|
+
|
|
84
|
+
Each service's environment variables are managed via **GitHub Settings > Secrets and Variables** in its respective repository. The deployment workflow reads these secrets/variables at deploy time and pushes them to the Lambda function configuration.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Scheduled Flows (Background Jobs)
|
|
89
|
+
|
|
90
|
+
Background jobs are triggered by **AWS EventBridge Schedules** which invoke **AWS Step Functions** state machines.
|
|
91
|
+
|
|
92
|
+
> **Console:** [EventBridge Schedules (us-east-2)](https://us-east-2.console.aws.amazon.com/scheduler/home?region=us-east-2#schedules) · [Step Functions (us-east-2)](https://us-east-2.console.aws.amazon.com/states/home?region=us-east-2#/statemachines)
|
|
93
|
+
|
|
94
|
+
### dt-refresh-prices-schedule
|
|
95
|
+
|
|
96
|
+
| Property | Value |
|
|
97
|
+
| ------------ | ----------------------------------------------------- |
|
|
98
|
+
| **Schedule** | Every 5 minutes, 9:00 AM – 4:00 PM (America/New_York) |
|
|
99
|
+
| **Target** | `dt-refresh-prices-step-fn` |
|
|
100
|
+
|
|
101
|
+
**Step Function Execution (sequential):**
|
|
102
|
+
|
|
103
|
+
1. `dt-market-service-refresh-realtime-quotes-prod`
|
|
104
|
+
2. `dt-market-service-refresh-performance-quotes-prod`
|
|
105
|
+
3. `dt-trade-service-execute-pending-orders-prod`
|
|
106
|
+
|
|
107
|
+
### dt-sync-portfolios-schedule
|
|
108
|
+
|
|
109
|
+
| Property | Value |
|
|
110
|
+
| ------------ | ------------------------------------------------------ |
|
|
111
|
+
| **Schedule** | Every 15 minutes, 9:01 AM – 4:01 PM (America/New_York) |
|
|
112
|
+
| **Target** | `dt-sync-portfolios-step-fn` |
|
|
113
|
+
|
|
114
|
+
> The 1-minute offset (9:01 AM instead of 9:00 AM) ensures that the latest realtime quotes have already been refreshed by `dt-refresh-prices-schedule` before portfolios are synced.
|
|
115
|
+
|
|
116
|
+
**Step Function Execution:**
|
|
117
|
+
|
|
118
|
+
1. `dt-trade-service-sync-portfolio-history-prod`
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## External Services
|
|
123
|
+
|
|
124
|
+
| Service | Provider | Plan | Purpose |
|
|
125
|
+
| ----------------- | ----------------------------------------------------------------- | --------------------- | ----------------------------------- |
|
|
126
|
+
| PostgreSQL | [Supabase](https://supabase.com) | Free | Primary relational database |
|
|
127
|
+
| Redis | [Upstash](https://upstash.com) | Fixed 250 MB ($10/mo) | Caching (quotes, etc.) |
|
|
128
|
+
| Stock Market Data | [Financial Modeling Prep](https://site.financialmodelingprep.com) | $19/mo | Real-time & historical stock prices |
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Region & Networking
|
|
133
|
+
|
|
134
|
+
- **AWS Region:** `us-east-2` (Ohio)
|
|
135
|
+
- **Ingress:** Lambda function URLs are publicly accessible (auth type: `NONE`)
|
|
136
|
+
- **Inter-service communication:** Services call each other via their function URLs
|
package/go/models/constants.go
CHANGED
|
@@ -40,6 +40,106 @@ const (
|
|
|
40
40
|
MarketCloseGracePeriod = 2 * time.Minute
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
+
var (
|
|
44
|
+
// MarketHolidays is a map of future market holidays.
|
|
45
|
+
// The key is the date of the holiday in YYYY-MM-DD format (based on the timezone specified in MarketTimezone).
|
|
46
|
+
// The value is the information about the holiday.
|
|
47
|
+
MarketHolidays = map[string]MarketHolidayInfo{
|
|
48
|
+
"2026-01-01": {
|
|
49
|
+
Name: "New Year's Day",
|
|
50
|
+
CloseTime: "",
|
|
51
|
+
},
|
|
52
|
+
"2026-01-19": {
|
|
53
|
+
Name: "Martin Luther King, Jr. Day",
|
|
54
|
+
CloseTime: "",
|
|
55
|
+
},
|
|
56
|
+
"2026-02-16": {
|
|
57
|
+
Name: "Presidents' Day",
|
|
58
|
+
CloseTime: "",
|
|
59
|
+
},
|
|
60
|
+
"2026-04-03": {
|
|
61
|
+
Name: "Good Friday",
|
|
62
|
+
CloseTime: "",
|
|
63
|
+
},
|
|
64
|
+
"2026-05-25": {
|
|
65
|
+
Name: "Memorial Day",
|
|
66
|
+
CloseTime: "",
|
|
67
|
+
},
|
|
68
|
+
"2026-06-19": {
|
|
69
|
+
Name: "Juneteenth National Independence Day",
|
|
70
|
+
CloseTime: "",
|
|
71
|
+
},
|
|
72
|
+
"2026-07-03": {
|
|
73
|
+
Name: "Independence Day (Observed)",
|
|
74
|
+
CloseTime: "",
|
|
75
|
+
},
|
|
76
|
+
"2026-09-07": {
|
|
77
|
+
Name: "Labor Day",
|
|
78
|
+
CloseTime: "",
|
|
79
|
+
},
|
|
80
|
+
"2026-11-26": {
|
|
81
|
+
Name: "Thanksgiving Day",
|
|
82
|
+
CloseTime: "",
|
|
83
|
+
},
|
|
84
|
+
"2026-11-27": {
|
|
85
|
+
Name: "Friday after Thanksgiving",
|
|
86
|
+
CloseTime: "13:00",
|
|
87
|
+
},
|
|
88
|
+
"2026-12-24": {
|
|
89
|
+
Name: "Christmas Eve",
|
|
90
|
+
CloseTime: "13:00",
|
|
91
|
+
},
|
|
92
|
+
"2026-12-25": {
|
|
93
|
+
Name: "Christmas Day",
|
|
94
|
+
CloseTime: "",
|
|
95
|
+
},
|
|
96
|
+
"2027-01-01": {
|
|
97
|
+
Name: "New Year's Day",
|
|
98
|
+
CloseTime: "",
|
|
99
|
+
},
|
|
100
|
+
"2027-01-18": {
|
|
101
|
+
Name: "Martin Luther King, Jr. Day",
|
|
102
|
+
CloseTime: "",
|
|
103
|
+
},
|
|
104
|
+
"2027-02-15": {
|
|
105
|
+
Name: "Presidents' Day",
|
|
106
|
+
CloseTime: "",
|
|
107
|
+
},
|
|
108
|
+
"2027-03-26": {
|
|
109
|
+
Name: "Good Friday",
|
|
110
|
+
CloseTime: "",
|
|
111
|
+
},
|
|
112
|
+
"2027-05-31": {
|
|
113
|
+
Name: "Memorial Day",
|
|
114
|
+
CloseTime: "",
|
|
115
|
+
},
|
|
116
|
+
"2027-06-18": {
|
|
117
|
+
Name: "Juneteenth National Independence Day (Observed)",
|
|
118
|
+
CloseTime: "",
|
|
119
|
+
},
|
|
120
|
+
"2027-07-05": {
|
|
121
|
+
Name: "Independence Day (Observed)",
|
|
122
|
+
CloseTime: "",
|
|
123
|
+
},
|
|
124
|
+
"2027-09-06": {
|
|
125
|
+
Name: "Labor Day",
|
|
126
|
+
CloseTime: "",
|
|
127
|
+
},
|
|
128
|
+
"2027-11-25": {
|
|
129
|
+
Name: "Thanksgiving Day",
|
|
130
|
+
CloseTime: "",
|
|
131
|
+
},
|
|
132
|
+
"2027-11-26": {
|
|
133
|
+
Name: "Friday after Thanksgiving",
|
|
134
|
+
CloseTime: "13:00",
|
|
135
|
+
},
|
|
136
|
+
"2027-12-24": {
|
|
137
|
+
Name: "Christmas Day (Observed)",
|
|
138
|
+
CloseTime: "",
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
|
|
43
143
|
var (
|
|
44
144
|
// PortfolioDefaultCash is the default cash for new portfolios.
|
|
45
145
|
PortfolioDefaultCash = float64(50000)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package models
|
|
2
|
+
|
|
3
|
+
// MarketHolidayInfo contains information about a market holiday.
|
|
4
|
+
type MarketHolidayInfo struct {
|
|
5
|
+
// Name is the name of the holiday.
|
|
6
|
+
Name string
|
|
7
|
+
// CloseTime is the time the market closes on the holiday in a 24-hour format (HH:mm).
|
|
8
|
+
// Based on the timezone specified in MarketTimezone.
|
|
9
|
+
// For some holidays, the market may simply close early.
|
|
10
|
+
// The value will be empty if the market closes the entire day.
|
|
11
|
+
CloseTime string
|
|
12
|
+
}
|
package/go/utils/context.go
CHANGED
|
@@ -21,6 +21,11 @@ func GetAccessTokenFromContext(ctx context.Context) *jwt.Token {
|
|
|
21
21
|
return token
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// ContextWithAccessToken returns a new context with the given access token set.
|
|
25
|
+
func ContextWithAccessToken(ctx context.Context, accessToken *jwt.Token) context.Context {
|
|
26
|
+
return context.WithValue(ctx, models.AccessTokenKey, accessToken)
|
|
27
|
+
}
|
|
28
|
+
|
|
24
29
|
// GetActorIdFromContext retrieves the actor id from the JWT access token in the given context.
|
|
25
30
|
// The actor id is the subject of the JWT access token that represents the user id or service name that initiated the request.
|
|
26
31
|
func GetActorIdFromContext(ctx context.Context) (string, error) {
|
package/go/utils/market.go
CHANGED
|
@@ -66,6 +66,25 @@ func isMarketHoursWithOpenAndCloseOffsets(now time.Time, openOffset time.Duratio
|
|
|
66
66
|
return false, fmt.Errorf("unable to parse the close minute: %w", err)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// check if on a market holiday
|
|
70
|
+
if holidayInfo, ok := models.MarketHolidays[now.Format("2006-01-02")]; ok {
|
|
71
|
+
if holidayInfo.CloseTime == "" {
|
|
72
|
+
// the market closes the entire day
|
|
73
|
+
return false, nil
|
|
74
|
+
} else {
|
|
75
|
+
// the market closes early, so set the close time to the holiday close time
|
|
76
|
+
holidayCloseTimeParts := strings.Split(holidayInfo.CloseTime, ":")
|
|
77
|
+
closeHour, err = strconv.Atoi(holidayCloseTimeParts[0])
|
|
78
|
+
if err != nil {
|
|
79
|
+
return false, fmt.Errorf("unable to parse the holiday close hour: %w", err)
|
|
80
|
+
}
|
|
81
|
+
closeMinute, err = strconv.Atoi(holidayCloseTimeParts[1])
|
|
82
|
+
if err != nil {
|
|
83
|
+
return false, fmt.Errorf("unable to parse the holiday close minute: %w", err)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
// create the open time with the offset
|
|
70
89
|
openTime := time.Date(now.Year(), now.Month(), now.Day(), openHour, openMinute, 0, 0, loc)
|
|
71
90
|
openWithOffset := openTime.Add(openOffset)
|