Index: backend/pom.xml
===================================================================
--- backend/pom.xml	(revision 5c20c64185ecffb14d2efb75c8120977f8a6bcec)
+++ backend/pom.xml	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -51,4 +51,9 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-webmvc</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
         </dependency>
         <dependency>
Index: backend/src/main/java/com/trekr/backend/config/SecurityConfig.java
===================================================================
--- backend/src/main/java/com/trekr/backend/config/SecurityConfig.java	(revision 5c20c64185ecffb14d2efb75c8120977f8a6bcec)
+++ backend/src/main/java/com/trekr/backend/config/SecurityConfig.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -1,4 +1,5 @@
 package com.trekr.backend.config;
 
+import com.trekr.backend.security.JwtAuthenticationFilter;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -7,6 +8,8 @@
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.CorsConfigurationSource;
@@ -19,4 +22,10 @@
 public class SecurityConfig {
 
+    private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
+    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
+        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+    }
+
     @Bean
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
@@ -24,7 +33,12 @@
                 .csrf(AbstractHttpConfigurer::disable)
                 .cors(Customizer.withDefaults())
+                .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+                .httpBasic(AbstractHttpConfigurer::disable)
+                .formLogin(AbstractHttpConfigurer::disable)
                 .authorizeHttpRequests(auth -> auth
                         .requestMatchers("/api/auth/**").permitAll()
                         .anyRequest().authenticated());
+
+        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
         return http.build();
     }
Index: backend/src/main/java/com/trekr/backend/controller/InvestingController.java
===================================================================
--- backend/src/main/java/com/trekr/backend/controller/InvestingController.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/controller/InvestingController.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,74 @@
+package com.trekr.backend.controller;
+
+import com.trekr.backend.dto.invest.AssetDto;
+import com.trekr.backend.dto.invest.CreateAssetRequest;
+import com.trekr.backend.dto.invest.InvestingAssetsResponse;
+import com.trekr.backend.dto.invest.TrackingStatusResponse;
+import com.trekr.backend.security.UserPrincipal;
+import com.trekr.backend.service.InvestingService;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/investing")
+public class InvestingController {
+
+    private final InvestingService investingService;
+
+    public InvestingController(InvestingService investingService) {
+        this.investingService = investingService;
+    }
+
+    @GetMapping("/status")
+    public TrackingStatusResponse status(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        boolean tracking = investingService.isTracking(principal.getUserId());
+        return new TrackingStatusResponse(tracking);
+    }
+
+    @PostMapping("/start")
+    @ResponseStatus(HttpStatus.CREATED)
+    public TrackingStatusResponse start(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        investingService.startTracking(principal.getUserId());
+        return new TrackingStatusResponse(true);
+    }
+
+    @GetMapping("/assets")
+    public InvestingAssetsResponse assets(
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "5") int size,
+            Authentication authentication) {
+        if (size < 1)
+            size = 5;
+        if (size > 50)
+            size = 50;
+        if (page < 0)
+            page = 0;
+
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return investingService.getAssets(principal.getUserId(), page, size);
+    }
+
+    @PostMapping("/assets")
+    @ResponseStatus(HttpStatus.CREATED)
+    public AssetDto createAsset(
+            @Valid @RequestBody CreateAssetRequest request,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return investingService.createAsset(principal.getUserId(), request);
+    }
+
+    @DeleteMapping("/assets/{assetId}")
+    @ResponseStatus(HttpStatus.NO_CONTENT)
+    public void deleteAsset(
+            @PathVariable Long assetId,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        investingService.deleteAsset(principal.getUserId(), assetId);
+    }
+}
Index: backend/src/main/java/com/trekr/backend/controller/TrainingController.java
===================================================================
--- backend/src/main/java/com/trekr/backend/controller/TrainingController.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/controller/TrainingController.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,80 @@
+package com.trekr.backend.controller;
+
+import com.trekr.backend.dto.training.CreateTrainingSessionRequest;
+import com.trekr.backend.dto.training.TrainingSessionDto;
+import com.trekr.backend.dto.training.TrainingStartRequest;
+import com.trekr.backend.dto.training.TrainingSessionsResponse;
+import com.trekr.backend.dto.training.TrainingProfileResponse;
+import com.trekr.backend.dto.training.TrackingStatusResponse;
+import com.trekr.backend.dto.training.WorkoutTypeDto;
+import com.trekr.backend.security.UserPrincipal;
+import com.trekr.backend.service.TrainingService;
+import jakarta.validation.Valid;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/training")
+public class TrainingController {
+
+    private final TrainingService trainingService;
+
+    public TrainingController(TrainingService trainingService) {
+        this.trainingService = trainingService;
+    }
+
+    @GetMapping("/status")
+    public TrackingStatusResponse status(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        boolean tracking = trainingService.isTracking(principal.getUserId());
+        return new TrackingStatusResponse(tracking);
+    }
+
+    @PostMapping("/start")
+    @ResponseStatus(HttpStatus.CREATED)
+    public TrackingStatusResponse start(@Valid @RequestBody TrainingStartRequest request,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        trainingService.startTracking(principal.getUserId(), request);
+        return new TrackingStatusResponse(true);
+    }
+
+    @GetMapping("/sessions")
+    public TrainingSessionsResponse sessions(
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "5") int size,
+            Authentication authentication) {
+        if (size < 1)
+            size = 5;
+        if (size > 50)
+            size = 50;
+        if (page < 0)
+            page = 0;
+
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return trainingService.getSessions(principal.getUserId(), page, size);
+    }
+
+    @GetMapping("/workout-types")
+    public List<WorkoutTypeDto> workoutTypes() {
+        return trainingService.getWorkoutTypes();
+    }
+
+    @GetMapping("/profile")
+    public TrainingProfileResponse profile(Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return trainingService.getProfile(principal.getUserId());
+    }
+
+    @PostMapping("/sessions")
+    @ResponseStatus(HttpStatus.CREATED)
+    public TrainingSessionDto createSession(
+            @Valid @RequestBody CreateTrainingSessionRequest request,
+            Authentication authentication) {
+        UserPrincipal principal = (UserPrincipal) authentication.getPrincipal();
+        return trainingService.createSession(principal.getUserId(), request);
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/invest/AssetDto.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/invest/AssetDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/invest/AssetDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,64 @@
+package com.trekr.backend.dto.invest;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+public class AssetDto {
+
+    private Long assetId;
+    private String tickerSymbol;
+    private BigDecimal buyPrice;
+    private LocalDate buyDate;
+    private BigDecimal quantity;
+
+    public AssetDto() {
+    }
+
+    public AssetDto(Long assetId, String tickerSymbol, BigDecimal buyPrice, LocalDate buyDate, BigDecimal quantity) {
+        this.assetId = assetId;
+        this.tickerSymbol = tickerSymbol;
+        this.buyPrice = buyPrice;
+        this.buyDate = buyDate;
+        this.quantity = quantity;
+    }
+
+    public Long getAssetId() {
+        return assetId;
+    }
+
+    public void setAssetId(Long assetId) {
+        this.assetId = assetId;
+    }
+
+    public String getTickerSymbol() {
+        return tickerSymbol;
+    }
+
+    public void setTickerSymbol(String tickerSymbol) {
+        this.tickerSymbol = tickerSymbol;
+    }
+
+    public BigDecimal getBuyPrice() {
+        return buyPrice;
+    }
+
+    public void setBuyPrice(BigDecimal buyPrice) {
+        this.buyPrice = buyPrice;
+    }
+
+    public LocalDate getBuyDate() {
+        return buyDate;
+    }
+
+    public void setBuyDate(LocalDate buyDate) {
+        this.buyDate = buyDate;
+    }
+
+    public BigDecimal getQuantity() {
+        return quantity;
+    }
+
+    public void setQuantity(BigDecimal quantity) {
+        this.quantity = quantity;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/invest/CreateAssetRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/invest/CreateAssetRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/invest/CreateAssetRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,60 @@
+package com.trekr.backend.dto.invest;
+
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.PastOrPresent;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+public class CreateAssetRequest {
+
+    @NotBlank
+    private String tickerSymbol;
+
+    @NotNull
+    @DecimalMin(value = "0", inclusive = false)
+    private BigDecimal quantity;
+
+    @DecimalMin(value = "0", inclusive = true)
+    private BigDecimal buyPrice;
+
+    @PastOrPresent
+    private LocalDate buyDate;
+
+    public CreateAssetRequest() {
+    }
+
+    public String getTickerSymbol() {
+        return tickerSymbol;
+    }
+
+    public void setTickerSymbol(String tickerSymbol) {
+        this.tickerSymbol = tickerSymbol;
+    }
+
+    public BigDecimal getQuantity() {
+        return quantity;
+    }
+
+    public void setQuantity(BigDecimal quantity) {
+        this.quantity = quantity;
+    }
+
+    public BigDecimal getBuyPrice() {
+        return buyPrice;
+    }
+
+    public void setBuyPrice(BigDecimal buyPrice) {
+        this.buyPrice = buyPrice;
+    }
+
+    public LocalDate getBuyDate() {
+        return buyDate;
+    }
+
+    public void setBuyDate(LocalDate buyDate) {
+        this.buyDate = buyDate;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/invest/InvestingAssetsResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/invest/InvestingAssetsResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/invest/InvestingAssetsResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,33 @@
+package com.trekr.backend.dto.invest;
+
+import java.util.List;
+
+public class InvestingAssetsResponse {
+
+    private List<AssetDto> assets;
+    private boolean hasMore;
+
+    public InvestingAssetsResponse() {
+    }
+
+    public InvestingAssetsResponse(List<AssetDto> assets, boolean hasMore) {
+        this.assets = assets;
+        this.hasMore = hasMore;
+    }
+
+    public List<AssetDto> getAssets() {
+        return assets;
+    }
+
+    public void setAssets(List<AssetDto> assets) {
+        this.assets = assets;
+    }
+
+    public boolean isHasMore() {
+        return hasMore;
+    }
+
+    public void setHasMore(boolean hasMore) {
+        this.hasMore = hasMore;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/invest/TrackingStatusResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/invest/TrackingStatusResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/invest/TrackingStatusResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,21 @@
+package com.trekr.backend.dto.invest;
+
+public class TrackingStatusResponse {
+
+    private boolean tracking;
+
+    public TrackingStatusResponse() {
+    }
+
+    public TrackingStatusResponse(boolean tracking) {
+        this.tracking = tracking;
+    }
+
+    public boolean isTracking() {
+        return tracking;
+    }
+
+    public void setTracking(boolean tracking) {
+        this.tracking = tracking;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/CreateTrainingSessionRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/CreateTrainingSessionRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/CreateTrainingSessionRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,58 @@
+package com.trekr.backend.dto.training;
+
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.math.BigDecimal;
+
+public class CreateTrainingSessionRequest {
+
+    @NotBlank
+    private String type;
+
+    @NotNull
+    @DecimalMin(value = "1", inclusive = true)
+    private BigDecimal durationMinutes;
+
+    @NotNull
+    private Boolean autoCalculateCalories;
+
+    @DecimalMin(value = "0", inclusive = true)
+    private BigDecimal calories;
+
+    public CreateTrainingSessionRequest() {
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public BigDecimal getDurationMinutes() {
+        return durationMinutes;
+    }
+
+    public void setDurationMinutes(BigDecimal durationMinutes) {
+        this.durationMinutes = durationMinutes;
+    }
+
+    public Boolean getAutoCalculateCalories() {
+        return autoCalculateCalories;
+    }
+
+    public void setAutoCalculateCalories(Boolean autoCalculateCalories) {
+        this.autoCalculateCalories = autoCalculateCalories;
+    }
+
+    public BigDecimal getCalories() {
+        return calories;
+    }
+
+    public void setCalories(BigDecimal calories) {
+        this.calories = calories;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/TrackingStatusResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/TrackingStatusResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/TrackingStatusResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,21 @@
+package com.trekr.backend.dto.training;
+
+public class TrackingStatusResponse {
+
+    private boolean tracking;
+
+    public TrackingStatusResponse() {
+    }
+
+    public TrackingStatusResponse(boolean tracking) {
+        this.tracking = tracking;
+    }
+
+    public boolean isTracking() {
+        return tracking;
+    }
+
+    public void setTracking(boolean tracking) {
+        this.tracking = tracking;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/TrainingProfileResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/TrainingProfileResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/TrainingProfileResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,43 @@
+package com.trekr.backend.dto.training;
+
+import java.math.BigDecimal;
+
+public class TrainingProfileResponse {
+
+    private String gender;
+    private Integer age;
+    private BigDecimal weight;
+
+    public TrainingProfileResponse() {
+    }
+
+    public TrainingProfileResponse(String gender, Integer age, BigDecimal weight) {
+        this.gender = gender;
+        this.age = age;
+        this.weight = weight;
+    }
+
+    public String getGender() {
+        return gender;
+    }
+
+    public void setGender(String gender) {
+        this.gender = gender;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public BigDecimal getWeight() {
+        return weight;
+    }
+
+    public void setWeight(BigDecimal weight) {
+        this.weight = weight;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionDto.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,64 @@
+package com.trekr.backend.dto.training;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+public class TrainingSessionDto {
+
+    private Long trainingId;
+    private LocalDate date;
+    private String type;
+    private BigDecimal duration;
+    private BigDecimal calories;
+
+    public TrainingSessionDto() {
+    }
+
+    public TrainingSessionDto(Long trainingId, LocalDate date, String type, BigDecimal duration, BigDecimal calories) {
+        this.trainingId = trainingId;
+        this.date = date;
+        this.type = type;
+        this.duration = duration;
+        this.calories = calories;
+    }
+
+    public Long getTrainingId() {
+        return trainingId;
+    }
+
+    public void setTrainingId(Long trainingId) {
+        this.trainingId = trainingId;
+    }
+
+    public LocalDate getDate() {
+        return date;
+    }
+
+    public void setDate(LocalDate date) {
+        this.date = date;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public BigDecimal getDuration() {
+        return duration;
+    }
+
+    public void setDuration(BigDecimal duration) {
+        this.duration = duration;
+    }
+
+    public BigDecimal getCalories() {
+        return calories;
+    }
+
+    public void setCalories(BigDecimal calories) {
+        this.calories = calories;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionsResponse.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionsResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/TrainingSessionsResponse.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,33 @@
+package com.trekr.backend.dto.training;
+
+import java.util.List;
+
+public class TrainingSessionsResponse {
+
+    private List<TrainingSessionDto> sessions;
+    private boolean hasMore;
+
+    public TrainingSessionsResponse() {
+    }
+
+    public TrainingSessionsResponse(List<TrainingSessionDto> sessions, boolean hasMore) {
+        this.sessions = sessions;
+        this.hasMore = hasMore;
+    }
+
+    public List<TrainingSessionDto> getSessions() {
+        return sessions;
+    }
+
+    public void setSessions(List<TrainingSessionDto> sessions) {
+        this.sessions = sessions;
+    }
+
+    public boolean isHasMore() {
+        return hasMore;
+    }
+
+    public void setHasMore(boolean hasMore) {
+        this.hasMore = hasMore;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/TrainingStartRequest.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/TrainingStartRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/TrainingStartRequest.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,50 @@
+package com.trekr.backend.dto.training;
+
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.math.BigDecimal;
+
+public class TrainingStartRequest {
+
+    @NotBlank(message = "Gender is required")
+    private String gender;
+
+    @NotNull(message = "Age is required")
+    @Min(value = 1, message = "Age must be greater than 0")
+    @Max(value = 120, message = "Age must be realistic")
+    private Integer age;
+
+    @NotNull(message = "Weight is required")
+    @Min(value = 1, message = "Weight must be greater than 0")
+    private BigDecimal weight;
+
+    public TrainingStartRequest() {
+    }
+
+    public String getGender() {
+        return gender;
+    }
+
+    public void setGender(String gender) {
+        this.gender = gender;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public BigDecimal getWeight() {
+        return weight;
+    }
+
+    public void setWeight(BigDecimal weight) {
+        this.weight = weight;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/dto/training/WorkoutTypeDto.java
===================================================================
--- backend/src/main/java/com/trekr/backend/dto/training/WorkoutTypeDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/dto/training/WorkoutTypeDto.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,43 @@
+package com.trekr.backend.dto.training;
+
+import java.math.BigDecimal;
+
+public class WorkoutTypeDto {
+
+    private String type;
+    private String label;
+    private BigDecimal met;
+
+    public WorkoutTypeDto() {
+    }
+
+    public WorkoutTypeDto(String type, String label, BigDecimal met) {
+        this.type = type;
+        this.label = label;
+        this.met = met;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public BigDecimal getMet() {
+        return met;
+    }
+
+    public void setMet(BigDecimal met) {
+        this.met = met;
+    }
+}
Index: backend/src/main/java/com/trekr/backend/repository/AssetRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/AssetRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/repository/AssetRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,14 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.invest.Asset;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface AssetRepository extends JpaRepository<Asset, Long> {
+    Page<Asset> findByInvestorUser_UserIdOrderByAssetIdDesc(Long userId, Pageable pageable);
+
+    int deleteByAssetIdAndInvestorUser_UserId(Long assetId, Long userId);
+}
Index: backend/src/main/java/com/trekr/backend/repository/InvestorUserRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/InvestorUserRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/repository/InvestorUserRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,9 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.invest.InvestorUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface InvestorUserRepository extends JpaRepository<InvestorUser, Long> {
+}
Index: backend/src/main/java/com/trekr/backend/repository/TrainingSessionRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/TrainingSessionRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/repository/TrainingSessionRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,12 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.training.TrainingSession;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TrainingSessionRepository extends JpaRepository<TrainingSession, Long> {
+    Page<TrainingSession> findByTrainingUser_UserIdOrderByDateDesc(Long userId, Pageable pageable);
+}
Index: backend/src/main/java/com/trekr/backend/repository/TrainingUserRepository.java
===================================================================
--- backend/src/main/java/com/trekr/backend/repository/TrainingUserRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/repository/TrainingUserRepository.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,9 @@
+package com.trekr.backend.repository;
+
+import com.trekr.backend.entity.training.TrainingUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface TrainingUserRepository extends JpaRepository<TrainingUser, Long> {
+}
Index: backend/src/main/java/com/trekr/backend/security/JwtAuthenticationFilter.java
===================================================================
--- backend/src/main/java/com/trekr/backend/security/JwtAuthenticationFilter.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/security/JwtAuthenticationFilter.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,54 @@
+package com.trekr.backend.security;
+
+import com.trekr.backend.service.JwtService;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+import java.util.List;
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+    private final JwtService jwtService;
+
+    public JwtAuthenticationFilter(JwtService jwtService) {
+        this.jwtService = jwtService;
+    }
+
+    @Override
+    protected void doFilterInternal(
+            HttpServletRequest request,
+            HttpServletResponse response,
+            FilterChain filterChain) throws ServletException, IOException {
+
+        String authHeader = request.getHeader("Authorization");
+        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        String token = authHeader.substring("Bearer ".length()).trim();
+        if (token.isEmpty() || !jwtService.isTokenValid(token)) {
+            filterChain.doFilter(request, response);
+            return;
+        }
+
+        if (SecurityContextHolder.getContext().getAuthentication() == null) {
+            UserPrincipal principal = jwtService.parseUserPrincipal(token);
+            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(principal,
+                    null, List.of());
+            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+        }
+
+        filterChain.doFilter(request, response);
+    }
+}
Index: backend/src/main/java/com/trekr/backend/security/UserPrincipal.java
===================================================================
--- backend/src/main/java/com/trekr/backend/security/UserPrincipal.java	(revision 5c20c64185ecffb14d2efb75c8120977f8a6bcec)
+++ backend/src/main/java/com/trekr/backend/security/UserPrincipal.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -1,5 +1,3 @@
 package com.trekr.backend.security;
-
-import lombok.Getter;
 
 import java.io.Serializable;
@@ -9,5 +7,4 @@
  * Controllers can inject this or get it from SecurityContextHolder.
  */
-@Getter
 public class UserPrincipal implements Serializable {
     private static final long serialVersionUID = 1L;
@@ -22,3 +19,15 @@
         this.email = email;
     }
+
+    public Long getUserId() {
+        return userId;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getEmail() {
+        return email;
+    }
 }
Index: backend/src/main/java/com/trekr/backend/service/InvestingService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/InvestingService.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/service/InvestingService.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,135 @@
+package com.trekr.backend.service;
+
+import com.trekr.backend.dto.invest.AssetDto;
+import com.trekr.backend.dto.invest.CreateAssetRequest;
+import com.trekr.backend.dto.invest.InvestingAssetsResponse;
+import com.trekr.backend.entity.User;
+import com.trekr.backend.entity.invest.Asset;
+import com.trekr.backend.entity.invest.InvestorUser;
+import com.trekr.backend.repository.AssetRepository;
+import com.trekr.backend.repository.InvestorUserRepository;
+import com.trekr.backend.repository.UserRepository;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Locale;
+
+@Service
+public class InvestingService {
+
+    private final InvestorUserRepository investorUserRepository;
+    private final AssetRepository assetRepository;
+    private final UserRepository userRepository;
+
+    public InvestingService(
+            InvestorUserRepository investorUserRepository,
+            AssetRepository assetRepository,
+            UserRepository userRepository) {
+        this.investorUserRepository = investorUserRepository;
+        this.assetRepository = assetRepository;
+        this.userRepository = userRepository;
+    }
+
+    public boolean isTracking(Long userId) {
+        return investorUserRepository.existsById(userId);
+    }
+
+    @Transactional
+    public void startTracking(Long userId) {
+        if (investorUserRepository.existsById(userId)) {
+            return;
+        }
+
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User not found"));
+
+        InvestorUser investorUser = new InvestorUser();
+        investorUser.setUser(user);
+        investorUserRepository.save(investorUser);
+    }
+
+    public InvestingAssetsResponse getAssets(Long userId, int page, int size) {
+        if (!investorUserRepository.existsById(userId)) {
+            return new InvestingAssetsResponse(List.of(), false);
+        }
+
+        Page<Asset> result = assetRepository
+                .findByInvestorUser_UserIdOrderByAssetIdDesc(userId, PageRequest.of(page, size));
+
+        List<AssetDto> assets = result.getContent().stream()
+                .map(a -> new AssetDto(
+                        a.getAssetId(),
+                        a.getTickerSymbol(),
+                        a.getBuyPrice(),
+                        a.getBuyDate(),
+                        a.getQuantity()))
+                .toList();
+
+        return new InvestingAssetsResponse(assets, result.hasNext());
+    }
+
+    @Transactional
+    public AssetDto createAsset(Long userId, CreateAssetRequest request) {
+        InvestorUser investorUser = investorUserRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("Investing tracking is not enabled for this user"));
+
+        String ticker = normalizeTicker(request.getTickerSymbol());
+        if (ticker == null || ticker.isBlank()) {
+            throw new RuntimeException("Ticker symbol is required");
+        }
+
+        if (ticker.length() > 30) {
+            throw new RuntimeException("Ticker symbol is too long");
+        }
+
+        // Allow equities (AAPL), crypto pairs (BTC/USD), indices (^GSPC), FX pairs
+        // (EURUSD=X), etc.
+        if (!ticker.matches("^[A-Z0-9.^=\\-/]{1,30}$")) {
+            throw new RuntimeException("Ticker symbol contains invalid characters");
+        }
+
+        LocalDate buyDate = request.getBuyDate();
+        if (buyDate != null && buyDate.isAfter(LocalDate.now())) {
+            throw new RuntimeException("Buy date cannot be in the future");
+        }
+
+        Asset asset = new Asset();
+        asset.setInvestorUser(investorUser);
+        asset.setTickerSymbol(ticker);
+        asset.setQuantity(request.getQuantity());
+        asset.setBuyPrice(request.getBuyPrice());
+        asset.setBuyDate(buyDate);
+
+        Asset saved = assetRepository.save(asset);
+
+        return new AssetDto(
+                saved.getAssetId(),
+                saved.getTickerSymbol(),
+                saved.getBuyPrice(),
+                saved.getBuyDate(),
+                saved.getQuantity());
+    }
+
+    @Transactional
+    public void deleteAsset(Long userId, Long assetId) {
+        if (assetId == null) {
+            throw new RuntimeException("Asset not found");
+        }
+
+        int deleted = assetRepository.deleteByAssetIdAndInvestorUser_UserId(assetId, userId);
+        if (deleted == 0) {
+            throw new RuntimeException("Asset not found");
+        }
+    }
+
+    private static String normalizeTicker(String tickerSymbol) {
+        if (tickerSymbol == null) {
+            return null;
+        }
+        return tickerSymbol.trim().toUpperCase(Locale.ROOT);
+    }
+}
Index: backend/src/main/java/com/trekr/backend/service/JwtService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/JwtService.java	(revision 5c20c64185ecffb14d2efb75c8120977f8a6bcec)
+++ backend/src/main/java/com/trekr/backend/service/JwtService.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -1,5 +1,8 @@
 package com.trekr.backend.service;
 
+import com.trekr.backend.security.UserPrincipal;
 import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.security.Keys;
 import org.springframework.beans.factory.annotation.Value;
@@ -34,3 +37,28 @@
                 .compact();
     }
+
+    public boolean isTokenValid(String token) {
+        try {
+            parseClaims(token);
+            return true;
+        } catch (JwtException | IllegalArgumentException ex) {
+            return false;
+        }
+    }
+
+    public UserPrincipal parseUserPrincipal(String token) {
+        Claims claims = parseClaims(token);
+        Long userId = Long.valueOf(claims.getSubject());
+        String username = (String) claims.get("username");
+        String email = (String) claims.get("email");
+        return new UserPrincipal(userId, username, email);
+    }
+
+    private Claims parseClaims(String token) {
+        return Jwts.parser()
+                .verifyWith(signingKey)
+                .build()
+                .parseSignedClaims(token)
+                .getPayload();
+    }
 }
Index: backend/src/main/java/com/trekr/backend/service/TrainingService.java
===================================================================
--- backend/src/main/java/com/trekr/backend/service/TrainingService.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
+++ backend/src/main/java/com/trekr/backend/service/TrainingService.java	(revision 8c01a1f14ebd23dc0aeffe2d391546a92ddde9f9)
@@ -0,0 +1,222 @@
+package com.trekr.backend.service;
+
+import com.trekr.backend.dto.training.CreateTrainingSessionRequest;
+import com.trekr.backend.dto.training.TrainingSessionDto;
+import com.trekr.backend.dto.training.TrainingStartRequest;
+import com.trekr.backend.dto.training.TrainingSessionsResponse;
+import com.trekr.backend.dto.training.TrainingProfileResponse;
+import com.trekr.backend.dto.training.WorkoutTypeDto;
+import com.trekr.backend.entity.User;
+import com.trekr.backend.entity.training.TrainingSession;
+import com.trekr.backend.entity.training.TrainingUser;
+import com.trekr.backend.repository.TrainingSessionRepository;
+import com.trekr.backend.repository.TrainingUserRepository;
+import com.trekr.backend.repository.UserRepository;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Service
+public class TrainingService {
+
+    private static final Map<String, BigDecimal> MET_BY_TYPE = Map.ofEntries(
+            Map.entry("running", new BigDecimal("9.8")),
+            Map.entry("cycling", new BigDecimal("7.5")),
+            Map.entry("walking", new BigDecimal("3.5")),
+            Map.entry("strength", new BigDecimal("6.0")),
+            Map.entry("hiit", new BigDecimal("9.0")),
+            Map.entry("yoga", new BigDecimal("2.5")),
+            Map.entry("swimming", new BigDecimal("8.0")),
+            Map.entry("rowing", new BigDecimal("7.0")),
+            Map.entry("elliptical", new BigDecimal("5.0")),
+
+            // Added types (same calories formula; MET is the only extra input)
+            Map.entry("hiking", new BigDecimal("6.0")),
+            Map.entry("stair-climbing", new BigDecimal("8.8")),
+            Map.entry("jump-rope", new BigDecimal("12.3")),
+            Map.entry("boxing", new BigDecimal("7.8")),
+            Map.entry("basketball", new BigDecimal("8.0")),
+            Map.entry("soccer", new BigDecimal("10.0")),
+            Map.entry("tennis", new BigDecimal("7.3")),
+            Map.entry("dance", new BigDecimal("5.5")),
+            Map.entry("pilates", new BigDecimal("3.0")),
+            Map.entry("stretching", new BigDecimal("2.3")));
+
+    private static final List<String> WORKOUT_TYPE_ORDER = List.of(
+            "running",
+            "cycling",
+            "walking",
+            "strength",
+            "hiit",
+            "yoga",
+            "swimming",
+            "rowing",
+            "elliptical",
+            "hiking",
+            "stair-climbing",
+            "jump-rope",
+            "boxing",
+            "basketball",
+            "soccer",
+            "tennis",
+            "dance",
+            "pilates",
+            "stretching");
+
+    private final TrainingUserRepository trainingUserRepository;
+    private final TrainingSessionRepository trainingSessionRepository;
+    private final UserRepository userRepository;
+
+    public TrainingService(
+            TrainingUserRepository trainingUserRepository,
+            TrainingSessionRepository trainingSessionRepository,
+            UserRepository userRepository) {
+        this.trainingUserRepository = trainingUserRepository;
+        this.trainingSessionRepository = trainingSessionRepository;
+        this.userRepository = userRepository;
+    }
+
+    public boolean isTracking(Long userId) {
+        return trainingUserRepository.existsById(userId);
+    }
+
+    @Transactional
+    public void startTracking(Long userId, TrainingStartRequest request) {
+        if (trainingUserRepository.existsById(userId)) {
+            return;
+        }
+
+        User user = userRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("User not found"));
+
+        TrainingUser trainingUser = new TrainingUser();
+        trainingUser.setUser(user);
+        trainingUser.setGender(request.getGender());
+        trainingUser.setAge(request.getAge());
+        trainingUser.setWeight(request.getWeight());
+
+        trainingUserRepository.save(trainingUser);
+    }
+
+    public TrainingSessionsResponse getSessions(Long userId, int page, int size) {
+        if (!trainingUserRepository.existsById(userId)) {
+            return new TrainingSessionsResponse(List.of(), false);
+        }
+
+        Page<TrainingSession> result = trainingSessionRepository
+                .findByTrainingUser_UserIdOrderByDateDesc(userId, PageRequest.of(page, size));
+
+        List<TrainingSessionDto> sessions = result.getContent().stream()
+                .map(s -> new TrainingSessionDto(
+                        s.getTrainingId(),
+                        s.getDate(),
+                        s.getType(),
+                        s.getDuration(),
+                        s.getCalories()))
+                .toList();
+
+        return new TrainingSessionsResponse(sessions, result.hasNext());
+    }
+
+    public List<WorkoutTypeDto> getWorkoutTypes() {
+        return WORKOUT_TYPE_ORDER.stream()
+                .map(type -> new WorkoutTypeDto(type, labelForType(type), MET_BY_TYPE.get(type)))
+                .toList();
+    }
+
+    public TrainingProfileResponse getProfile(Long userId) {
+        TrainingUser trainingUser = trainingUserRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("Training tracking is not enabled for this user"));
+        return new TrainingProfileResponse(trainingUser.getGender(), trainingUser.getAge(), trainingUser.getWeight());
+    }
+
+    @Transactional
+    public TrainingSessionDto createSession(Long userId, CreateTrainingSessionRequest request) {
+        TrainingUser trainingUser = trainingUserRepository.findById(userId)
+                .orElseThrow(() -> new RuntimeException("Training tracking is not enabled for this user"));
+
+        String type = normalizeType(request.getType());
+        BigDecimal met = MET_BY_TYPE.get(type);
+        if (met == null) {
+            throw new RuntimeException("Unsupported workout type");
+        }
+
+        BigDecimal durationMinutes = request.getDurationMinutes();
+        if (durationMinutes == null || durationMinutes.compareTo(BigDecimal.ONE) < 0) {
+            throw new RuntimeException("Duration must be at least 1 minute");
+        }
+
+        BigDecimal calories;
+        if (Boolean.TRUE.equals(request.getAutoCalculateCalories())) {
+            calories = calculateCalories(trainingUser, met, durationMinutes);
+        } else {
+            calories = request.getCalories();
+            if (calories == null) {
+                throw new RuntimeException("Calories are required when auto-calculation is disabled");
+            }
+            if (calories.compareTo(BigDecimal.ZERO) < 0) {
+                throw new RuntimeException("Calories must be 0 or greater");
+            }
+        }
+
+        TrainingSession session = new TrainingSession();
+        session.setTrainingUser(trainingUser);
+        session.setType(type);
+        session.setDuration(durationMinutes);
+        session.setCalories(calories);
+        session.setDate(LocalDate.now());
+
+        TrainingSession saved = trainingSessionRepository.save(session);
+
+        return new TrainingSessionDto(
+                saved.getTrainingId(),
+                saved.getDate(),
+                saved.getType(),
+                saved.getDuration(),
+                saved.getCalories());
+    }
+
+    private static String normalizeType(String type) {
+        if (type == null) {
+            return null;
+        }
+        return type.trim().toLowerCase(Locale.ROOT);
+    }
+
+    private static String labelForType(String type) {
+        if (type == null || type.isBlank()) {
+            return "";
+        }
+        if ("hiit".equals(type)) {
+            return "HIIT";
+        }
+
+        String normalized = type.replace('_', '-');
+        return List.of(normalized.split("-"))
+                .stream()
+                .filter(part -> part != null && !part.isBlank())
+                .map(part -> Character.toUpperCase(part.charAt(0)) + part.substring(1))
+                .collect(Collectors.joining(" "));
+    }
+
+    private static BigDecimal calculateCalories(TrainingUser trainingUser, BigDecimal met, BigDecimal durationMinutes) {
+        BigDecimal weightKg = trainingUser.getWeight();
+        if (weightKg == null) {
+            throw new RuntimeException("Weight is required to auto-calculate calories");
+        }
+
+        BigDecimal durationHours = durationMinutes.divide(new BigDecimal("60"), 10, RoundingMode.HALF_UP);
+        return met.multiply(weightKg)
+                .multiply(durationHours)
+                .setScale(2, RoundingMode.HALF_UP);
+    }
+}
