Create API for price data.
This commit is contained in:
parent
f5e2d70f57
commit
08825894f4
43
Backend.cs
Normal file
43
Backend.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Backend.ServiceHander;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
builder.Services.AddSingleton<MarketDataService>();
|
||||
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("Frontend", policy =>
|
||||
{
|
||||
policy
|
||||
// ---- DEVELOPMENT ----
|
||||
.AllowAnyOrigin()
|
||||
// ---- PRODUCTION ----
|
||||
// .WithOrigins("https://app.dongfeng-systems.org")
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod();
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||
{
|
||||
options.ForwardedHeaders =
|
||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
|
||||
app.UseForwardedHeaders();
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseCors("Frontend");
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
@ -10,5 +10,10 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../twsapi/CSharpAPI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
89
Controller/MarketDataController.cs
Normal file
89
Controller/MarketDataController.cs
Normal file
@ -0,0 +1,89 @@
|
||||
using Backend.ServiceHander;
|
||||
using Backend.MarketDataRequest;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using IBApi;
|
||||
|
||||
namespace BackendApi.Controller
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/market")]
|
||||
public class MarketController : ControllerBase
|
||||
{
|
||||
private readonly MarketDataService _marketDataService;
|
||||
|
||||
public MarketController(MarketDataService marketDataService)
|
||||
{
|
||||
_marketDataService = marketDataService;
|
||||
}
|
||||
|
||||
[HttpGet("live/{symbol}")]
|
||||
public IActionResult GetLive(string symbol)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _marketDataService.GetLiveQuote(symbol.ToUpperInvariant());
|
||||
return Ok(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { error = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("historical")]
|
||||
public IActionResult GetHistorical(
|
||||
[FromQuery] string symbol,
|
||||
[FromQuery] string endDateTime,
|
||||
[FromQuery] string duration,
|
||||
[FromQuery] HistoricalBarSize barSize,
|
||||
[FromQuery] HistoricalWhatToShow whatToShow = HistoricalWhatToShow.Trades,
|
||||
[FromQuery] bool useRth = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _marketDataService.GetHistorical(
|
||||
symbol.ToUpperInvariant(),
|
||||
endDateTime,
|
||||
duration,
|
||||
barSize,
|
||||
whatToShow,
|
||||
useRth
|
||||
);
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { error = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("historical-line")]
|
||||
public IActionResult GetHistoricalLine(
|
||||
[FromQuery] string symbol,
|
||||
[FromQuery] string endDateTime,
|
||||
[FromQuery] string duration,
|
||||
[FromQuery] HistoricalBarSize barSize,
|
||||
[FromQuery] HistoricalWhatToShow whatToShow = HistoricalWhatToShow.Trades,
|
||||
[FromQuery] bool useRth = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _marketDataService.GetHistoricalLine(
|
||||
symbol.ToUpperInvariant(),
|
||||
endDateTime,
|
||||
duration,
|
||||
barSize,
|
||||
whatToShow,
|
||||
useRth
|
||||
);
|
||||
|
||||
return Ok(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return StatusCode(500, new { error = ex.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,9 @@ namespace Backend.MarketDataRequest
|
||||
FiveMins,
|
||||
FifteenMins,
|
||||
OneHour,
|
||||
OneDay
|
||||
OneDay,
|
||||
FiveDays,
|
||||
OneMonth
|
||||
}
|
||||
|
||||
public enum HistoricalWhatToShow
|
||||
@ -17,6 +19,12 @@ namespace Backend.MarketDataRequest
|
||||
Midpoint
|
||||
}
|
||||
|
||||
public sealed class HistoricalLinePoint
|
||||
{
|
||||
public string Timestamp { get; init; } = string.Empty;
|
||||
public double Price { get; init; }
|
||||
}
|
||||
|
||||
public sealed class HistoricalEquityRequest
|
||||
{
|
||||
public int RequestId { get; init; }
|
||||
@ -62,6 +70,8 @@ namespace Backend.MarketDataRequest
|
||||
HistoricalBarSize.FifteenMins => "15 mins",
|
||||
HistoricalBarSize.OneHour => "1 hour",
|
||||
HistoricalBarSize.OneDay => "1 day",
|
||||
HistoricalBarSize.FiveDays => "5 days",
|
||||
HistoricalBarSize.OneMonth => "1 month",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(BarSize))
|
||||
};
|
||||
|
||||
|
||||
100
Program.cs
100
Program.cs
@ -1,100 +0,0 @@
|
||||
using Backend.MarketDataRequest;
|
||||
using IBApi;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Backend
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var Gateway = new IBGatewayControl();
|
||||
try
|
||||
{
|
||||
Gateway.Start("127.0.0.1", 4002, 1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Invalid connection to IB Gateway. Please ensure gateway is running and connected.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
while (!Gateway.IsApiReady)
|
||||
{
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
// ---- TEST HISTORICAL DATA REQUEST ----
|
||||
|
||||
var HistoricalRequest = new HistoricalEquityRequest
|
||||
{
|
||||
RequestId = 1,
|
||||
Symbol = "NVDA",
|
||||
EndDateTime = "20260311 11:00:00 US/Eastern",
|
||||
Duration = "3600 S",
|
||||
BarSize = HistoricalBarSize.FiveMins,
|
||||
WhatToShow = HistoricalWhatToShow.Trades,
|
||||
UseRth = true,
|
||||
KeepUpToDate = false,
|
||||
FormatDate = 1
|
||||
};
|
||||
|
||||
List<Bar> Bars = Gateway.RequestHistoricalBlocking(HistoricalRequest, TimeSpan.FromSeconds(15));
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Received {Bars.Count} bars. Continue program here.");
|
||||
|
||||
// ---- TEST LIVE DATA REQUEST ----
|
||||
|
||||
var liveRequest = new LiveEquityRequest
|
||||
{
|
||||
RequestId = 2,
|
||||
Symbol = "NVDA"
|
||||
};
|
||||
|
||||
var live = Gateway.RequestLiveEquityBlocking(
|
||||
liveRequest,
|
||||
TimeSpan.FromSeconds(15));
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("=== Live Quote ===");
|
||||
Console.WriteLine($"Symbol: {live.Symbol}");
|
||||
Console.WriteLine($"Last Price: {live.LastPrice}");
|
||||
Console.WriteLine($"Volume: {live.Volume}");
|
||||
Console.WriteLine($"Previous Close: {live.PreviousClose}");
|
||||
Console.WriteLine($"Open: {live.Open}");
|
||||
|
||||
|
||||
// ---- FUNDAMENTALS DATA REQUEST ----
|
||||
// DOES NOT WORK, NEEDS EXTRA DATA SUBSCRIPTION
|
||||
/*
|
||||
var fundamentalsRequest = new FundamentalsEquityRequest
|
||||
{
|
||||
RequestId = 3,
|
||||
Symbol = "NVDA"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var fundamentals = Gateway.RequestFundamentalsBlocking(
|
||||
fundamentalsRequest,
|
||||
TimeSpan.FromSeconds(15));
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("=== Fundamentals ===");
|
||||
Console.WriteLine($"Symbol: {fundamentals.Symbol}");
|
||||
Console.WriteLine($"Market Cap: {fundamentals.MarketCap}");
|
||||
Console.WriteLine($"P/E Ratio: {fundamentals.PERatio}");
|
||||
Console.WriteLine($"Dividend Yield: {fundamentals.DividendYield}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Fundamentals request failed.");
|
||||
}
|
||||
*/
|
||||
Gateway.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5246",
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
@ -14,7 +14,7 @@
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7039;http://localhost:5246",
|
||||
"applicationUrl": "https://localhost:7039;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
113
ServiceHandler/MarketDataHander.cs
Normal file
113
ServiceHandler/MarketDataHander.cs
Normal file
@ -0,0 +1,113 @@
|
||||
using Backend.MarketDataRequest;
|
||||
using IBApi;
|
||||
|
||||
namespace Backend.ServiceHander
|
||||
{
|
||||
public sealed class MarketDataService
|
||||
{
|
||||
private readonly IBGatewayControl Gateway;
|
||||
private int NextRequestId = 1;
|
||||
private readonly object Lock = new();
|
||||
|
||||
public MarketDataService()
|
||||
{
|
||||
Gateway = new IBGatewayControl();
|
||||
try
|
||||
{
|
||||
Gateway.Start("127.0.0.1", 4002, 1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Invalid connection to IB Gateway. Please ensure gateway is running and connected.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
private int GetNextRequestId()
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
return NextRequestId++;
|
||||
}
|
||||
}
|
||||
|
||||
public LiveEquitySnapshot GetLiveQuote(string Symbol) //
|
||||
{
|
||||
var Request = new LiveEquityRequest
|
||||
{
|
||||
RequestId = GetNextRequestId(),
|
||||
Symbol = Symbol,
|
||||
SecType = "STK",
|
||||
Exchange = "SMART",
|
||||
PrimaryExchange = "NASDAQ",
|
||||
Currency = "USD",
|
||||
RegulatorySnapshot = false
|
||||
};
|
||||
|
||||
return Gateway.RequestLiveEquityBlocking(Request, TimeSpan.FromSeconds(10));
|
||||
}
|
||||
|
||||
public List<object> GetHistorical(string symbol, string endDateTime, string duration, HistoricalBarSize barSize, HistoricalWhatToShow whatToShow = HistoricalWhatToShow.Trades, bool useRth = true)
|
||||
{
|
||||
var Request = new HistoricalEquityRequest
|
||||
{
|
||||
RequestId = GetNextRequestId(),
|
||||
Symbol = symbol,
|
||||
EndDateTime = endDateTime,
|
||||
Duration = duration,
|
||||
BarSize = barSize,
|
||||
WhatToShow = whatToShow,
|
||||
UseRth = useRth,
|
||||
KeepUpToDate = false,
|
||||
FormatDate = 1
|
||||
};
|
||||
|
||||
var Bars = Gateway.RequestHistoricalBlocking(Request, TimeSpan.FromSeconds(20));
|
||||
|
||||
return Bars.Select(b => new
|
||||
{
|
||||
time = b.Time,
|
||||
open = b.Open,
|
||||
high = b.High,
|
||||
low = b.Low,
|
||||
close = b.Close,
|
||||
volume = b.Volume
|
||||
}).Cast<object>().ToList();
|
||||
}
|
||||
|
||||
public List<object> GetHistoricalLine(string symbol, string endDateTime, string duration, HistoricalBarSize barSize, HistoricalWhatToShow whatToShow = HistoricalWhatToShow.Trades, bool useRth = true)
|
||||
{
|
||||
var Request = new HistoricalEquityRequest
|
||||
{
|
||||
RequestId = GetNextRequestId(),
|
||||
Symbol = symbol,
|
||||
EndDateTime = endDateTime,
|
||||
Duration = duration,
|
||||
BarSize = barSize,
|
||||
WhatToShow = whatToShow,
|
||||
UseRth = useRth,
|
||||
KeepUpToDate = false,
|
||||
FormatDate = 1
|
||||
};
|
||||
|
||||
var Bars = Gateway.RequestHistoricalBlocking(Request, TimeSpan.FromSeconds(20));
|
||||
|
||||
return Bars.Select(b => new
|
||||
{
|
||||
timestamp = b.Time,
|
||||
price = b.Close,
|
||||
volume = b.Volume
|
||||
}).Cast<object>().ToList();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Gateway.Dispose();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
Gateway.Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user