Skip to content

Commit

Permalink
[MDAPI-42] [.NET] Implement OptionChain
Browse files Browse the repository at this point in the history
  • Loading branch information
Konstantin Ivaschenko committed Jul 26, 2024
1 parent 29a2f1b commit 8e0e616
Show file tree
Hide file tree
Showing 6 changed files with 797 additions and 0 deletions.
7 changes: 7 additions & 0 deletions dxfeed-graal-net-api.sln
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultipleMarketDepthSample",
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{5F74BD34-C2D4-436B-8243-FB0F3BB9F0AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DXFeedOptionChain", "samples\DXFeedOptionChain\DXFeedOptionChain.csproj", "{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -138,6 +140,10 @@ Global
{C8F5013F-7F40-46D2-92AD-6B593524A1D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C8F5013F-7F40-46D2-92AD-6B593524A1D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C8F5013F-7F40-46D2-92AD-6B593524A1D0}.Release|Any CPU.Build.0 = Release|Any CPU
{7E7BF3A7-C564-4B82-AAD6-6C1D1BCE3F19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{73597E04-D8A8-4991-A759-7F886CBE2A8F} = {C4490D74-2970-4A1B-8178-A724A06B140A}
Expand All @@ -160,5 +166,6 @@ Global
{5F74BD34-C2D4-436B-8243-FB0F3BB9F0AC} = {C4490D74-2970-4A1B-8178-A724A06B140A}
{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}
EndGlobalSection
EndGlobal
27 changes: 27 additions & 0 deletions samples/DXFeedOptionChain/DXFeedOptionChain.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<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>

<ItemGroup>
<None Update="dxfeed.properties">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
103 changes: 103 additions & 0 deletions samples/DXFeedOptionChain/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DxFeed.Graal.Net.Api;
using DxFeed.Graal.Net.Events.Market;
using DxFeed.Graal.Net.Ipf;
using DxFeed.Graal.Net.Ipf.Option;

namespace DxFeed.Graal.Net.Samples;

abstract class Program
{
public static async Task Main(string[] args)
{
if (args.Length != 4)
{
Console.WriteLine("usage: DXFeedOptionChain <ipf-file> <symbol> <nStrikes> <nMonths>");
Console.WriteLine(" <ipf-file> is name of instrument profiles file");
Console.WriteLine(" <symbol> is the product or underlying symbol");
Console.WriteLine(" <nStrikes> number of strikes to print for each series");
Console.WriteLine(" <nMonths> number of months to print");
return;
}

SystemProperty.SetProperty("dxfeed.address", "demo.dxfeed.com:7300");

var argIpfFile = args[0];
var argSymbol = args[1];
var nStrikes = int.Parse(args[2]);
var nMonths = int.Parse(args[3]);

var feed = DXFeed.GetInstance();

// subscribe to trade to learn instrument last price
Console.WriteLine($"Waiting for price of {argSymbol} ...");
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1));
var trade = await feed.GetLastEventAsync<Trade>(argSymbol, cts.Token);

var price = trade.Price;
Console.WriteLine($"Price of {argSymbol} is {price.ToString(CultureInfo.InvariantCulture)}");

Console.WriteLine($"Reading instruments from {argIpfFile} ...");
var instruments = new InstrumentProfileReader().ReadFromFile(argIpfFile).ToList();

Console.WriteLine("Building option chains ...");
var chains = OptionChainsBuilder<InstrumentProfile>.Build(instruments).GetChains();
if (!chains.TryGetValue(argSymbol, out var chain))
{
Console.WriteLine($"No chain found for symbol {argSymbol}");
return;
}

nMonths = Math.Min(nMonths, chain.GetSeries().Count);
var seriesList = chain.GetSeries().Take(nMonths).ToList();

Console.WriteLine("Requesting option quotes ...");
var quotes = new Dictionary<InstrumentProfile, Task<Quote>>();

foreach (var series in seriesList)
{
var strikes = series.GetNStrikesAround(nStrikes, price);
foreach (var strike in strikes)
{
if (series.Calls.TryGetValue(strike, out var call))
{
quotes[call] = feed.GetLastEventAsync<Quote>(call.Symbol);
}

if (series.Puts.TryGetValue(strike, out var put))
{
quotes[put] = feed.GetLastEventAsync<Quote>(put.Symbol);
}
}
}

await Task.WhenAll(quotes.Values);

Console.WriteLine("Printing option series ...");
foreach (var series in seriesList)
{
Console.WriteLine($"Option series {series}");
var strikes = series.GetNStrikesAround(nStrikes, price);
Console.WriteLine($"{"C.BID",10} {"C.ASK",10} {"STRIKE",10} {"P.BID",10} {"P.ASK",10}");
foreach (var strike in strikes)
{
series.Calls.TryGetValue(strike, out var call);
series.Puts.TryGetValue(strike, out var put);
var callQuote = call != null && quotes.TryGetValue(call, out var callTask)
? callTask.Result
: new Quote();
var putQuote = put != null && quotes.TryGetValue(put, out var putTask) ? putTask.Result : new Quote();
Console.WriteLine(
$"{callQuote.BidPrice,10:F3} {callQuote.AskPrice,10:F3} {strike,10:F3} {putQuote.BidPrice,10:F3} {putQuote.AskPrice,10:F3}");
}
}

Environment.Exit(0); // shutdown when done
}
}
63 changes: 63 additions & 0 deletions src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <copyright file="OptionChain.cs" company="Devexperts LLC">
// Copyright © 2024 Devexperts LLC. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// </copyright>

using System;
using System.Collections.Generic;

namespace DxFeed.Graal.Net.Ipf.Option;

public sealed class OptionChain<T> : ICloneable

Check warning on line 12 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on win-x64

Missing XML comment for publicly visible type or member 'OptionChain<T>'

Check warning on line 12 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on linux-x64

Missing XML comment for publicly visible type or member 'OptionChain<T>'

Check warning on line 12 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on osx-arm64

Missing XML comment for publicly visible type or member 'OptionChain<T>'
{
private readonly string _symbol;

private readonly SortedDictionary<OptionSeries<T>, OptionSeries<T>> seriesMap = new();

public OptionChain(string symbol) => this._symbol = symbol;

Check warning on line 18 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on win-x64

Missing XML comment for publicly visible type or member 'OptionChain<T>.OptionChain(string)'

Check warning on line 18 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on linux-x64

Missing XML comment for publicly visible type or member 'OptionChain<T>.OptionChain(string)'

Check warning on line 18 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on osx-arm64

Missing XML comment for publicly visible type or member 'OptionChain<T>.OptionChain(string)'

/// <summary>
/// Returns a shallow copy of this option chain.
/// All series are copied (cloned) themselves, but option instrument instances are shared with the original.
/// </summary>
/// <returns>A shallow copy of this option chain.</returns>
public object Clone()
{
OptionChain<T> clone = new OptionChain<T>(_symbol);
foreach (OptionSeries<T> series in seriesMap.Values)
{
OptionSeries<T> seriesClone = (OptionSeries<T>)series.Clone();
clone.seriesMap.Add(seriesClone, seriesClone);
}

Check warning on line 32 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on win-x64

Check warning on line 32 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on linux-x64

Check warning on line 32 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on osx-arm64

return clone;
}

/// <summary>
/// Returns symbol (product or underlying) of this option chain.
/// </summary>
/// <returns>Symbol (product or underlying) of this option chain.</returns>
public string GetSymbol()
{
return _symbol;
}

/// <summary>
/// Returns a sorted set of option series of this option chain.
/// </summary>
/// <returns>Sorted set of option series of this option chain.</returns>
public SortedSet<OptionSeries<T>> GetSeries()
{
return new SortedSet<OptionSeries<T>>(seriesMap.Keys);
}

internal void AddOption(OptionSeries<T> series, bool isCall, double strike, T option)
{
if (!seriesMap.TryGetValue(series, out var os))
{
os = new OptionSeries<T>(series);
seriesMap[os] = os;
}

Check warning on line 60 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on win-x64

Check warning on line 60 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on linux-x64

Check warning on line 60 in src/DxFeed.Graal.Net/Ipf/Option/OptionChain.cs

View workflow job for this annotation

GitHub Actions / Build & Test on osx-arm64

os.AddOption(isCall, strike, option);
}
}
Loading

0 comments on commit 8e0e616

Please sign in to comment.