Skip to content

Commit

Permalink
[MDAPI-112] [.NET] Add LastEventsConsole API sample
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Ivaschenko committed Jul 29, 2024
1 parent efd866a commit 725ec38
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 0 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ sudo /usr/bin/xattr -r -d com.apple.quarantine <directory_with_tools>
is a simple demonstration of how to use the `MarketDepthModel` to manage and display order books for multiple symbols.
- [x] [DXFeedOptionChain](https://github.com/dxFeed/dxfeed-graal-net-api/tree/main/samples/DXFeedOptionChain)
how to build option chains, and prints quotes for nearby option strikes.
- [x] [LastEventConsoleSample](https://github.com/dxFeed/dxfeed-graal-net-api/tree/main/samples/LastEventConsoleSample)
is a simple demonstration how to subscribe to various market events with the dxFeed API, cache them in memory, and
take snapshots of these events based on user input.

## Current State

Expand Down
1 change: 1 addition & 0 deletions ReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* [MDAPI-112] [.NET] Add LastEventsConsole API sample
* [MDAPI-42] [.NET] Implement OptionChain

## Version 2.3.0
Expand Down
7 changes: 7 additions & 0 deletions dxfeed-graal-net-api.sln
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{5F74BD34-C2D4-
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DXFeedOptionChain", "samples\DXFeedOptionChain\DXFeedOptionChain.csproj", "{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LastEventConsoleSample", "samples\LastEventConsoleSample\LastEventConsoleSample.csproj", "{0CD0E240-687A-46A7-B343-BA8A6549A216}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -144,6 +146,10 @@ Global
{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}.Release|Any CPU.Build.0 = Release|Any CPU
{0CD0E240-687A-46A7-B343-BA8A6549A216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0CD0E240-687A-46A7-B343-BA8A6549A216}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CD0E240-687A-46A7-B343-BA8A6549A216}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CD0E240-687A-46A7-B343-BA8A6549A216}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{73597E04-D8A8-4991-A759-7F886CBE2A8F} = {C4490D74-2970-4A1B-8178-A724A06B140A}
Expand All @@ -167,5 +173,6 @@ Global
{B74E8A86-1AB7-4B36-AED3-292CDD95BF90} = {5F74BD34-C2D4-436B-8243-FB0F3BB9F0AC}
{930B1039-B76C-42C5-AD0F-9FA1A1FC9D84} = {5F74BD34-C2D4-436B-8243-FB0F3BB9F0AC}
{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19} = {C4490D74-2970-4A1B-8178-A724A06B140A}
{0CD0E240-687A-46A7-B343-BA8A6549A216} = {C4490D74-2970-4A1B-8178-A724A06B140A}
EndGlobalSection
EndGlobal
21 changes: 21 additions & 0 deletions samples/LastEventConsoleSample/LastEventConsoleSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<RootNamespace>DxFeed.Graal.Net.Samples</RootNamespace>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>../../artifacts/Debug/Samples/</OutputPath>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>../../artifacts/Release/Samples/</OutputPath>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\DxFeed.Graal.Net\DxFeed.Graal.Net.csproj"/>
</ItemGroup>

</Project>
120 changes: 120 additions & 0 deletions samples/LastEventConsoleSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using DxFeed.Graal.Net.Api;
using DxFeed.Graal.Net.Events;
using DxFeed.Graal.Net.Events.Market;

namespace DxFeed.Graal.Net.Samples;

/// <summary>
/// This sample demonstrates a way to subscribe to the big world of symbols with dxFeed API, so that the events are
/// updated and cached in memory of this process, and then take snapshots of those events from memory whenever
/// they are needed. This example repeatedly reads symbol name from the console and prints a snapshot of its last
/// quote, trade, summary, and profile events.
/// </summary>
[SuppressMessage("ReSharper", "FunctionNeverReturns")]
public abstract class Program
{
private static async Task Main()
{
// Permanent subscription to the world is performed with a special property named "dxfeed.qd.subscribe.ticker".
// Its value consists of a comma-separated list of records, followed by a space, followed by a comma-separated
// list of symbols. Record names for composite (NBBO) events are the same as the corresponding event classes
// in API. The string below defines subscription for quote, trade, summary, and profile composite events:
var records = "Quote,Trade,Summary,Profile";

// Records for regional-exchange events are derived by appending "&" (ampersand) and the a single-digit
// exchange code. Regexp-like syntax can be used instead of listing them all. For example, the commented
// line below and to the mix a subscription on regional quotes from all potential exchange codes A-Z
// var record records = "Quote,Trade,Summary,Profile,Quote&[A-Z]";

// There is an important trade-off between a resource consumption and speed of access to the last events.
// The whole world of stocks and options from all the exchanges is very large and will consume gigabytes
// of memory to cache. Moreover, this cache has to be constantly kept up-to-date which consumes a lot of
// network and CPU.
//
// A particular application's use cases have to be studied to figure out what is optimal for this particular
// application. If some events are known to be rarely needed and a small delay while accessing them can be
// tolerated, then it is not worth configuring a permanent subscription for them. The code in this
// sample works using DXFeed.GetLastEventAsync method that will request the event from the upstream data provider
// if it is not present in the local in-memory cache.

// There are multiple ways to specify a list of symbols. It is typically taken from an IPF file and its
// specification consists of a URL to the file which has to contain ".ipf" in order to be recognized.
// The string below defines subscription for all symbols that are available on the demo feed.
var symbols = "http://dxfeed.s3.amazonaws.com/masterdata/ipf/demo/mux-demo.ipf.zip";

// The permanent subscription property "dxfeed.qd.subscribe.ticker" can be placed directly into the
// "dxfeed.properties" file, eliminating the need for a custom DXEndpoint instance. In this example,
// it is explicitly specified using the DXFeedEndpoint.Builder class. Note that the "connect" method
// is not used on DXEndpoint. This sample assumes that the "dxfeed.address" property is specified in the builder,
// establishing the connection automatically. Alternatively, "dxfeed.address" can also
// be specified in the "dxfeed.properties" file.
var endpoint = DXEndpoint.NewBuilder()
.WithProperty("dxfeed.address", "demo.dxfeed.com:7300")
.WithProperty("dxfeed.qd.subscribe.ticker", $"{records} {symbols}")
.Build();

// The actual client code does not need a reference to DXEndpoint, which only contains lifecycle
// methods like "connect" and "close". The client code needs a reference to DXFeed.
var feed = endpoint.GetFeed();

// Print a short help.
Console.WriteLine("Type symbols to get their quote, trade, summary, and profile event snapshots");

// The main loop of this sample loops forever reading symbols from console and printing events.
while (true)
{
// User of this sample application can type symbols on the console. Symbol like "IBM" corresponds
// to the stock. Symbol like "IBM&N" corresponds to the information from a specific exchange.
// See the dxFeed Symbol guide at http://www.dxfeed.com/downloads/documentation/dxFeed_Symbol_Guide.pdf
var symbol = Console.ReadLine();
if (symbol == null)
{
continue;
}

// The first step is to extract tasks for all events that we are interested in. This way we
// can get an event even if we have not previously subscribed for it.
var quoteTask = feed.GetLastEventAsync<Quote>(symbol).ContinueWith(task => task.Result as ILastingEvent);
var tradeTask = feed.GetLastEventAsync<Trade>(symbol).ContinueWith(task => task.Result as ILastingEvent);
var summaryTask = feed.GetLastEventAsync<Summary>(symbol).ContinueWith(task => task.Result as ILastingEvent);

// All tasks are put into a list for convenience.
var tasks = new List<Task<ILastingEvent>> { quoteTask, tradeTask, summaryTask };

// Profile events are composite-only. They are not available for regional symbols like
// "IBM&N" and the attempt to retrieve them never completes (will timeout), so we don't even try.
if (!MarketEventSymbols.HasExchangeCode(symbol))
{
var profileTask = feed.GetLastEventAsync<Profile>(symbol)
.ContinueWith(task => task.Result as ILastingEvent);
tasks.Add(profileTask);
}

// If the events are available in the in-memory cache, then the tasks will be completed immediately.
// Otherwise, a request to the upstream data provider is sent. Below we combine tasks using
// Task.WhenAll in order to wait for at most 1 second for all the tasks to complete.
// This sample prints a special message in the case of timeout.
var timeout = Task.Delay(TimeSpan.FromSeconds(1));
var completedTask = await Task.WhenAny(Task.WhenAll(tasks), timeout);
if (completedTask == timeout)
{
Console.WriteLine("Request timed out");
}

// The combination above is used only to ensure a common wait of 1 second. Tasks for individual events
// are completed independently and the corresponding events can be accessed even if some events were not
// available for any reason and the wait above had timed out. This sample just prints all completed tasks.
foreach (var task in tasks)
{
if (task.IsCompleted)
{
Console.WriteLine(task.Result);
}
}
}
}
}

0 comments on commit 725ec38

Please sign in to comment.