Initial commit

This commit is contained in:
Michael Schimmel
2026-01-28 09:10:52 +01:00
commit a1d2e96f8a
513 changed files with 19503 additions and 0 deletions
@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImbalanceAlert", "ImbalanceAlert\ImbalanceAlert.csproj", "{c36a0d9f-f01d-43c6-8718-c23e761197a5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{c36a0d9f-f01d-43c6-8718-c23e761197a5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{c36a0d9f-f01d-43c6-8718-c23e761197a5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{c36a0d9f-f01d-43c6-8718-c23e761197a5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{c36a0d9f-f01d-43c6-8718-c23e761197a5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,170 @@
using System;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ImbalanceAlert : Indicator
{
#region Parameters
// Alert Settings
[Parameter("Send Email", DefaultValue = true, Group = "Alert Settings")]
public bool SendEmail { get; set; }
[Parameter("Sender Email", DefaultValue = "junk001@mycelium.de", Group = "Alert Settings")]
public string SenderEmail { get; set; }
[Parameter("Recipient Email", DefaultValue = "junk001@mycelium.de", Group = "Alert Settings")]
public string RecipientEmail { get; set; }
[Parameter("Play Sound", DefaultValue = true, Group = "Alert Settings")]
public bool PlaySound { get; set; }
[Parameter("Sound File Path", DefaultValue = "C:\\Windows\\Media\\Alarm07.wav", Group = "Alert Settings")]
public string SoundFilePath { get; set; }
// Filters
[Parameter("Min Gap Size (Pips)", DefaultValue = 1.0, Group = "Filters")]
public double MinGapSizePips { get; set; }
// Visuals
[Parameter("Draw Boxes", DefaultValue = true, Group = "Visuals")]
public bool DrawBoxes { get; set; }
[Parameter("Bullish Color", DefaultValue = "Green", Group = "Visuals")]
public string BullishColorName { get; set; }
[Parameter("Bearish Color", DefaultValue = "Red", Group = "Visuals")]
public string BearishColorName { get; set; }
#endregion
#region Fields
private int _lastAlertBarIndex = -1;
private Color _bullColor;
private Color _bearColor;
#endregion
protected override void Initialize()
{
// 50% Transparency (Alpha 127 out of 255)
_bullColor = Color.FromArgb(127, Color.FromName(BullishColorName));
_bearColor = Color.FromArgb(127, Color.FromName(BearishColorName));
}
public override void Calculate(int index)
{
// Need at least 3 completed bars (index-3 to index-1)
if (index < 3) return;
int cIndex = index - 1; // The closed bar confirming the gap
int aIndex = index - 3; // The origin bar
double highA = Bars.HighPrices[aIndex];
double lowA = Bars.LowPrices[aIndex];
double highC = Bars.HighPrices[cIndex];
double lowC = Bars.LowPrices[cIndex];
// Check Bullish FVG (Undervalued)
// Gap is between High of A and Low of C
if (lowC > highA)
{
// Bottom: HighA, Top: LowC
ProcessGap(true, aIndex, cIndex, highA, lowC, index);
}
// Check Bearish FVG (Overvalued)
// Gap is between Low of A and High of C
else if (highC < lowA)
{
// Bottom: HighC, Top: LowA (Bugfix: Parameter order swapped)
ProcessGap(false, aIndex, cIndex, highC, lowA, index);
}
}
private void ProcessGap(bool isBullish, int startBarIdx, int endBarIdx, double bottomPrice, double topPrice, int currentIndex)
{
double gapSize = topPrice - bottomPrice;
// Check against min size
if (gapSize < MinGapSizePips * Symbol.PipSize) return;
// Visualization
if (DrawBoxes)
{
string objName = $"FVG_{startBarIdx}";
var color = isBullish ? _bullColor : _bearColor;
// Draw rectangle
var rect = Chart.DrawRectangle(objName, startBarIdx, bottomPrice, endBarIdx, topPrice, color, 1, LineStyle.Solid);
rect.IsFilled = true;
}
// Alert Logic
if (IsLastBar && RunningMode == RunningMode.RealTime)
{
if (_lastAlertBarIndex != currentIndex)
{
TriggerAlerts(isBullish, topPrice, bottomPrice);
_lastAlertBarIndex = currentIndex;
}
}
}
private void TriggerAlerts(bool isBullish, double top, double bottom)
{
string type = isBullish ? "BULLISH" : "BEARISH";
string message = $"{Symbol.Name} ({TimeFrame}): {type} Imbalance Detected. Range: {bottom} - {top}";
// 1. Sound Alert
if (PlaySound)
{
if (!string.IsNullOrWhiteSpace(SoundFilePath))
{
try
{
Notifications.PlaySound(SoundFilePath);
}
catch
{
Notifications.PlaySound(SoundType.Doorbell);
}
}
}
// 2. Email Alert
if (SendEmail)
{
SendAlertEmail(type, top, bottom);
}
Print(message);
}
private void SendAlertEmail(string type, double top, double bottom)
{
try
{
string subject = $"{Symbol.Name} ({TimeFrame}): {type} Imbalance Detected";
string body = $@"
<h1>Imbalance Manifested</h1>
<p><strong>Symbol:</strong> {Symbol.Name}</p>
<p><strong>Timeframe:</strong> {TimeFrame}</p>
<p><strong>Direction:</strong> {type}</p>
<p><strong>Gap Range:</strong> {bottom} - {top}</p>
<p><strong>Time:</strong> {Server.Time.ToUniversalTime()} UTC</p>
";
Notifications.SendEmail(SenderEmail, RecipientEmail, subject, body);
}
catch (Exception ex)
{
Print($"Failed to send email: {ex.Message}");
}
}
}
}
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="cTrader.Automate" Version="*" />
</ItemGroup>
</Project>