Files
PineSript/PineScript/TradingSignal.cs
2026-04-25 17:32:49 +09:00

105 lines
4.1 KiB
C#

using Serilog;
namespace PineScriptV6
{
/// <summary>plotshape의 text 값으로 감지된 매매 신호 종류</summary>
public enum SignalType
{
Buy, // Long 진입
Sell, // Long 청산
Short, // Short 진입
Cover // Short 청산
}
/// <summary>
/// 신호 발생 시점의 데이터를 담는 레코드.
/// TradingView 결과와 비교하는 데 사용합니다.
/// </summary>
public record TradeSignalEvent
{
public DateTimeOffset Time { get; init; }
public SignalType Signal { get; init; }
public decimal Price { get; init; }
/// <summary>CSV 한 줄 포맷</summary>
public string ToCsvLine() =>
$"{Time:yyyy-MM-dd HH:mm},{Signal},{Price:F4}";
/// <summary>콘솔 출력용 포맷</summary>
public override string ToString() =>
$" {Time:yyyy-MM-dd HH:mm} | {Signal,-10} | {Price,10:F4}";
}
/// <summary>
/// Pine Script의 plotshape → C# 매매 함수를 연결하는 신호 허브.
/// 발생한 신호는 Results 컬렉션에 누적되어 TradingView 결과와 비교할 수 있습니다.
/// </summary>
public static class TradingSignal
{
// ── 매매 델리게이트 ──────────────────────────────────────
public static Action<decimal>? OnBuy { get; set; }
public static Action<decimal>? OnSell { get; set; }
public static Action<decimal>? OnShort { get; set; }
public static Action<decimal>? OnCover { get; set; }
// ── 결과 수집 ────────────────────────────────────────────
/// <summary>발생한 모든 신호를 시간 순으로 누적합니다.</summary>
public static List<TradeSignalEvent> Results { get; } = new();
/// <summary>Results를 초기화합니다. (재실행 시 사용)</summary>
public static void ClearResults() => Results.Clear();
// ── text → SignalType 매핑 ───────────────────────────────
private static readonly Dictionary<string, SignalType> TextMap =
new(StringComparer.OrdinalIgnoreCase)
{
{ "Long", SignalType.Buy },
{ "Long Sell", SignalType.Sell },
{ "Short", SignalType.Short },
{ "Short Sell", SignalType.Cover },
};
/// <summary>
/// plotshape의 text 값으로 신호를 감지합니다.
/// 감지된 신호는 Results에 기록되고 등록된 델리게이트를 호출합니다.
/// </summary>
public static void DetectAndRaise(string text, decimal price, DateTimeOffset time = default)
{
string key = text.Trim();
if (!TextMap.TryGetValue(key, out SignalType signal)) return;
// ── 결과 수집 ──
var evt = new TradeSignalEvent
{
Time = time == default ? DateTimeOffset.Now : time,
Signal = signal,
Price = price,
};
Results.Add(evt);
Log.Information("[Signal] {Signal,-10} | {Time:yyyy-MM-dd HH:mm} | {Price:F4}",
signal, evt.Time, price);
// ── 델리게이트 호출 ──
switch (signal)
{
case SignalType.Buy:
OnBuy?.Invoke(price);
break;
case SignalType.Sell:
OnSell?.Invoke(price);
break;
case SignalType.Short:
OnShort?.Invoke(price);
break;
case SignalType.Cover:
OnCover?.Invoke(price);
break;
}
}
public static bool IsSignal(string text) =>
TextMap.ContainsKey(text.Trim());
}
}