Add endpoint for indicator refiner
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Managing.Api.Models.Requests;
|
using Managing.Api.Models.Requests;
|
||||||
|
using Managing.Api.Models.Responses;
|
||||||
using Managing.Application.Abstractions;
|
using Managing.Application.Abstractions;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Application.Shared;
|
using Managing.Application.Shared;
|
||||||
@@ -340,4 +341,54 @@ public class TradingController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates indicator values and generates signals for a given ticker, timeframe, and date range with selected indicators.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request containing ticker, timeframe, date range, and indicators configuration.</param>
|
||||||
|
/// <returns>A response containing calculated indicator values and generated signals.</returns>
|
||||||
|
[HttpPost("RefineIndicators")]
|
||||||
|
public async Task<ActionResult<RefineIndicatorsResponse>> RefineIndicators(
|
||||||
|
[FromBody] RefineIndicatorsRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Validate request
|
||||||
|
if (request == null)
|
||||||
|
{
|
||||||
|
return BadRequest("Request cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.Indicators == null || request.Indicators.Count == 0)
|
||||||
|
{
|
||||||
|
return BadRequest("At least one indicator must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.StartDate >= request.EndDate)
|
||||||
|
{
|
||||||
|
return BadRequest("Start date must be before end date.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call service - request.Indicators is already List<IndicatorRequest>
|
||||||
|
var result = await _tradingService.RefineIndicatorsAsync(
|
||||||
|
request.Ticker,
|
||||||
|
request.Timeframe,
|
||||||
|
request.StartDate,
|
||||||
|
request.EndDate,
|
||||||
|
request.Indicators);
|
||||||
|
|
||||||
|
// Map service result to API response
|
||||||
|
return Ok(new RefineIndicatorsResponse
|
||||||
|
{
|
||||||
|
IndicatorsValues = result.IndicatorsValues,
|
||||||
|
Signals = result.Signals
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error refining indicators for ticker {Ticker}, timeframe {Timeframe}",
|
||||||
|
request?.Ticker, request?.Timeframe);
|
||||||
|
return StatusCode(500, $"Error refining indicators: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
42
src/Managing.Api/Models/Requests/RefineIndicatorsRequest.cs
Normal file
42
src/Managing.Api/Models/Requests/RefineIndicatorsRequest.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Requests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request model for refining indicators and generating signals.
|
||||||
|
/// </summary>
|
||||||
|
public class RefineIndicatorsRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ticker symbol (e.g., "BTC", "ETH").
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Ticker Ticker { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The timeframe for the candles (e.g., "FifteenMinutes", "OneHour", "OneDay").
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Timeframe Timeframe { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The start date for the date range (ISO 8601 format).
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime StartDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The end date for the date range (ISO 8601 format).
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime EndDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Array of selected indicators with their parameters.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public List<IndicatorRequest> Indicators { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Managing.Domain.Indicators;
|
||||||
|
using Managing.Domain.Strategies.Base;
|
||||||
|
using static Managing.Common.Enums;
|
||||||
|
|
||||||
|
namespace Managing.Api.Models.Responses;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Response model for refined indicators and signals.
|
||||||
|
/// </summary>
|
||||||
|
public class RefineIndicatorsResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary of indicator types to their calculated values over time.
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; } =
|
||||||
|
new Dictionary<IndicatorType, IndicatorsResultBase>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Array of signals generated for the date range.
|
||||||
|
/// </summary>
|
||||||
|
public List<LightSignal> Signals { get; set; } = new List<LightSignal>();
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
using Managing.Domain.Indicators;
|
using Managing.Domain.Indicators;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
|
using Managing.Domain.Strategies.Base;
|
||||||
using Managing.Domain.Synth.Models;
|
using Managing.Domain.Synth.Models;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -57,4 +59,24 @@ public interface ITradingService
|
|||||||
Task<IEnumerable<Position>> GetPositionByUserIdAsync(int userId);
|
Task<IEnumerable<Position>> GetPositionByUserIdAsync(int userId);
|
||||||
Task<SwapInfos> SwapGmxTokensAsync(User user, string accountName, Ticker fromTicker, Ticker toTicker,
|
Task<SwapInfos> SwapGmxTokensAsync(User user, string accountName, Ticker fromTicker, Ticker toTicker,
|
||||||
double amount, string orderType = "market", double? triggerRatio = null, double allowedSlippage = 0.5);
|
double amount, string orderType = "market", double? triggerRatio = null, double allowedSlippage = 0.5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates indicator values and generates signals for a given ticker, timeframe, and date range with selected indicators.
|
||||||
|
/// </summary>
|
||||||
|
Task<RefineIndicatorsResult> RefineIndicatorsAsync(
|
||||||
|
Ticker ticker,
|
||||||
|
Timeframe timeframe,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
List<IndicatorRequest> indicators);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Result of refining indicators operation.
|
||||||
|
/// </summary>
|
||||||
|
public class RefineIndicatorsResult
|
||||||
|
{
|
||||||
|
public Dictionary<IndicatorType, IndicatorsResultBase> IndicatorsValues { get; set; } =
|
||||||
|
new Dictionary<IndicatorType, IndicatorsResultBase>();
|
||||||
|
public List<LightSignal> Signals { get; set; } = new List<LightSignal>();
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
using Managing.Application.Abstractions.Repositories;
|
using Managing.Application.Abstractions.Repositories;
|
||||||
using Managing.Application.Abstractions.Services;
|
using Managing.Application.Abstractions.Services;
|
||||||
using Managing.Domain.Accounts;
|
using Managing.Domain.Accounts;
|
||||||
|
using Managing.Domain.Backtests;
|
||||||
using Managing.Domain.Bots;
|
using Managing.Domain.Bots;
|
||||||
|
using Managing.Domain.Candles;
|
||||||
using Managing.Domain.Indicators;
|
using Managing.Domain.Indicators;
|
||||||
using Managing.Domain.Scenarios;
|
using Managing.Domain.Scenarios;
|
||||||
using Managing.Domain.Shared.Helpers;
|
using Managing.Domain.Shared.Helpers;
|
||||||
using Managing.Domain.Statistics;
|
using Managing.Domain.Statistics;
|
||||||
using Managing.Domain.Strategies;
|
using Managing.Domain.Strategies;
|
||||||
|
using Managing.Domain.Strategies.Base;
|
||||||
using Managing.Domain.Synth.Models;
|
using Managing.Domain.Synth.Models;
|
||||||
using Managing.Domain.Trades;
|
using Managing.Domain.Trades;
|
||||||
using Managing.Domain.Users;
|
using Managing.Domain.Users;
|
||||||
@@ -491,4 +494,141 @@ public class TradingService : ITradingService
|
|||||||
throw new InvalidOperationException($"Failed to swap GMX tokens: {ex.Message}", ex);
|
throw new InvalidOperationException($"Failed to swap GMX tokens: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates indicator values and generates signals for a given ticker, timeframe, and date range with selected indicators.
|
||||||
|
/// </summary>
|
||||||
|
public async Task<RefineIndicatorsResult> RefineIndicatorsAsync(
|
||||||
|
Ticker ticker,
|
||||||
|
Timeframe timeframe,
|
||||||
|
DateTime startDate,
|
||||||
|
DateTime endDate,
|
||||||
|
List<IndicatorRequest> indicators)
|
||||||
|
{
|
||||||
|
// Get candles for the specified period
|
||||||
|
var candles = await _exchangeService.GetCandlesInflux(
|
||||||
|
TradingExchanges.Evm,
|
||||||
|
ticker,
|
||||||
|
startDate,
|
||||||
|
timeframe,
|
||||||
|
endDate);
|
||||||
|
|
||||||
|
if (candles == null || candles.Count == 0)
|
||||||
|
{
|
||||||
|
return new RefineIndicatorsResult
|
||||||
|
{
|
||||||
|
IndicatorsValues = new Dictionary<IndicatorType, IndicatorsResultBase>(),
|
||||||
|
Signals = new List<LightSignal>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to ordered List to preserve chronological order for indicators
|
||||||
|
var candlesList = candles.OrderBy(c => c.Date).ToList();
|
||||||
|
|
||||||
|
// Map request indicators to domain Scenario
|
||||||
|
var scenario = MapRefineIndicatorsToScenario(indicators);
|
||||||
|
|
||||||
|
// Calculate indicators values
|
||||||
|
var indicatorsValues = TradingBox.CalculateIndicatorsValues(scenario, candlesList);
|
||||||
|
|
||||||
|
// Generate signals for the date range using rolling window approach
|
||||||
|
var signals = GenerateSignalsForDateRange(candlesList, scenario, indicatorsValues);
|
||||||
|
|
||||||
|
return new RefineIndicatorsResult
|
||||||
|
{
|
||||||
|
IndicatorsValues = indicatorsValues,
|
||||||
|
Signals = signals
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps IndicatorRequest list to a domain Scenario object.
|
||||||
|
/// </summary>
|
||||||
|
private Scenario MapRefineIndicatorsToScenario(List<IndicatorRequest> indicators)
|
||||||
|
{
|
||||||
|
var scenario = new Scenario("RefineIndicators", 1);
|
||||||
|
|
||||||
|
foreach (var indicatorRequest in indicators)
|
||||||
|
{
|
||||||
|
var indicator = new IndicatorBase(indicatorRequest.Name, indicatorRequest.Type)
|
||||||
|
{
|
||||||
|
SignalType = indicatorRequest.SignalType,
|
||||||
|
MinimumHistory = indicatorRequest.MinimumHistory,
|
||||||
|
Period = indicatorRequest.Period,
|
||||||
|
FastPeriods = indicatorRequest.FastPeriods,
|
||||||
|
SlowPeriods = indicatorRequest.SlowPeriods,
|
||||||
|
SignalPeriods = indicatorRequest.SignalPeriods,
|
||||||
|
Multiplier = indicatorRequest.Multiplier,
|
||||||
|
StDev = indicatorRequest.StDev,
|
||||||
|
SmoothPeriods = indicatorRequest.SmoothPeriods,
|
||||||
|
StochPeriods = indicatorRequest.StochPeriods,
|
||||||
|
CyclePeriods = indicatorRequest.CyclePeriods,
|
||||||
|
KFactor = indicatorRequest.KFactor,
|
||||||
|
DFactor = indicatorRequest.DFactor,
|
||||||
|
TenkanPeriods = indicatorRequest.TenkanPeriods,
|
||||||
|
KijunPeriods = indicatorRequest.KijunPeriods,
|
||||||
|
SenkouBPeriods = indicatorRequest.SenkouBPeriods,
|
||||||
|
OffsetPeriods = indicatorRequest.OffsetPeriods,
|
||||||
|
SenkouOffset = indicatorRequest.SenkouOffset,
|
||||||
|
ChikouOffset = indicatorRequest.ChikouOffset
|
||||||
|
};
|
||||||
|
|
||||||
|
scenario.AddIndicator(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return scenario;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates signals for a date range using a rolling window approach.
|
||||||
|
/// </summary>
|
||||||
|
private List<LightSignal> GenerateSignalsForDateRange(
|
||||||
|
List<Candle> candles,
|
||||||
|
Scenario scenario,
|
||||||
|
Dictionary<IndicatorType, IndicatorsResultBase> preCalculatedIndicatorValues)
|
||||||
|
{
|
||||||
|
var allSignals = new List<LightSignal>();
|
||||||
|
var previousSignals = new Dictionary<string, LightSignal>();
|
||||||
|
var lightScenario = LightScenario.FromScenario(scenario);
|
||||||
|
|
||||||
|
// Use rolling window approach similar to backtests
|
||||||
|
const int RollingWindowSize = 600;
|
||||||
|
var rollingWindowCandles = new List<Candle>(RollingWindowSize);
|
||||||
|
|
||||||
|
foreach (var candle in candles)
|
||||||
|
{
|
||||||
|
// Maintain rolling window
|
||||||
|
if (rollingWindowCandles.Count >= RollingWindowSize)
|
||||||
|
{
|
||||||
|
rollingWindowCandles.RemoveAt(0);
|
||||||
|
}
|
||||||
|
rollingWindowCandles.Add(candle);
|
||||||
|
|
||||||
|
// Only process if we have enough candles for indicators
|
||||||
|
if (rollingWindowCandles.Count < 2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate signal for current position
|
||||||
|
var signal = TradingBox.GetSignal(
|
||||||
|
rollingWindowCandles,
|
||||||
|
lightScenario,
|
||||||
|
previousSignals,
|
||||||
|
scenario.LookbackPeriod,
|
||||||
|
preCalculatedIndicatorValues);
|
||||||
|
|
||||||
|
if (signal != null)
|
||||||
|
{
|
||||||
|
// Check if this is a new signal (not already in our collection)
|
||||||
|
if (!previousSignals.ContainsKey(signal.Identifier))
|
||||||
|
{
|
||||||
|
allSignals.Add(signal);
|
||||||
|
previousSignals[signal.Identifier] = signal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allSignals.OrderBy(s => s.Date).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user