Skip to content

Commit

Permalink
use quote cache (#190)
Browse files Browse the repository at this point in the history
  • Loading branch information
DaveSkender authored Feb 13, 2022
1 parent 44fc05b commit 9908c48
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 62 deletions.
3 changes: 3 additions & 0 deletions Server/Backend.sln
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Functions", "Functions\Functions.csproj", "{E9B5CE4B-BFAA-4E3A-B365-BEB69338932F}"
ProjectSection(ProjectDependencies) = postProject
{8C23ABF9-340A-4D21-BACD-A515A3C072EF} = {8C23ABF9-340A-4D21-BACD-A515A3C072EF}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
4 changes: 3 additions & 1 deletion Server/Functions/Functions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Alpaca.Markets" Version="5.2.8" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
<PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WebApi\WebApi.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
Expand Down
10 changes: 2 additions & 8 deletions Server/Functions/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using Azure.Storage.Blobs;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using WebApi.Services;

[assembly: FunctionsStartup(typeof(Functions.Startup))]

Expand All @@ -10,11 +9,6 @@ public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// initialize Azure services (setup storage)
string awjsConnection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");

// main blob container
BlobContainerClient blobContainer = new(awjsConnection, "chart-demo");
blobContainer.CreateIfNotExists();
Storage.Startup();
}
}
64 changes: 22 additions & 42 deletions Server/Functions/UpdateQuotes.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Alpaca.Markets;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Skender.Stock.Indicators;
using WebApi.Services;

namespace Functions;

Expand All @@ -17,7 +18,6 @@ public static async Task Run([TimerTrigger("0 */1 08-18 * * 1-5")] TimerInfo myT
{
// ~ extended market hours, every minute "0 */1 08-18 * * 1-5"
// for dev: minutely "0 */1 * * * *"
await StoreQuoteDaily("DIA", log);
await StoreQuoteDaily("SPY", log);
await StoreQuoteDaily("QQQ", log);

Expand All @@ -40,49 +40,29 @@ private static async Task StoreQuoteDaily(string symbol, ILogger log)
IPage<IBar> barSet = await alpacaDataClient.ListHistoricalBarsAsync(
new HistoricalBarsRequest(symbol, from, into, BarTimeFrame.Day));

// compose CSV
string csv = string.Empty;
List<Quote> quotes = new(barSet.Items.Count);

// compose
foreach (IBar bar in barSet.Items)
{
csv += $"{bar.TimeUtc:d},{bar.Open},{bar.High},{bar.Low},{bar.Close},{bar.Volume}\r\n";
Quote q = new()
{
Date = bar.TimeUtc,
Open = bar.Open,
High = bar.High,
Low = bar.Low,
Close = bar.Close,
Volume = bar.Volume
};
quotes.Add(q);
}

string json = JsonSerializer.Serialize(quotes.OrderBy(x => x.Date));

// store in Azure Blog
string blobName = $"{symbol}-DAILY.csv";
await PutBlob(blobName, csv);
string blobName = $"{symbol}-DAILY.json";
await Storage.PutBlob(blobName, json);

log.LogInformation($"Updated {blobName}");
}

// SAVE BLOB
private static async Task<bool> PutBlob(string blobName, string csv)
{
BlobClient blob = GetBlobReference(blobName);
BlobHttpHeaders httpHeader = new()
{
ContentType = "application/text"
};

using (MemoryStream ms = new(Encoding.UTF8.GetBytes(csv)))
{
ms.Position = 0;
await blob.UploadAsync(ms, httpHeader);
};
return true;
}

// HELPERS
private static BlobClient GetBlobReference(string blobName)
{
BlobContainerClient blobContainer = GetContainerReference("chart-demo");
BlobClient blob = blobContainer.GetBlobClient(blobName);

return blob;
}

private static BlobContainerClient GetContainerReference(string containerName)
{
string awjsConnection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
return new BlobContainerClient(awjsConnection, containerName);
}
}
18 changes: 9 additions & 9 deletions Server/WebApi/Controllers/MainController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace WebApi.Controllers;
public class MainController : ControllerBase
{
internal static readonly IEnumerable<Quote> quotes = FetchQuotes.Get();
internal static readonly DateTime dateStart = DateTime.Parse("6/1/2018");
internal static readonly int limitLast = 130;

[HttpGet]
public string Get()
Expand All @@ -20,7 +20,7 @@ public string Get()
[HttpGet("quotes")]
public IActionResult GetQuotes()
{
return Ok(quotes.Where(x => x.Date >= dateStart));
return Ok(quotes.TakeLast(limitLast));
}

[HttpGet("indicators")]
Expand All @@ -41,7 +41,7 @@ public IActionResult GetBollingerBands(
{
IEnumerable<BollingerBandsResult> results =
quotes.GetBollingerBands(lookbackPeriods, standardDeviations)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -58,7 +58,7 @@ public IActionResult GetEMA(int lookbackPeriods)
{
IEnumerable<EmaResult> results =
quotes.GetEma(lookbackPeriods)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -77,7 +77,7 @@ public IActionResult GetParabolicSar(
{
IEnumerable<ParabolicSarResult> results =
quotes.GetParabolicSar(accelerationStep, maxAccelerationFactor)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -95,7 +95,7 @@ public IActionResult GetRsi(
{
IEnumerable<RsiResult> results =
quotes.GetRsi(lookbackPeriods)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -114,7 +114,7 @@ public IActionResult GetStoch(
{
IEnumerable<StochResult> results =
quotes.GetStoch(lookbackPeriods, signalPeriods)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -132,7 +132,7 @@ public IActionResult GetZigZagClose(
{
IEnumerable<ZigZagResult> results =
quotes.GetZigZag(EndType.Close, percentChange)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand All @@ -150,7 +150,7 @@ public IActionResult GetZigZagHighLow(
{
IEnumerable<ZigZagResult> results =
quotes.GetZigZag(EndType.HighLow, percentChange)
.Where(x => x.Date >= dateStart);
.TakeLast(limitLast);

return Ok(results);
}
Expand Down
8 changes: 8 additions & 0 deletions Server/WebApi/Properties/serviceDependencies.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"dependencies": {
"storage1": {
"type": "storage",
"connectionId": "AzureWebJobsStorage"
}
}
}
9 changes: 9 additions & 0 deletions Server/WebApi/Properties/serviceDependencies.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"dependencies": {
"storage1": {
"secretStore": null,
"type": "storage.emulator",
"connectionId": "AzureWebJobsStorage"
}
}
}
35 changes: 33 additions & 2 deletions Server/WebApi/Services/Service.Quotes.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
using Skender.Stock.Indicators;
using System.Text.Json;
using Azure;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Skender.Stock.Indicators;

namespace WebApi.Services;

internal static class FetchQuotes
{
internal static IEnumerable<Quote> Get()
internal static IEnumerable<Quote> Get(string symbol = "QQQ")
{
string blobName = $"{symbol}-DAILY.json";

try
{
BlobClient blob = Storage.GetBlobReference(blobName);
Response<BlobDownloadInfo> response = blob.Download();

List<Quote> quotes = JsonSerializer
.Deserialize<List<Quote>>(response.Value.Content);

if (quotes == null || quotes.Count == 0)
{
return GetBackup();
}

return quotes
.OrderBy(x => x.Date)
.ToList();
}
catch
{
return GetBackup();
}
}

internal static IEnumerable<Quote> GetBackup()
{
List<Quote> h = new()
{
Expand Down
51 changes: 51 additions & 0 deletions Server/WebApi/Services/Service.Storage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Text;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

namespace WebApi.Services;

public class Storage
{
// STARTUP
public static void Startup()
{
// initialize Azure services (setup storage)
string awjsConnection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");

// main blob container
BlobContainerClient blobContainer = new(awjsConnection, "chart-demo");
blobContainer.CreateIfNotExists();
}

// SAVE BLOB
public static async Task<bool> PutBlob(string blobName, string csv)
{
BlobClient blob = GetBlobReference(blobName);
BlobHttpHeaders httpHeader = new()
{
ContentType = "application/json"
};

using (MemoryStream ms = new(Encoding.UTF8.GetBytes(csv)))
{
ms.Position = 0;
await blob.UploadAsync(ms, httpHeader);
};
return true;
}

// HELPERS
internal static BlobClient GetBlobReference(string blobName)
{
BlobContainerClient blobContainer = GetContainerReference("chart-demo");
BlobClient blob = blobContainer.GetBlobClient(blobName);

return blob;
}

private static BlobContainerClient GetContainerReference(string containerName)
{
string awjsConnection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
return new BlobContainerClient(awjsConnection, containerName);
}
}
4 changes: 4 additions & 0 deletions Server/WebApi/WebApi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.10.0" />
<PackageReference Include="Azure.Storage.Files.Shares" Version="12.1.0" />
<PackageReference Include="Azure.Storage.Queues" Version="12.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.2" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.0.0" />
<PackageReference Include="Skender.Stock.Indicators" Version="1.21.0" />
</ItemGroup>

Expand Down

0 comments on commit 9908c48

Please sign in to comment.