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>
|
<ItemGroup>
|
||||||
<ProjectReference Include="../twsapi/CSharpAPI.csproj" />
|
<ProjectReference Include="../twsapi/CSharpAPI.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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,
|
FiveMins,
|
||||||
FifteenMins,
|
FifteenMins,
|
||||||
OneHour,
|
OneHour,
|
||||||
OneDay
|
OneDay,
|
||||||
|
FiveDays,
|
||||||
|
OneMonth
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum HistoricalWhatToShow
|
public enum HistoricalWhatToShow
|
||||||
@ -17,6 +19,12 @@ namespace Backend.MarketDataRequest
|
|||||||
Midpoint
|
Midpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class HistoricalLinePoint
|
||||||
|
{
|
||||||
|
public string Timestamp { get; init; } = string.Empty;
|
||||||
|
public double Price { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class HistoricalEquityRequest
|
public sealed class HistoricalEquityRequest
|
||||||
{
|
{
|
||||||
public int RequestId { get; init; }
|
public int RequestId { get; init; }
|
||||||
@ -62,6 +70,8 @@ namespace Backend.MarketDataRequest
|
|||||||
HistoricalBarSize.FifteenMins => "15 mins",
|
HistoricalBarSize.FifteenMins => "15 mins",
|
||||||
HistoricalBarSize.OneHour => "1 hour",
|
HistoricalBarSize.OneHour => "1 hour",
|
||||||
HistoricalBarSize.OneDay => "1 day",
|
HistoricalBarSize.OneDay => "1 day",
|
||||||
|
HistoricalBarSize.FiveDays => "5 days",
|
||||||
|
HistoricalBarSize.OneMonth => "1 month",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(BarSize))
|
_ => 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",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "http://localhost:5246",
|
"applicationUrl": "http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
@ -14,7 +14,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"applicationUrl": "https://localhost:7039;http://localhost:5246",
|
"applicationUrl": "https://localhost:7039;http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"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