-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[MDAPI-42] [.NET] Implement OptionChain
- Loading branch information
Konstantin Ivaschenko
committed
Jul 26, 2024
1 parent
29a2f1b
commit 2e35650
Showing
12 changed files
with
1,328 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
* [MDAPI-42] [.NET] Implement OptionChain | ||
|
||
## Version 2.3.0 | ||
|
||
* [MDAPI-115] [.NET] Add IncOrderSnapshot sample | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Diagnostics.CodeAnalysis; | ||
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.Options; | ||
|
||
namespace DxFeed.Graal.Net.Samples; | ||
|
||
/// <summary> | ||
/// A simple sample that shows how to build option chains, and prints quotes for nearby option strikes. | ||
/// </summary> | ||
[SuppressMessage("ReSharper", "MethodSupportsCancellation")] | ||
internal abstract class Program | ||
{ | ||
public static async Task Main(string[] args) | ||
{ | ||
if (args.Length != 5) | ||
{ | ||
Console.WriteLine("usage: DXFeedOptionChain <address> <ipf-file> <symbol> <nStrikes> <nMonths>"); | ||
Console.WriteLine(" <ipf-file> is endpoint address"); | ||
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; | ||
} | ||
|
||
var argAddress = args[0]; | ||
var argIpfFile = args[1]; | ||
var argSymbol = args[2]; | ||
var nStrikes = int.Parse(args[3], CultureInfo.InvariantCulture); | ||
var nMonths = int.Parse(args[4], CultureInfo.InvariantCulture); | ||
|
||
var feed = DXEndpoint.Create().Connect(argAddress).GetFeed(); | ||
|
||
// 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).Chains; | ||
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}"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// <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.Options; | ||
|
||
/// <summary> | ||
/// Represents a set of option series for a single product or underlying symbol. | ||
/// | ||
/// <h3>Threads and locks</h3> | ||
/// | ||
/// This class is <b>NOT</b> thread-safe and cannot be used from multiple threads without external synchronization. | ||
/// </summary> | ||
/// <typeparam name="T">The type of option instrument instances.</typeparam> | ||
public sealed class OptionChain<T> : ICloneable | ||
{ | ||
private readonly SortedDictionary<OptionSeries<T>, OptionSeries<T>> _seriesMap = new(); | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="OptionChain{T}"/> class with the specified symbol. | ||
/// </summary> | ||
/// <param name="symbol">The symbol (product or underlying) of this option chain.</param> | ||
internal OptionChain(string symbol) => | ||
Symbol = symbol; | ||
|
||
/// <summary> | ||
/// Gets the symbol (product or underlying) of this option chain. | ||
/// </summary> | ||
public string Symbol { get; } | ||
|
||
/// <summary> | ||
/// Returns a sorted set of option series in this option chain. | ||
/// </summary> | ||
/// <returns>A sorted set of option series in this option chain.</returns> | ||
public SortedSet<OptionSeries<T>> GetSeries() => | ||
new(_seriesMap.Keys); | ||
|
||
/// <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() | ||
{ | ||
var clone = new OptionChain<T>(Symbol); | ||
foreach (var series in _seriesMap.Values) | ||
{ | ||
var seriesClone = (OptionSeries<T>)series.Clone(); | ||
clone._seriesMap.Add(seriesClone, seriesClone); | ||
} | ||
|
||
return clone; | ||
} | ||
|
||
/// <summary> | ||
/// Adds an option to the specified series in this option chain. | ||
/// If the series does not exist, it is created. | ||
/// </summary> | ||
/// <param name="series">The option series to which the option will be added.</param> | ||
/// <param name="isCall">Indicates whether the option is a call option.</param> | ||
/// <param name="strike">The strike price of the option.</param> | ||
/// <param name="option">The option to add.</param> | ||
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; | ||
} | ||
|
||
os.AddOption(isCall, strike, option); | ||
} | ||
} |
Oops, something went wrong.