Grumson.Utilities.Taapi.Signals
1.0.7
See the version list below for details.
dotnet add package Grumson.Utilities.Taapi.Signals --version 1.0.7
NuGet\Install-Package Grumson.Utilities.Taapi.Signals -Version 1.0.7
<PackageReference Include="Grumson.Utilities.Taapi.Signals" Version="1.0.7" />
paket add Grumson.Utilities.Taapi.Signals --version 1.0.7
#r "nuget: Grumson.Utilities.Taapi.Signals, 1.0.7"
// Install Grumson.Utilities.Taapi.Signals as a Cake Addin #addin nuget:?package=Grumson.Utilities.Taapi.Signals&version=1.0.7 // Install Grumson.Utilities.Taapi.Signals as a Cake Tool #tool nuget:?package=Grumson.Utilities.Taapi.Signals&version=1.0.7
Taapi Signals
Table of Contents
Introduction
The Taapi Signals Library is a powerful C# library designed for building and managing trading signals using Finite State Machines (FSM). This library integrates seamlessly with the Taapi.io API, allowing users to leverage a wide range of trading indicators for creating custom trading strategies.
With the Taapi Signals Library, developers can create robust and flexible trading systems by defining states, transitions, and conditions based on indicator values. The library provides a structured framework for automating trading workflows, making it an essential tool for algorithmic trading solutions.
Key Concepts
- Signals as Finite State Machines (FSM): Each trading signal is represented as an FSM, consisting of multiple states and transitions.
- Indicator Integration: Leverage a variety of trading indicators from Taapi.io to define conditions for state transitions.
- Customizable Conditions: Define indicator-based or event-driven conditions to trigger state changes.
- Logging: Comprehensive logging is built-in for easy debugging and monitoring.
Use Cases
- Algorithmic Trading: Automate trading strategies by defining complex signal logic.
- Backtesting: Simulate trading signals using historical data for strategy optimization.
- Real-time Monitoring: Continuously evaluate market conditions and transition between signal states accordingly.
The Taapi Signals Library simplifies the process of creating sophisticated trading signals while ensuring clarity and maintainability of the trading logic.
Features
- Finite State Machine Framework: Create, manage, and execute trading signals as FSMs.
- Seamless Integration with Taapi.io: Utilize Taapi.io's rich set of indicators directly within your signal logic.
- Flexible State Management: Define multiple states (e.g., Idle, TradeLong, TradeShort) and transitions for complex workflows.
- Stop Loss Management: Set stop loss values for each state of FSM to manage risk effectively. Stop loss can be calculated based on the indicator value, or it can be a static values.
- Condition-Based Transitions: Support for indicator-driven and event-driven conditions for state changes.
- Event Handling: Subscribe to events for monitoring signal transitions, state changes, indicator values, stop loss values, etc.
- Extensibility: Easily extend the library with custom conditions, actions, or states.
- Comprehensive Logging: Built-in logging capabilities for monitoring and debugging signals.
- Support for Backtesting: Evaluate signals using historical data to optimize strategies.
- Real-Time Execution: Monitor live market conditions and execute signals in real time.
The library is designed to provide flexibility and ease of use, making it suitable for both beginners and advanced developers in algorithmic trading.
Installation
To install the Taapi Signals Library, follow these steps:
Prerequisites
- .NET 9.0 or higher: Ensure you have .NET SDK installed. You can download it from Microsoft's official site.
- Taapi.io API Key: Sign up at Taapi.io and obtain an API key for accessing indicators. Free version of the API key will probably not work, because of the limitations of the free version of the API key.
Installation Steps
Add the library to your .NET project using one of the following methods:
// .NET CLI
> dotnet add package Grumson.Utilities.Taapi.Signals --version x.x.x
// Package Manager
PM> NuGet\Install-Package Grumson.Utilities.Taapi.Signals -Version x.x.x
// Package Reference
<PackageReference Include="Grumson.Utilities.Taapi.Signals" Version="x.x.x" />
Or, include the project in your solution and reference it directly.
You're now ready to start using the Taapi Signals Library in your projects!
Usage
This section provides an examples of how to use the Taapi Signals Library to create and manage trading signals.
Basic Examples
Initialize the Logger, Signal Manager and Taapi Client
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); ILogger<TaapiClient> taapiLogger = loggerFactory.CreateLogger<TaapiClient>(); // Initialize taaapi client var taapiClient = new TaapiClient(taapiLogger, taapi_api_key, subscriptionType); // Initialize signal manager var signalManager = new SignalManager(loggerFactory, taapiClient);
Create a New Signal
SignalModel signalModel = new SignalModel { Name = "SignalTest_Strategy", Description = "Description of the signal" };
Add Indicators to the Signal
var rsiIndicator = new IndicatorModel { Name = "RsiCondition", IndicatorRequest = new IndicatorRequest { Exchange = "binance", Symbol = "BTC/USDT", Interval = "5m", Indicator = "rsi", AdditionalParameters = new Dictionary<string, string> { { "period", "15" } } } }; signalModel.Indicators.Add(rsiIndicator);
Define Signal States
var startState = new SignalState { Name = "Start", IsInitial = true }; var idleState = new SignalState { Name = "Idle" }; signalModel.States.Add(startState); signalModel.States.Add(idleState);
Create Indicator Conditions with static threshold
// Simple RSI condition: RSI > 70 var rsiCondition = new IndicatorCondition { IndicatorModel = rsiIndicator, SubValueKey = "value", ConditionOperator = ConditionOperator.GreaterThan, Threshold = 70 }; // Wrapper for indicator condition var rsiConditionWrapper = new IndicatorConditionWrapper(rsiCondition, loggerFactory.CreateLogger<IndicatorConditionWrapper>());
Create Indicator Conditions with dynamic threshold
// RSI condition with dynamic threshold from RSI indicator -> ( CONDITION: 5 MIN RSI > 1 HOUR RSI ) var rsiConditionDynamicThreshold = new IndicatorCondition { IndicatorModel = IndicatorRsiCondition, SubValueKey = "value", ConditionOperator = ConditionOperator.GreaterThan, //Threshold = 0m, IsDynamicThreshold = true, // Dynamic threshold based on the indicator value ThresholdIndicator = IndicatorRsiThreshold, // Indicator for dynamic threshold, ThresholdSubValueKey = "value" // Sub value key for dynamic threshold }; // Wrappers for indicator conditions var rsiConditionDynamicThresholdWrapper = new IndicatorConditionWrapper(rsiConditionDynamicThreshold, loggerFactory.CreateLogger<IndicatorConditionWrapper>());
Create Composite Conditions with AND/OR Logic
// Composite condition: with AND logic var compositeCondition = new CompositeCondition(loggerFactory.CreateLogger<CompositeCondition>()) { LogicOperator = LogicOperator.And, // AND or OR logic selection Conditions = new List<BaseCondition> { rsiConditionWrapper, rsiConditionDynamicThresholdWrapper } };
Create Event Driven Conditions
// Event driven condition for price crossed above specific value var priceCrossedCondition = new EventDrivenCondition { EventName = "PriceCrossed", // Ime dogodka Predicate = parameters => (decimal)parameters["Price"] > 50 // Predicate to check if the condition is met, (if ) }; // Event driven condition for button pressed var buttonPressedCondition = new EventDrivenCondition { EventName = "ButtonPressed", // Name of the event Predicate = _ => true // Predicate to check if the condition is met, for button press it is always true };
Add Transitions with Conditions
var transition = new StateTransition { Name = "StartToIdle", FromState = startState, ToState = idleState, Conditions = new List<BaseCondition> { compositeCondition } // Conditions from indicators EventDrivenConditions = new List<EventDrivenCondition> { buttonPressedCondition, buttonPressedCondition } // Event driven conditions }; signalModel.Transitions.Add(transition);
Validate and Save the Signal
try { // Save signal model to the signal manager, // method will first validate the model and throw exception if model is not valid signalModel.Id = await signalManager.CreateSignal(signalModel); } catch (ArgumentException ex) { Console.WriteLine($"Argument Exception: {ex.Message}"); return; } catch (InvalidOperationException ex) { Console.WriteLine($"Invalid Operation Exception: {ex.Message}"); return; } catch (Exception ex) { Console.WriteLine($"Error: {ex.Message}"); return; }
Start Fetching the Signal
signalManager.StartAutoFetching(signalId, TimeSpan.FromMinutes(1));
Subscribe to Events
signalManager.SubscribeToEvents(signalId, beforeTransition: (service, transition) => Console.WriteLine($"Before transitioning: {transition.Name}"), afterTransition: (service, transition) => Console.WriteLine($"After transitioning: {transition.Name}"));
This is a basic setup to get you started. Customize the indicators, conditions, states, and transitions as per your trading strategy.
Example of Console Application
This example is only for demonstration purposes and should not be used in a production environment. It demonstrates how to create a simple trading signal using the Taapi Signals Library.
class Program {
// Taapi API key
static string taapi_api_key = "YOUR_TAAPI_API_KEY";
// Subscription type for taapi
static SubscriptionType subscriptionType = SubscriptionType.Pro;
// Main method
static async Task Main(string[] args) {
#region 1. Initialize Logger's
ILoggerFactory loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger<TaapiClient> taapiLogger = loggerFactory.CreateLogger<TaapiClient>();
ILogger<EventManager> signalEventManager = loggerFactory.CreateLogger<EventManager>();
#endregion
#region 2. Initialize Signal Manager, Taapi Client, Event Manager
// Initialize taaapi client
var taapiClient = new TaapiClient(taapiLogger, taapi_api_key, subscriptionType);
// Initialize event manager
var eventManager = new EventManager(signalEventManager);
// Initialize signal manager
var signalManager = new SignalManager(loggerFactory, taapiClient, eventManager);
#endregion
#region 4. Create a new signal
SignalModel signalModel = new SignalModel {
Name = "SignalTest_Strategy",
Description = "Test if signal manger works correctly",
};
#endregion
#region 5. Define Indicators needed for the signal
// RSI indicator for transition condition
IndicatorModel IndicatorRsiCondition = new IndicatorModel {
Name = "RsiCondition",
IndicatorRequest = new IndicatorRequest {
Exchange = "binance",
Symbol = "BTC/USDT",
Interval = "5m",
Indicator = "rsi",
AdditionalParameters = new Dictionary<string, string> {
{ "period", "15" },
}
},
};
// RSI indicator for transition condition threshold
IndicatorModel IndicatorRsiThreshold = new IndicatorModel {
Name = "RsiThreshold",
IndicatorRequest = new IndicatorRequest {
Exchange = "binance",
Symbol = "BTC/USDT",
Interval = "1h",
Indicator = "rsi",
AdditionalParameters = new Dictionary<string, string> {
{ "period", "15" },
}
},
};
// Supertrend indicator for AND condition with RSI
IndicatorModel IndicatorSupertrend = new IndicatorModel {
Name = "Supertrend",
IndicatorRequest = new IndicatorRequest {
Exchange = "binance",
Symbol = "BTC/USDT",
Interval = "1h",
Indicator = "supertrend",
Backtrack = 1,
AdditionalParameters = new Dictionary<string, string> {
{ "period", "10" },
{ "multiplier", "3" }
}
},
};
// ATR indicator for stop loss line
IndicatorModel IndicatorAtr = new IndicatorModel {
Name = "Atr",
IndicatorRequest = new IndicatorRequest {
Exchange = "binance",
Symbol = "BTC/USDT",
Interval = "1h",
Indicator = "atr",
AdditionalParameters = new Dictionary<string, string> {
{ "period", "14" },
}
},
};
// Add indicators to the signal
signalModel.Indicators.Add(IndicatorRsiCondition);
signalModel.Indicators.Add(IndicatorRsiThreshold);
signalModel.Indicators.Add(IndicatorSupertrend);
signalModel.Indicators.Add(IndicatorAtr);
#endregion
#region 6. Define signal states
// Start state with initial flag ( FSM will start from this state )
var startState = new SignalState { Name = "Start", IsInitial = true };
// Sate wher we are waiting for the trading signal
var idleState = new SignalState { Name = "Idle" };
// State where we are trading, inthis we set the stop loss
var tradeState = new SignalState {
Name = "Trade",
StopLoss = new StopLossModel {
Indicator = IndicatorAtr,
IndicatorValueName = "value",
IndicatorMultiplier = 1.5m
}
};
signalModel.States.Add(startState);
signalModel.States.Add(idleState);
signalModel.States.Add(tradeState);
#endregion
#region 7. Define indicators conditions and composite conditions
// Final composite condition with RSI and Supertrend with OR logic -> (5 MIN RSI > 1 HOUR RSI ) || SUPERTREND == LONG )
// If five minute RSI is greater than one hour RSI, and Supertrend is long, then transition to Trade state
// This condition is used for the final transition from Idle to Trade state
// RSI condition with dynamic threshold from RSI indicator -> ( CONDITION: 5 MIN RSI > 1 HOUR RSI )
var rsiConditionDynamicThreshold = new IndicatorCondition {
IndicatorModel = IndicatorRsiCondition,
SubValueKey = "value",
ConditionOperator = ConditionOperator.GreaterThan,
//Threshold = 0m,
IsDynamicThreshold = true, // Dynamic threshold based on the indicator value
ThresholdIndicator = IndicatorRsiThreshold, // Indicator for dynamic threshold,
ThresholdSubValueKey = "value" // Sub value key for dynamic threshold
};
// Supertrend condition with static threshold -> ( CONDITION: SUPERTREND == LONG )
var supertrendCondition = new IndicatorCondition {
IndicatorModel = IndicatorSupertrend,
SubValueKey = "valueAdvice",
ConditionOperator = ConditionOperator.Equal,
Threshold = "long" // Static threshold for Supertrend,
};
// Wrappers for indicator conditions
var rsiConditionDynamicThresholdWrapper = new IndicatorConditionWrapper(rsiConditionDynamicThreshold, loggerFactory.CreateLogger<IndicatorConditionWrapper>());
var supertrendConditionWrapper = new IndicatorConditionWrapper(supertrendCondition, loggerFactory.CreateLogger<IndicatorConditionWrapper>());
// Composite condition with RSI and Supertrend with AND logic -> ( CONDITION: (5 MIN RSI > 1 HOUR RSI ) && SUPERTREND == LONG )
var compositeCondition = new CompositeCondition(loggerFactory.CreateLogger<CompositeCondition>()) {
LogicOperator = LogicOperator.And,
Conditions = new List<BaseCondition>
{
rsiConditionDynamicThresholdWrapper, // RSI -> ( CONDITION: 5 MIN RSI > 1 HOUR RSI )
supertrendConditionWrapper // SUPERTREND -> ( CONDITION: SUPERTREND == LONG )
}
};
// RSI condition with static threshold -> ( CONDITION: 5 MIN RSI > 75 )
var rsiCondition = new IndicatorCondition {
IndicatorModel = IndicatorRsiCondition,
SubValueKey = "value",
ConditionOperator = ConditionOperator.GreaterThan,
Threshold = 75,
};
// Wrapper for RSI condition
var rsiConditionConditionWrapper = new IndicatorConditionWrapper(rsiCondition, loggerFactory.CreateLogger<IndicatorConditionWrapper>());
#endregion
#region 8. Define event driven conditions
// Event driven condition for price crossed above specific value
var priceCrossedCondition = new EventDrivenCondition {
EventName = "PriceCrossed", // Ime dogodka
Predicate = parameters => (decimal)parameters["Price"] > 50 // Predicate to check if the condition is met, (if )
};
// Event driven condition for button pressed
var buttonPressedCondition = new EventDrivenCondition {
EventName = "ButtonPressed", // Name of the event
Predicate = _ => true // Predicate to check if the condition is met, for button press it is always true
};
#endregion
#region 9. Add transitions to the signal with conditions
// Transition from Start to Idle state
var transitionStartToIdle = new StateTransition {
Name = "StartToIdle",
Description = "Transition from Start to Idle state",
FromState = startState,
ToState = idleState,
Conditions = new List<BaseCondition> { compositeCondition }, // Conditions based on the indicators
EventDrivenConditions = new List<EventDrivenCondition> { buttonPressedCondition } // Event driven conditions
};
signalModel.Transitions.Add(transitionStartToIdle);
// Transition from Idle to Trade state
var transitionIdleToTrade = new StateTransition {
Name = "IdleToTrade",
Description = "Transition from Idle to Trade state",
FromState = idleState,
ToState = tradeState,
Conditions = new List<BaseCondition> { compositeCondition },
EventDrivenConditions = new List<EventDrivenCondition> { priceCrossedCondition }
};
signalModel.Transitions.Add(transitionIdleToTrade);
// Transition from Trade to Idle state
var transitionTradeToIdle = new StateTransition {
Name = "TradeToIdle",
Description = "Transition from Trade to Idle state",
FromState = tradeState,
ToState = idleState,
Conditions = new List<BaseCondition> { rsiConditionConditionWrapper }
};
signalModel.Transitions.Add(transitionTradeToIdle);
#endregion
#region 10. Validate signal model
try {
// Validate signal model if needed for some reason
signalManager.ValidateSignalModel(signalModel);
}
catch (ArgumentException ex) {
Console.WriteLine($"Argument Exception: {ex.Message}");
return;
}
catch (InvalidOperationException ex) {
Console.WriteLine($"Invalid Operation Exception: {ex.Message}");
return;
}
catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
return;
}
#endregion
#region 11. Save signal model to the signal manager
try {
// Save signal model to the signal manager,
// method will first validate the model and throw exception if model is not valid
signalModel.Id = await signalManager.CreateSignal(signalModel);
}
catch (ArgumentException ex) {
Console.WriteLine($"Argument Exception: {ex.Message}");
return;
}
catch (InvalidOperationException ex) {
Console.WriteLine($"Invalid Operation Exception: {ex.Message}");
return;
}
catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
return;
}
#endregion
#region 12. Register to signal events
// Register to signal events
signalManager.SubscribeToEvents(signalModel.Id,
beforeTransition: BeforeTransitionEvent,
afterTransition: AfterTransitionEvent,
onEnterState: OnEnterStateEvent,
onExitState: OnExitStateEvent,
onIndicatorValueFetched: OnIndicatorValueFetchedEvent
);
tradeState.StopLoss.OnStopLossCalculatedEvent += OnStopLossCalculated;
#endregion
#region 13. Start fetching signal
// Start fetching signal
//signalManager.StartAutoFetching(signalModel.Id, new TimeSpan(0,1,0));
#endregion
#region 13. Test event driven conditions
// Not met conditions
eventManager.TriggerEvent("PriceCrossed", new Dictionary<string, object> { { "Price", 40 } });
// Wait for 5 seconds
await Task.Delay(5000);
// Met conditions
eventManager.TriggerEvent("ButtonPressed", new Dictionary<string, object>());
// Wait for 5 seconds
await Task.Delay(5000);
// Met conditions
eventManager.TriggerEvent("PriceCrossed", new Dictionary<string, object> { { "Price", 60 } });
#endregion
// Wait for user input to stop the program
Console.WriteLine("Press any key to stop the program");
Console.ReadKey();
}//end Main()
// Before Transition Event
private static void BeforeTransitionEvent(SignalService service, StateTransition transition) {
Console.WriteLine($"Before transitioning from {transition.FromState.Name} to {transition.ToState.Name}");
}//end BeforeTransitionEvent()
// After Transition Event
private static void AfterTransitionEvent(SignalService service, StateTransition transition) {
Console.WriteLine($"After transitioning from {transition.FromState.Name} to {transition.ToState.Name}");
}//end AfterTransitionEvent()
// On Enter State Event
private static void OnEnterStateEvent(SignalService service, SignalState state) {
Console.WriteLine($"Entered state: {state.Name}");
}//end OnEnterStateEvent()
// On Exit State Event
private static void OnExitStateEvent(SignalService service, SignalState state) {
Console.WriteLine($"Exited state: {state.Name}");
}//end OnExitStateEvent()
// On Indicator Value Fetched Event
private static void OnIndicatorValueFetchedEvent(string indicatorKey, object indicatorValue) {
Console.WriteLine($"Indicator value fetched: {indicatorKey} = {indicatorValue}");
}//end OnIndicatorValueFetchedEvent()
// On Stop Loss Calculated Event
public static void OnStopLossCalculated( string stateName, decimal stopLossValue) {
Console.WriteLine($"Stop loss calculated for state {stateName}: {stopLossValue}");
}//end OnStopLossCalculated()
}// class
Contributing
Contributions are welcome! If you have any questions, suggestions, or you want to contribute to this project, feel free to contact me at info@grumson.eu
License
This project is licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE
for more information.
Support
Use this link to register on Taapi.io and you will support me with a small commission. Thank you!
If you like this project and you want to support it, you can donate to the following addresses:
Network BSC BNB smart chain (BEP20) : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
- BNB : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
- USDT : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
Network Solana (SPL) : 4D1W3Vv2tbfAzuEgBSiNGqdtGT5wUjbodoF6mXEsnvTf
- SOL : 4D1W3Vv2tbfAzuEgBSiNGqdtGT5wUjbodoF6mXEsnvTf
- USDC : 4D1W3Vv2tbfAzuEgBSiNGqdtGT5wUjbodoF6mXEsnvTf
Network Ethereum (ERC20) : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
- ETH : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
- USDC : 0xd8c509ed7d8f96847618d926e2b831d804e02ece
BTC : 19pxXzh1Kzzw73v6iKbowr1DJro5ozgZj6
Changelog
Version 1.0.7 (30.01.2025)
- Add methods to SignalManager:
- GetBulkRequest(SignalModel signalModel)
- GetBulkResponsesAsync(Guid signalId)
- GetBulkResponsesAsync(SignalModel signalModel)
Version 1.0.6 (27.01.2025)
- Add methods to SignalManager:
- GetBulkRequest(Guid signalId, SignalModel signalModel) to get the bulk request for specific signal model, to validate the signal indicators before creating the signal
- GetBulkRequest(Guid signalId), to get the bulk request for created signal in the signal manager
- GetBulkResponsesAsync(Guid signalId, BulkRequest bulkRequest), to get signals data for the bulk request, to chek if the indicators settings are working correctly
- Set deprecated methods to SignalManager:
- CreateSignal(SignalModel signalModel), use CreateSignalAsync(SignalModel signalModel) instead
Version 1.0.5 (23.01.2025)
- when signal go to new state and this state has static stop loss it will rise event OnStopLossCalculated with the stop loss value. Just for the first time when the signal goes to the state.
Version 1.0.4 (23.01.2025)
- Add Stop Loss value to the signal state.
- Now you can allso set the static stop loss value for each state of the signal. Not just the dynamic thru the indicator value.
Version 1.0.3 (23.01.2025)
- Some minor bug fixes and improvements.
Version 1.0.1 (22.01.2025)
- Added event driven conditions to the signal model.
Version 1.0.0 (21.01.2025)
- Initial release of the Taapi Signals Library.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
-
net9.0
- Grumson.Utilities.Taapi (>= 2.0.3)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Grumson.Utilities.Taapi.Signals:
Package | Downloads |
---|---|
Grumson.Utilities.Taapi.Signals.Blazor
The Taapi Signals Blazor Razor Class Library (RCL) is a reusable component library built for managing and visualizing trading signals using the Taapi Signals Library. This Razor Class Library provides a UI layer for defining, testing, and managing trading signals in a structured manner, utilizing **Finite State Machines (FSMs)**. |
GitHub repositories
This package is not used by any popular GitHub repositories.