diff --git a/.editorconfig b/.editorconfig
index 9263be7..876b864 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -361,7 +361,7 @@ dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibi
dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
-dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = none
# Private fields must be camelCase
# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
diff --git a/StyleCop.xml b/StyleCop.xml
index cd0b013..354d234 100644
--- a/StyleCop.xml
+++ b/StyleCop.xml
@@ -11,5 +11,6 @@
+
diff --git a/src/DxFeed.Graal.Net/Models/AbstractTxModel.cs b/src/DxFeed.Graal.Net/Models/AbstractTxModel.cs
new file mode 100644
index 0000000..e18ec3c
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Models/AbstractTxModel.cs
@@ -0,0 +1,178 @@
+//
+// 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/.
+//
+
+using System;
+using DxFeed.Graal.Net.Api;
+using DxFeed.Graal.Net.Events;
+using DxFeed.Graal.Net.Native.Model;
+
+namespace DxFeed.Graal.Net.Models;
+
+///
+/// Abstract base class for models that handle transactions of .
+/// This class manages all snapshot and transaction logic, subscription handling, and listener notifications.
+///
+///
+/// This model is designed to handle incremental transactions. Users of this model only see the list
+/// of events in a consistent state. This model delays incoming events that are part of an incomplete snapshot
+/// or ongoing transaction until the snapshot is complete or the transaction has ended.
+///
+/// Configuration
+/// This model must be configured using the . Specific implementations can add additional
+/// configuration options as needed.
+///
+/// Resource management and closed models
+/// Attached model is a potential memory leak. If the pointer to attached model is lost, then there is no way
+/// to detach this model from the feed and the model will not be reclaimed by the garbage collector as long as the
+/// corresponding feed is still used. Detached model can be reclaimed by the garbage collector, but detaching model
+/// requires knowing the pointer to the feed at the place of the call, which is not always convenient.
+///
+/// The convenient way to detach model from the feed is to call its method. Closed model
+/// becomes permanently detached from all feeds, removes all its listeners and is guaranteed to be reclaimable by
+/// the garbage collector as soon as all external references to it are cleared.
+///
+/// Threads and locks
+/// This class is thread-safe and can be used concurrently from multiple threads without external synchronization.
+///
+public abstract class AbstractTxModel
+{
+ private protected readonly AbstractTxModelHandle handle;
+
+ private protected AbstractTxModel(AbstractTxModelHandle handle) =>
+ this.handle = handle;
+
+ ///
+ /// Gets whether batch processing is enabled.
+ /// See .
+ ///
+ ///
+ /// true if batch processing is enabled; otherwise, false.
+ ///
+ public bool IsBatchProcessing() =>
+ handle.IsBatchProcessing();
+
+ ///
+ /// Gets whether snapshot processing is enabled.
+ /// See .
+ ///
+ ///
+ /// true if snapshot processing is enabled; otherwise, false.
+ ///
+ public bool IsSnapshotProcessing() =>
+ handle.IsSnapshotProcessing();
+
+ ///
+ /// Closes this model and makes it permanently detached.
+ ///
+ ///
+ /// This method clears installed listeners and ensures that the model
+ /// can be safely garbage-collected when all outside references to it are lost.
+ ///
+ public void Close() =>
+ handle.Close();
+
+ ///
+ /// Abstract builder for building models inherited from .
+ /// Specific implementations can add additional configuration options to this builder.
+ ///
+ ///
+ /// Inheritors of this class must override the abstract method to build a specific model.
+ ///
+ /// The type of the builder subclass.
+ /// The type of the model subclass.
+ public abstract class Builder
+ where TB : Builder
+ where TM : AbstractTxModel
+ {
+ ///
+ /// Initializes a new instance of the class with the specified event type.
+ ///
+ /// The type of events processed by the model being created.
+ protected Builder(Type eventType)
+ {
+ }
+
+ ///
+ /// Enables or disables batch processing. This is enabled by default.
+ ///
+ ///
+ /// If batch processing is disabled, the model will notify the listener
+ /// separately for each transaction (even if it is represented by a single event);
+ /// otherwise, transactions can be combined in a single listener call.
+ ///
+ ///
A transaction may represent either a snapshot or update events that are received after a snapshot.
+ /// Whether this flag is set or not, the model will always notify listeners that a snapshot has been received
+ /// and will not combine multiple snapshots or a snapshot with another transaction
+ /// into a single listener notification.
+ ///
+ /// true to enable batch processing; false otherwise.
+ /// The builder instance.
+ public TB WithBatchProcessing(bool isBatchProcessing)
+ {
+ return (TB)this;
+ }
+
+ ///
+ /// Enables or disables snapshot processing. This is disabled by default.
+ ///
+ ///
+ /// If snapshot processing is enabled, transactions representing a snapshot will be processed as follows:
+ /// events that are marked for removal will be removed, repeated indexes will be merged, and
+ /// event flags of events are set to zero; otherwise, the user will see the snapshot in raw form,
+ /// with possible repeated indexes, events marked for removal, and event flags unchanged.
+ ///
+ /// Whether this flag is set or not, in transactions that are not a snapshot, events that are marked
+ /// for removal will not be removed, repeated indexes will not be merged, and
+ /// event flags of events will not be changed.
+ /// This flag only affects the processing of transactions that are a snapshot.
+ ///
+ /// true to enable snapshot processing; false otherwise.
+ /// The builder instance.
+ public TB WithSnapshotProcessing(bool isSnapshotProcessing)
+ {
+ return (TB)this;
+ }
+
+ ///
+ /// Sets the for the model being created.
+ /// The feed cannot be attached after the model has been built.
+ ///
+ /// The .
+ /// The builder instance.
+ public TB WithFeed(DXFeed feed)
+ {
+ return (TB)this;
+ }
+
+ ///
+ /// Sets the subscription symbol for the model being created.
+ /// The symbol cannot be added or changed after the model has been built.
+ ///
+ /// The subscription symbol.
+ /// The builder instance.
+ public TB WithSymbol(object symbol)
+ {
+ return (TB)this;
+ }
+
+ ///
+ /// Sets the listener for transaction notifications.
+ /// The listener cannot be changed or added once the model has been built.
+ ///
+ /// The transaction listener.
+ /// The builder instance.
+ public TB WithListener(TxModelListener listener)
+ {
+ return (TB)this;
+ }
+
+ ///
+ /// Builds an instance of the model based on the provided parameters.
+ ///
+ /// The created model.
+ public abstract TM Build();
+ }
+}
diff --git a/src/DxFeed.Graal.Net/Models/IndexedTxModel.cs b/src/DxFeed.Graal.Net/Models/IndexedTxModel.cs
new file mode 100644
index 0000000..4010b4d
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Models/IndexedTxModel.cs
@@ -0,0 +1,151 @@
+//
+// 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/.
+//
+
+using System;
+using System.Collections.Generic;
+using DxFeed.Graal.Net.Api;
+using DxFeed.Graal.Net.Events;
+using DxFeed.Graal.Net.Native.Model;
+
+namespace DxFeed.Graal.Net.Models;
+
+///
+/// An incremental model for indexed events.
+/// This model manages all snapshot and transaction logic, subscription handling, and listener notifications.
+///
+///
+///
+/// This model is designed to handle incremental transactions. Users of this model only see the list
+/// of events in a consistent state. This model delays incoming events that are part of an incomplete snapshot
+/// or ongoing transaction until the snapshot is complete or the transaction has ended. This model notifies
+/// the user of received transactions through an installed listener.
+///
+/// Configuration
+///
+/// This model must be configured using the class, as most configuration
+/// settings cannot be changed once the model is built. This model requires configuration with
+/// a symbol and
+/// a sources
+/// for subscription, and it must be attached to a
+/// instance to begin operation.
+/// For ease of use, some of these configurations can be changed after the model is built, see
+/// .
+///
+///
+/// This model only supports single symbol subscriptions; multiple symbols cannot be configured.
+///
+/// Resource management and closed models
+///
+/// Attached model is a potential memory leak. If the pointer to attached model is lost, then there is no way
+/// to detach this model from the feed and the model will not be reclaimed by the garbage collector as long as the
+/// corresponding feed is still used. Detached model can be reclaimed by the garbage collector, but detaching model
+/// requires knowing the pointer to the feed at the place of the call, which is not always convenient.
+///
+///
+/// The convenient way to detach model from the feed is to call its method.
+/// Closed model becomes permanently detached from all feeds, removes all its listeners and is guaranteed
+/// to be reclaimable by the garbage collector as soon as all external references to it are cleared.
+///
+/// Threads and locks
+///
+/// This class is thread-safe and can be used concurrently from multiple threads without external synchronization.
+///
+///
+public sealed class IndexedTxModel : AbstractTxModel
+{
+ private IndexedTxModel(AbstractTxModelHandle handle)
+ : base(handle)
+ {
+ }
+
+ ///
+ /// Factory method to create a new builder for this model.
+ ///
+ /// The class type of the time series event.
+ /// A new instance.
+ public static Builder NewBuilder(Type eventType) =>
+ new(eventType);
+
+ ///
+ /// Returns the current set of sources.
+ /// If no sources have been set, an empty set is returned,
+ /// indicating that all possible sources have been subscribed to.
+ ///
+ /// The set of current sources.
+ public HashSet GetSources() =>
+ ((IndexedTxModelHandle)handle).GetSources();
+
+ ///
+ /// Sets the sources from which to subscribe for indexed events.
+ /// If an empty list is provided, subscriptions will default to all available sources.
+ /// If these sources have already been set, nothing happens.
+ ///
+ /// The specified sources.
+ public void SetSources(params IndexedEventSource[] sources) =>
+ ((IndexedTxModelHandle)handle).SetSources(sources);
+
+ ///
+ /// Sets the sources from which to subscribe for indexed events.
+ /// If an empty set is provided, subscriptions will default to all available sources.
+ /// If these sources have already been set, nothing happens.
+ ///
+ /// The specified sources.
+ public void SetSources(ICollection sources) =>
+ ((IndexedTxModelHandle)handle).SetSources(sources);
+
+ ///
+ /// A builder class for creating an instance of .
+ ///
+ public class Builder : Builder
+ {
+ ///
+ /// Initializes a new instance of the class with the specified event type.
+ ///
+ /// The type of events processed by the model being created.
+ public Builder(Type eventType)
+ : base(eventType)
+ {
+ }
+
+ ///
+ /// Sets the sources from which to subscribe for indexed events.
+ /// If no sources have been set, subscriptions will default to all possible sources.
+ ///
+ ///
+ /// The default value for this source is an empty set,
+ /// which means that this model subscribes to all available sources.
+ /// These sources can be changed later, after the model has been created,
+ /// by calling .
+ ///
+ /// The specified sources.
+ /// The builder instance.
+ public Builder WithSources(params IndexedEventSource[] sources)
+ {
+ return this;
+ }
+
+ ///
+ /// Sets the sources from which to subscribe for indexed events.
+ /// If no sources have been set, subscriptions will default to all possible sources.
+ ///
+ ///
+ /// The default value for this source is an empty set,
+ /// which means that this model subscribes to all available sources.
+ /// These sources can be changed later, after the model has been created,
+ /// by calling .
+ ///
+ /// The specified sources.
+ /// The builder instance.
+ public Builder WithSources(ICollection sources)
+ {
+ return this;
+ }
+
+ ///
+ public override IndexedTxModel Build() =>
+ throw new NotImplementedException();
+ }
+}
diff --git a/src/DxFeed.Graal.Net/Models/TimeSeriesTxModel.cs b/src/DxFeed.Graal.Net/Models/TimeSeriesTxModel.cs
new file mode 100644
index 0000000..203325b
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Models/TimeSeriesTxModel.cs
@@ -0,0 +1,118 @@
+//
+// 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/.
+//
+
+using System;
+using DxFeed.Graal.Net.Api;
+using DxFeed.Graal.Net.Native.Model;
+
+namespace DxFeed.Graal.Net.Models;
+
+///
+/// An incremental model for time-series events.
+/// This model manages all snapshot and transaction logic, subscription handling, and listener notifications.
+///
+///
+///
+/// This model is designed to handle incremental transactions. Users of this model only see the list
+/// of events in a consistent state. This model delays incoming events that are part of an incomplete snapshot
+/// or ongoing transaction until the snapshot is complete or the transaction has ended. This model notifies
+/// the user of received transactions through an installed listener.
+///
+/// Configuration
+///
+/// This model must be configured using the class, as most configuration
+/// settings cannot be changed once the model is built. This model requires configuration with
+/// a symbol and a
+/// fromTime for subscription, and it must be attached
+/// to a instance to begin operation.
+/// For ease of use, some of these configurations can be changed after the model is built, see
+/// .
+///
+///
+/// This model only supports single symbol subscriptions; multiple symbols cannot be configured.
+///
+/// Resource management and closed models
+///
+/// Attached model is a potential memory leak. If the pointer to the attached model is lost, then there is no way
+/// to detach this model from the feed and the model will not be reclaimed by the garbage collector as long as the
+/// corresponding feed is still used. Detached model can be reclaimed by the garbage collector, but detaching the model
+/// requires knowing the pointer to the feed at the place of the call, which is not always convenient.
+///
+///
+/// The convenient way to detach the model from the feed is to call its method.
+/// Closed model becomes permanently detached from all feeds, removes all its listeners and is guaranteed
+/// to be reclaimable by the garbage collector as soon as all external references to it are cleared.
+///
+/// Threads and locks
+///
+/// This class is thread-safe and can be used concurrently from multiple threads without external synchronization.
+///
+///
+public class TimeSeriesTxModel : AbstractTxModel
+{
+ private TimeSeriesTxModel(AbstractTxModelHandle handle)
+ : base(handle)
+ {
+ }
+
+ ///
+ /// Factory method to create a new builder for this model.
+ ///
+ /// The class type of the time series event.
+ /// A new instance.
+ public static Builder NewBuilder(Type eventType) =>
+ new(eventType);
+
+ ///
+ /// Returns the time from which to subscribe for time-series,
+ /// or if this model is not subscribed (this is the default value).
+ ///
+ /// The time in milliseconds since Unix epoch of January 1, 1970.
+ public long GetFromTime() =>
+ ((TimeSeriesTxModelHandle)handle).GetFromTime();
+
+ ///
+ /// Sets the time from which to subscribe for time-series.
+ /// If this time has already been set, nothing happens.
+ ///
+ /// The time in milliseconds since Unix epoch of January 1, 1970.
+ public void SetFromTime(long fromTime) =>
+ ((TimeSeriesTxModelHandle)handle).SetFromTime(fromTime);
+
+ ///
+ /// A builder class for creating an instance of .
+ ///
+ public class Builder : Builder
+ {
+ ///
+ /// Initializes a new instance of the class with the specified event type.
+ ///
+ /// The type of events processed by the model being created.
+ public Builder(Type eventType)
+ : base(eventType)
+ {
+ }
+
+ ///
+ /// Sets the time from which to subscribe for time-series.
+ ///
+ ///
+ /// This time defaults to , which means that this model is not subscribed.
+ /// This time can be changed later, after the model has been created,
+ /// by calling .
+ ///
+ /// The time in milliseconds since Unix epoch of January 1, 1970.
+ /// The builder instance.
+ public Builder WithFromTime(long fromTime)
+ {
+ return this;
+ }
+
+ ///
+ public override TimeSeriesTxModel Build() =>
+ throw new NotImplementedException();
+ }
+}
diff --git a/src/DxFeed.Graal.Net/Models/TxModelListener.cs b/src/DxFeed.Graal.Net/Models/TxModelListener.cs
new file mode 100644
index 0000000..897d303
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Models/TxModelListener.cs
@@ -0,0 +1,13 @@
+//
+// 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/.
+//
+
+using System.Collections.Generic;
+using DxFeed.Graal.Net.Events;
+
+namespace DxFeed.Graal.Net.Models;
+
+
+public delegate void TxModelListener(IndexedEventSource source, IEnumerable events, bool isSnapshot);
diff --git a/src/DxFeed.Graal.Net/Native/Model/AbstractTxModelHandle.cs b/src/DxFeed.Graal.Net/Native/Model/AbstractTxModelHandle.cs
new file mode 100644
index 0000000..6355b58
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Native/Model/AbstractTxModelHandle.cs
@@ -0,0 +1,22 @@
+//
+// 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/.
+//
+
+using DxFeed.Graal.Net.Native.Interop;
+
+namespace DxFeed.Graal.Net.Native.Model;
+
+internal class AbstractTxModelHandle : JavaHandle
+{
+ public bool IsBatchProcessing() =>
+ false;
+
+ public bool IsSnapshotProcessing() =>
+ false;
+
+ public void Close()
+ {
+ }
+}
diff --git a/src/DxFeed.Graal.Net/Native/Model/IndexedTxModelHandle.cs b/src/DxFeed.Graal.Net/Native/Model/IndexedTxModelHandle.cs
new file mode 100644
index 0000000..ff80a24
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Native/Model/IndexedTxModelHandle.cs
@@ -0,0 +1,23 @@
+//
+// 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/.
+//
+
+using System;
+using System.Collections.Generic;
+using DxFeed.Graal.Net.Events;
+
+namespace DxFeed.Graal.Net.Native.Model;
+
+internal class IndexedTxModelHandle : AbstractTxModelHandle
+{
+ public HashSet GetSources() =>
+ throw new NotImplementedException();
+
+ public void SetSources(params IndexedEventSource[] sources) =>
+ throw new NotImplementedException();
+
+ public void SetSources(ICollection sources) =>
+ throw new NotImplementedException();
+}
diff --git a/src/DxFeed.Graal.Net/Native/Model/TimeSeriesTxModelHandle.cs b/src/DxFeed.Graal.Net/Native/Model/TimeSeriesTxModelHandle.cs
new file mode 100644
index 0000000..3304392
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Native/Model/TimeSeriesTxModelHandle.cs
@@ -0,0 +1,18 @@
+//
+// 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/.
+//
+
+using System;
+
+namespace DxFeed.Graal.Net.Native.Model;
+
+internal class TimeSeriesTxModelHandle : AbstractTxModelHandle
+{
+ public long GetFromTime() =>
+ throw new NotImplementedException();
+
+ public void SetFromTime(long fromTime) =>
+ throw new NotImplementedException();
+}
diff --git a/src/DxFeed.Graal.Net/Native/Model/TxEventListenerHandle.cs b/src/DxFeed.Graal.Net/Native/Model/TxEventListenerHandle.cs
new file mode 100644
index 0000000..5e9ba8d
--- /dev/null
+++ b/src/DxFeed.Graal.Net/Native/Model/TxEventListenerHandle.cs
@@ -0,0 +1,63 @@
+//
+// 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/.
+//
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using DxFeed.Graal.Net.Api;
+using DxFeed.Graal.Net.Events;
+using DxFeed.Graal.Net.Events.Market;
+using DxFeed.Graal.Net.Models;
+using DxFeed.Graal.Net.Native.Endpoint;
+using DxFeed.Graal.Net.Native.Events;
+using DxFeed.Graal.Net.Native.Interop;
+using static DxFeed.Graal.Net.Native.ErrorHandling.ErrorCheck;
+
+namespace DxFeed.Graal.Net.Native.Model;
+
+internal class TxEventListenerHandle : JavaHandle
+{
+ private static readonly unsafe Delegate OnEventsReceivedDelegate = new OnEventsReceivedDelegateType(OnEventReceived);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private unsafe delegate void OnEventsReceivedDelegateType(IntPtr thread, int source, ListNative* events, bool isSnapshot, GCHandle handle);
+
+ public static TxEventListenerHandle Create(TxModelListener listener) =>
+ CreateAndRegisterFinalize(listener, handle => SafeCall(Import.New(CurrentThread, OnEventsReceivedDelegate, handle)));
+
+ private static unsafe void OnEventReceived(IntPtr thread, int source, ListNative* events, bool isSnapshot, GCHandle handle)
+ {
+ if (!handle.IsAllocated)
+ {
+ return;
+ }
+
+ var listener = handle.Target as TxModelListener;
+ try
+ {
+ var eventList = EventMapper.FromNative(events);
+ listener?.Invoke(OrderSource.ValueOf(source), eventList.OfType(), isSnapshot);
+ }
+ catch (Exception e)
+ {
+ // ToDo Add log entry.
+ Console.Error.WriteLine($"Exception in user {nameof(TxModelListener)}. {e}");
+ }
+ }
+
+ private static class Import
+ {
+ [DllImport(
+ ImportInfo.DllName,
+ CallingConvention = CallingConvention.Cdecl,
+ CharSet = CharSet.Ansi,
+ EntryPoint = "dxfg_PropertyChangeListener_new")]
+ public static extern TxEventListenerHandle New(
+ nint thread,
+ Delegate listener,
+ GCHandle handle);
+ }
+}