Building an AI-Driven Financial Forecasting Application with ASP.NET, ML.NET, and OpenAI

Building an AI-Driven Financial Forecasting Application with ASP.NET, ML.NET, and OpenAI

 

In today's fast-paced financial markets, the ability to predict stock trends can offer a significant edge to investors and analysts alike. Combining the robustness of ASP.NET with the power of ML.NET and OpenAI's advanced language models, you can create a sophisticated financial forecasting application. This article walks you through the process of developing such an application, integrating stock data APIs, implementing machine learning models, and designing an intuitive user interface.

Table of Contents

  1. Introduction
  2. Project Overview
  3. Prerequisites
  4. Setting Up the ASP.NET Core Project
  5. Integrating Stock Data APIs
  6. Implementing AI Financial Forecasting
  7. Creating Web API Endpoints
  8. Developing Razor Views
  9. Visualizing Forecast Data
  10. Testing and Deployment
  11. Conclusion
  12. Additional Resources

Introduction

Financial forecasting has evolved significantly with the advent of Artificial Intelligence (AI). By harnessing machine learning and advanced language models, applications can analyze vast datasets to predict future stock prices with remarkable accuracy. This guide demonstrates how to build an AI-driven financial forecasting application using ASP.NET Core, ML.NET for traditional machine learning, and OpenAI's language models for enhanced predictive capabilities.

Project Overview

The application aims to provide users with AI-based financial forecasts based on input stock symbols. It encompasses:

  • Backend: ASP.NET Web API for data handling and processing.
  • Frontend: ASP.NET Razor Views for an interactive user interface.
  • AI Components:
    • ML.NET: For building and deploying machine learning models.
    • OpenAI Models: To leverage advanced language processing for generating forecasts.

The interplay between these components ensures seamless data flow from user input to AI-driven predictions, culminating in an informative and interactive user experience.

Prerequisites

Before you begin, ensure you have the following:

  • Development Environment: Visual Studio 2022 or later.
  • .NET SDK: .NET 6.0 or higher.
  • API Keys:
  • Knowledge Base:
    • Familiarity with ASP.NET Core and Razor Pages.
    • Understanding of Web API principles.
    • Basic concepts of machine learning and AI.

Having a solid grasp of these areas will facilitate a smoother development experience.

Setting Up the ASP.NET Core Project

Creating a New ASP.NET Core Project

  1. Launch Visual Studio and select "Create a new project".
  2. Choose the "ASP.NET Core Web App (Model-View-Controller)" template and click "Next".
  3. Configure Project Details:
    • Project Name: `StockForecastingApp`.
    • Location: Choose your preferred directory.
    • Solution Name: Typically the same as the project name.
  4. Click "Create".
  5. In the "Additional Information" dialog:
    • Framework: Select .NET 6.0 or later.
    • Authentication: Select "No Authentication" for simplicity.
  6. Click "Create" to generate the project.

Understanding the Project Structure

The project structure includes:

  • Controllers: Handles HTTP requests and responses.
  • Models: Defines data structures.
  • Views: Contains Razor Views for the frontend.
  • wwwroot: Hosts static files like JavaScript, CSS, and images.

Familiarizing yourself with this structure is crucial as it forms the foundation for further development.

Integrating Stock Data APIs

To forecast stock prices accurately, the application needs access to real-time and historical stock data.

Obtaining an API Key

  1. Sign Up: Visit Alpha Vantage and sign up for a free API key.
  2. Secure Storage: Store the API key securely using environment variables or secure configuration files to prevent exposure.

Creating a Service to Fetch Stock Data

Defining Data Models

Create data models to represent stock data.

// Models/StockData.cs
public class StockData
{
    public string Symbol { get; set; }
    public List<StockPrice> Prices { get; set; }
}

public class StockPrice
{
    public DateTime Date { get; set; }
    public decimal Close { get; set; }
}

Implementing the StockService

Develop a service to communicate with the Alpha Vantage API.

// Services/StockService.cs
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public class StockService
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey = "YOUR_ALPHA_VANTAGE_API_KEY"; // Replace with your API key

    public StockService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<StockData> GetStockDataAsync(string symbol)
    {
        var url = $"https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol={symbol}&apikey={_apiKey}&outputsize=compact";
        var response = await _httpClient.GetAsync(url);
        response.EnsureSuccessStatusCode();
        var json = await response.Content.ReadAsStringAsync();
        using JsonDocument doc = JsonDocument.Parse(json);
        var timeSeries = doc.RootElement.GetProperty("Time Series (Daily)");

        var prices = new List<StockPrice>();
        foreach (var property in timeSeries.EnumerateObject())
        {
            prices.Add(new StockPrice
            {
                Date = DateTime.Parse(property.Name),
                Close = decimal.Parse(property.Value.GetProperty("4. close").GetString())
            });
        }

        return new StockData
        {
            Symbol = symbol,
            Prices = prices.OrderBy(p => p.Date).ToList()
        };
    }
}

Security Note: Replace "YOUR_ALPHA_VANTAGE_API_KEY" with your actual API key. Use secure methods to store and access API keys.

Registering the StockService with Dependency Injection

Register the StockService in the dependency injection container to enable its use throughout the application.

// Program.cs or Startup.cs
builder.Services.AddHttpClient<StockService>();

Implementing AI Financial Forecasting

AI enhances the application's forecasting capabilities through machine learning models and advanced language processing.

Traditional Machine Learning with ML.NET

Installing ML.NET Packages

Add the necessary ML.NET packages to your project:

dotnet add package Microsoft.ML
dotnet add package Microsoft.ML.TimeSeries

Creating the Forecasting Model

Develop a service that uses ML.NET to predict future stock prices.

Defining Prediction Models
// Models/StockPricePrediction.cs
public class StockPricePrediction
{
    public DateTime Date { get; set; }
    public float PredictedClose { get; set; }
}
Implementing the ForecastService
// Services/ForecastService.cs
using Microsoft.ML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.Linq;

public class ForecastService
{
    private readonly MLContext _mlContext;

    public ForecastService()
    {
        _mlContext = new MLContext();
    }

    public List<StockPricePrediction> Forecast(List<StockPrice> historicalData, int forecastDays = 30)
    {
        var data = historicalData.Select(p => new StockDataPoint { Date = p.Date, Close = (float)p.Close }).ToList();
        var dataView = _mlContext.Data.LoadFromEnumerable(data);

        var forecastingPipeline = _mlContext.Forecasting.ForecastBySsa(
            outputColumnName: "ForecastedClose",
            inputColumnName: "Close",
            windowSize: 30,
            seriesLength: 100,
            trainSize: data.Count,
            horizon: forecastDays,
            confidenceLevel: 0.95f,
            confidenceLowerBoundColumn: "LowerBoundClose",
            confidenceUpperBoundColumn: "UpperBoundClose"
        );

        var model = forecastingPipeline.Fit(dataView);
        var forecastEngine = model.CreateTimeSeriesEngine<StockDataPoint, StockForecast>(_mlContext);
        var forecast = forecastEngine.Predict();

        var predictions = new List<StockPricePrediction>();
        DateTime lastDate = historicalData.Last().Date;

        for (int i = 0; i < forecast.ForecastedClose.Length; i++)
        {
            predictions.Add(new StockPricePrediction
            {
                Date = lastDate.AddDays(i + 1),
                PredictedClose = forecast.ForecastedClose[i]
            });
        }

        return predictions;
    }
}

public class StockDataPoint
{
    public DateTime Date { get; set; }
    public float Close { get; set; }
}

public class StockForecast
{
    public float[] ForecastedClose { get; set; }
    public float[] LowerBoundClose { get; set; }
    public float[] UpperBoundClose { get; set; }
}

Parameter Explanation:

  • windowSize: Determines the number of previous data points used to make a prediction.
  • seriesLength: Length of the input time series.
  • trainSize: Number of data points used for training the model.
  • horizon: Number of future points to predict.
  • confidenceLevel: Probability that the forecast lies within the prediction interval.

Registering the ForecastService

Add the ForecastService to the dependency injection container.

// Program.cs or Startup.cs
builder.Services.AddSingleton<ForecastService>();

Leveraging OpenAI Models

OpenAI's advanced language models, such as GPT-4, can be harnessed to enhance financial forecasting by interpreting complex data patterns, generating insightful analyses, and even predicting trends based on vast amounts of information.

Obtaining an OpenAI API Key

  1. Sign Up: Visit OpenAI API and create an account.
  2. Generate API Key: Create a new API key for your application.
  3. Secure Storage: Store the API key securely using environment variables or secure configuration files.

Installing Required Packages

Use the OpenAI-API-dotnet package to interact with OpenAI's API:

dotnet add package OpenAI-API

Creating the OpenAIService

Develop a service to handle interactions with OpenAI's API.

// Services/OpenAIService.cs
using OpenAI_API;
using OpenAI_API.Completions;
using System.Collections.Generic;
using System.Threading.Tasks;

public class OpenAIService
{
    private readonly OpenAIAPI _api;

    public OpenAIService(string apiKey)
    {
        _api = new OpenAIAPI(apiKey);
    }

    public async Task<string> GetForecastAsync(string symbol, List<StockPrice> historicalData, int forecastDays = 30)
    {
        // Prepare the prompt with historical data
        string prompt = GeneratePrompt(symbol, historicalData, forecastDays);

        var completionRequest = new CompletionRequest
        {
            Prompt = prompt,
            Model = OpenAI_API.Models.Model.DavinciText, // Choose appropriate model
            MaxTokens = 150,
            Temperature = 0.5,
            TopP = 1.0,
            FrequencyPenalty = 0.0,
            PresencePenalty = 0.0,
            StopSequences = new List<string> { "\n" }
        };

        var result = await _api.Completions.CreateCompletionAsync(completionRequest);
        return result.Completions[0].Text.Trim();
    }

    private string GeneratePrompt(string symbol, List<StockPrice> historicalData, int forecastDays)
    {
        // Construct a detailed prompt for OpenAI
        var prompt = $"You are a financial analyst. Based on the following historical closing prices for {symbol}, provide a forecast for the next {forecastDays} days.\n\n";

        foreach (var price in historicalData)
        {
            prompt += $"{price.Date.ToString("yyyy-MM-dd")}: {price.Close}\n";
        }

        prompt += "\nForecast:\n";
        return prompt;
    }
}

Registering the OpenAIService

Add the OpenAIService to the dependency injection container, ensuring the API key is securely passed.

// Program.cs or Startup.cs
builder.Services.AddSingleton<OpenAIService>(provider =>
{
    var configuration = provider.GetRequiredService<IConfiguration>();
    var apiKey = configuration["OpenAI:ApiKey"]; // Ensure this is set in your configuration
    return new OpenAIService(apiKey);
});

Configuration:

// appsettings.json
{
  "OpenAI": {
    "ApiKey": "YOUR_OPENAI_API_KEY"
  },
  // ... other settings
}

Security Note: Avoid hard-coding API keys. Use secure methods like environment variables or secret managers to store sensitive information.

Implementing the OpenAIForecastService

// Services/OpenAIForecastService.cs
using System.Collections.Generic;
using System.Threading.Tasks;

public class OpenAIForecastService
{
    private readonly OpenAIService _openAIService;

    public OpenAIForecastService(OpenAIService openAIService)
    {
        _openAIService = openAIService;
    }

    public async Task<string> GetForecastAsync(string symbol, List<StockPrice> historicalData, int forecastDays = 30)
    {
        return await _openAIService.GetForecastAsync(symbol, historicalData, forecastDays);
    }
}

Registering the OpenAIForecastService

Add the OpenAIForecastService to the dependency injection container.

// Program.cs or Startup.cs
builder.Services.AddSingleton<OpenAIForecastService>();

Choosing Between ML.NET and OpenAI Models

Both ML.NET and OpenAI offer unique advantages:

  • ML.NET:
    • Pros: Tailored for structured numerical data, lower latency, no external dependencies post-training.
    • Cons: Requires manual feature engineering, limited in handling unstructured data.
  • OpenAI Models:
    • Pros: Capable of understanding complex patterns, can incorporate unstructured data, no need for manual training.
    • Cons: Dependent on external API calls, potential costs, limited control over model behavior.

Recommendation: Use ML.NET for precise numerical forecasting and OpenAI for generating detailed analytical insights or handling more nuanced forecasting scenarios.

Creating Web API Endpoints

Web APIs bridge the frontend and backend, managing requests and delivering responses.

Developing the ForecastController

Create a controller that exposes endpoints for both ML.NET and OpenAI forecasts.

// Controllers/ForecastController.cs
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class ForecastController : ControllerBase
{
    private readonly StockService _stockService;
    private readonly ForecastService _forecastService;
    private readonly OpenAIForecastService _openAIForecastService;

    public ForecastController(StockService stockService, ForecastService forecastService, OpenAIForecastService openAIForecastService)
    {
        _stockService = stockService;
        _forecastService = forecastService;
        _openAIForecastService = openAIForecastService;
    }

    [HttpGet("{symbol}/mlnet")]
    public async Task GetMLNetForecast(string symbol, int days = 30)
    {
        var stockData = await _stockService.GetStockDataAsync(symbol);
        var predictions = _forecastService.Forecast(stockData.Prices, days);
        return Ok(new { Symbol = symbol, ForecastMethod = "ML.NET", Predictions = predictions });
    }

    [HttpGet("{symbol}/openai")]
    public async Task GetOpenAIForecast(string symbol, int days = 30)
    {
        var stockData = await _stockService.GetStockDataAsync(symbol);
        var forecastText = await _openAIForecastService.GetForecastAsync(symbol, stockData.Prices, days);
        return Ok(new { Symbol = symbol, ForecastMethod = "OpenAI", Forecast = forecastText });
    }
}

Endpoint Details:

  • ML.NET Forecast:
    • Route: api/forecast/{symbol}/mlnet
    • Parameters: symbol (stock symbol), days (optional, default 30)
    • Response: Returns the symbol, forecast method, and predicted closing prices.
  • OpenAI Forecast:
    • Route: api/forecast/{symbol}/openai
    • Parameters: symbol (stock symbol), days (optional, default 30)
    • Response: Returns the symbol, forecast method, and textual forecast.

Enabling Cross-Origin Resource Sharing (CORS)

If your frontend is hosted on a different domain or port, configure CORS to allow cross-origin requests.

// Program.cs or Startup.cs
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll",
        builder => builder.AllowAnyOrigin()
                          .AllowAnyMethod()
                          .AllowAnyHeader());
});

app.UseCors("AllowAll");

Security Consideration: While allowing all origins is suitable for development, restrict origins in production to enhance security.

Developing Razor Views

Razor Views enable the creation of dynamic and interactive frontend interfaces.

Designing the Home Page

Create a Razor View that allows users to input stock symbols and view forecasts from both ML.NET and OpenAI.


@page
@model IndexModel
@{
    ViewData["Title"] = "Stock Forecast";
}

AI Stock Forecast



@if (Model.MLNetPredictions != null || !string.IsNullOrEmpty(Model.OpenAIForecast))
{

Forecast Results for @Model.Symbol



    @if (Model.MLNetPredictions != null)
    {

ML.NET Forecast


        
    }

    @if (!string.IsNullOrEmpty(Model.OpenAIForecast))
    {

OpenAI Forecast

@Model.OpenAIForecast

    }
}

@if (!string.IsNullOrEmpty(Model.ErrorMessage))
{
@Model.ErrorMessage

}

@section Scripts {
    @if (Model.MLNetPredictions != null)
    {
        
        
    }

    @if (!string.IsNullOrEmpty(Model.OpenAIForecast))
    {
        // Additional scripts for OpenAI-generated content can be added here if needed
    }
}

Creating the Page Model

Implement the backend logic to handle form submissions and fetch forecasts from both ML.NET and OpenAI.

// Pages/Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;

public class IndexModel : PageModel
{
    private readonly IHttpClientFactory _httpClientFactory;

    [BindProperty]
    public string Symbol { get; set; }

    public string ErrorMessage { get; set; }

    public List<StockPricePrediction> MLNetPredictions { get; set; }

    public string OpenAIForecast { get; set; }

    public string ForecastSymbol { get; set; }

    public IndexModel(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (string.IsNullOrEmpty(Symbol))
        {
            ErrorMessage = "Please enter a stock symbol.";
            return Page();
        }

        var client = _httpClientFactory.CreateClient();

        // Fetch ML.NET Forecast
        var mlnetApiUrl = $"api/forecast/{Symbol}/mlnet?days=30";
        var mlnetResponse = await client.GetAsync(mlnetApiUrl);
        if (mlnetResponse.IsSuccessStatusCode)
        {
            var mlnetJsonString = await mlnetResponse.Content.ReadAsStringAsync();
            var mlnetForecastResult = JsonConvert.DeserializeObject<MLNetForecastResult>(mlnetJsonString);
            MLNetPredictions = mlnetForecastResult.Predictions;
            ForecastSymbol = mlnetForecastResult.Symbol;
        }
        else
        {
            ErrorMessage += " Failed to retrieve ML.NET forecast data.";
        }

        // Fetch OpenAI Forecast
        var openaiApiUrl = $"api/forecast/{Symbol}/openai?days=30";
        var openaiResponse = await client.GetAsync(openaiApiUrl);
        if (openaiResponse.IsSuccessStatusCode)
        {
            var openaiJsonString = await openaiResponse.Content.ReadAsStringAsync();
            var openaiForecastResult = JsonConvert.DeserializeObject<OpenAIForecastResult>(openaiJsonString);
            OpenAIForecast = openaiForecastResult.Forecast;
            ForecastSymbol = openaiForecastResult.Symbol;
        }
        else
        {
            ErrorMessage += " Failed to retrieve OpenAI forecast data.";
        }

        return Page();
    }

    public class MLNetForecastResult
    {
        public string Symbol { get; set; }
        public string ForecastMethod { get; set; }
        public List<StockPricePrediction> Predictions { get; set; }
    }

    public class OpenAIForecastResult
    {
        public string Symbol { get; set; }
        public string ForecastMethod { get; set; }
        public string Forecast { get; set; }
    }
}

Key Points:

  • Form Handling: Captures the stock symbol input by the user.
  • API Calls: Fetches forecasts from both ML.NET and OpenAI endpoints.
  • Error Aggregation: Accumulates error messages if any of the API calls fail.
  • Data Binding: Populates the view with forecast data upon successful retrieval.

Visualizing Forecast Data

Effective visualization helps users interpret forecast data easily.

Integrating Chart.js for ML.NET Forecast

Chart.js is used to render the ML.NET-generated forecast in a line chart.

@section Scripts {
    @if (Model.MLNetPredictions != null)
    {
        
        
    }

    @if (!string.IsNullOrEmpty(Model.OpenAIForecast))
    {
        // Additional scripts for OpenAI-generated content can be added here if needed
    }
}

Features:

  • Dynamic Data Binding: The mlnetPredictions variable is populated with JSON data from the server.
  • Chart Configuration: Sets up a responsive line chart with time-based x-axis.

Displaying OpenAI Forecast

The OpenAI forecast is displayed as a textual analysis within an alert box.

@if (!string.IsNullOrEmpty(Model.OpenAIForecast))
{

OpenAI Forecast

@Model.OpenAIForecast

}

Enhancements:

  • Styling: Use CSS to enhance readability and aesthetics.
  • Interactivity: Allow users to expand or collapse the forecast text for better user experience.

Testing and Deployment

Ensuring your application works seamlessly across different scenarios is crucial.

Running the Application Locally

  1. Start the Application: Press F5 in Visual Studio to launch the application.
  2. Access the Home Page: Navigate to https://localhost:5001 or the specified URL.
  3. Input Stock Symbol: Enter a valid stock symbol (e.g., AAPL) and submit the form.
  4. Review Forecasts: View both ML.NET and OpenAI-generated forecasts.

Handling Edge Cases and Errors

Implement robust error handling to manage potential issues:

  • Invalid Symbols: Notify users if the entered stock symbol is invalid or not found.
  • API Rate Limits: Monitor and handle API rate limiting responses gracefully.
  • Network Issues: Inform users of connectivity problems affecting data retrieval.
  • Data Validation: Ensure input data adheres to expected formats and ranges.
  • OpenAI Response Handling: Validate and sanitize responses to prevent injection of malicious content.

Implementing Caching Mechanisms

To optimize performance and reduce API calls:

  • Cache Responses: Store recent forecasts and reuse them for identical requests within a specified timeframe.
  • Invalidate Cache: Establish rules to refresh cached data periodically to maintain accuracy.

Implementing caching enhances application responsiveness and efficiency.

Deploying the Application

Choose a hosting platform to make your application accessible:

  • Azure App Service: Seamless integration with ASP.NET Core applications.
  • Amazon Web Services (AWS): Flexible hosting solutions with services like Elastic Beanstalk.
  • Internet Information Services (IIS): Suitable for on-premises or dedicated server deployments.

Deployment Steps:

  1. Publish the Application: Use Visual Studio's publishing tools to build and prepare the application.
  2. Configure Environment Variables: Securely manage API keys and other sensitive configurations in the production environment.
  3. Enable HTTPS: Ensure secure data transmission by enforcing HTTPS.
  4. Monitor and Scale: Utilize monitoring tools to track application performance and scale resources as needed.
  5. Implement CI/CD: Set up Continuous Integration/Continuous Deployment pipelines for streamlined updates.

Conclusion

Combining ASP.NET Core with ML.NET and OpenAI models enables the creation of a powerful financial forecasting application. By integrating stock data APIs, implementing both traditional and advanced AI models, and designing an intuitive user interface, you can provide users with comprehensive and insightful forecasts.

As financial markets continue to evolve, consider enhancing your application with features like user authentication, historical data visualization, advanced AI models, real-time updates, and responsive design to meet the growing demands of users. Continuous testing, optimization, and user feedback will further refine the application, ensuring it remains a valuable tool in the dynamic world of stock markets.

Additional Resources

Back to blog
  • ChatGPT Uncovered Podcast

    ChatGPT Uncovered Podcast

    Pedro Martins

    ChatGPT Uncovered Podcast ChatGPT Uncovered Podcast Exploring the Frontiers of AI Conversational Models Episode 1: Understanding ChatGPT Published on: May 15, 2023 Your browser does not support the audio element....

    ChatGPT Uncovered Podcast

    Pedro Martins

    ChatGPT Uncovered Podcast ChatGPT Uncovered Podcast Exploring the Frontiers of AI Conversational Models Episode 1: Understanding ChatGPT Published on: May 15, 2023 Your browser does not support the audio element....

  • Power Apps In-Depth Podcast

    Power Apps In-Depth Podcast

    Pedro Martins

    Power Apps In-Depth Podcast Power Apps In-Depth Podcast Exploring the Capabilities of Microsoft Power Apps Episode 1: Introduction to Power Apps Published on: April 20, 2023 Your browser does not...

    Power Apps In-Depth Podcast

    Pedro Martins

    Power Apps In-Depth Podcast Power Apps In-Depth Podcast Exploring the Capabilities of Microsoft Power Apps Episode 1: Introduction to Power Apps Published on: April 20, 2023 Your browser does not...

  • Exploring Power Pages Podcast

    Exploring Power Pages Podcast

    Pedro Martins

    Exploring Power Pages Podcast Exploring Power Pages Podcast Delving into the World of Microsoft Power Pages Episode 1: Getting Started with Power Pages Published on: March 10, 2023 Your browser...

    Exploring Power Pages Podcast

    Pedro Martins

    Exploring Power Pages Podcast Exploring Power Pages Podcast Delving into the World of Microsoft Power Pages Episode 1: Getting Started with Power Pages Published on: March 10, 2023 Your browser...

1 of 3