105 lines
4.1 KiB
C#
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());
|
|
}
|
|
}
|