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}") = "SmaHmaNotification", "SmaHmaNotification\SmaHmaNotification.csproj", "{7c1a7ee9-4442-4a60-b965-f5dae20e9381}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7c1a7ee9-4442-4a60-b965-f5dae20e9381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7c1a7ee9-4442-4a60-b965-f5dae20e9381}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7c1a7ee9-4442-4a60-b965-f5dae20e9381}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7c1a7ee9-4442-4a60-b965-f5dae20e9381}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,230 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
public enum TradingDirection
{
Both,
LongOnly,
ShortOnly
}
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public class SmaHmaNotification : Robot
{
#region Parameters
[Parameter("SMA Period", DefaultValue = 200, MinValue = 1)]
public int SmaPeriod { get; set; }
[Parameter("HMA Period", DefaultValue = 250, MinValue = 1)]
public int HmaPeriod { get; set; }
[Parameter("Trading Direction", DefaultValue = TradingDirection.Both)]
public TradingDirection AllowedDirection { get; set; }
[Parameter("Sound File Path", DefaultValue = @"C:\Windows\Media\Alarm05.wav")]
public string SoundFilePath { get; set; }
[Parameter("Telegram Bot Token", DefaultValue = "8569913524:AAE9RGsvkBPa0yhTFCKBjVeST0fuzdOx5w0")]
public string TelegramBotToken { get; set; }
[Parameter("Telegram Chat ID", DefaultValue = "5171721381")]
public string TelegramChatId { get; set; }
#endregion
#region Fields
private SimpleMovingAverage _sma;
private HullMovingAverage _hma;
private HttpClient _httpClient;
private bool _waitingForSmaRise;
private bool _waitingForSmaFall;
#endregion
protected override void OnStart()
{
_sma = Indicators.SimpleMovingAverage(Bars.ClosePrices, SmaPeriod);
_hma = Indicators.HullMovingAverage(Bars.ClosePrices, HmaPeriod);
_httpClient = new HttpClient();
// TriggerSignal("START", $"Bot started. Direction: {AllowedDirection}");
}
protected override void OnStop()
{
_httpClient?.Dispose();
}
protected override void OnBarClosed()
{
if (Bars.Count <= Math.Max(SmaPeriod, HmaPeriod) + 1)
{
return;
}
double smaClosed = _sma.Result.Last(0);
double smaPrev = _sma.Result.Last(1);
double hmaClosed = _hma.Result.Last(0);
double hmaPrev = _hma.Result.Last(1);
bool isSmaRising = smaClosed > smaPrev;
bool isSmaFalling = smaClosed < smaPrev;
bool isCrossUp = (hmaPrev <= smaPrev) && (hmaClosed > smaClosed);
bool isCrossDown = (hmaPrev >= smaPrev) && (hmaClosed < smaClosed);
bool isHmaAbove = hmaClosed > smaClosed;
bool isHmaBelow = hmaClosed < smaClosed;
if (AllowedDirection == TradingDirection.Both || AllowedDirection == TradingDirection.LongOnly)
{
if (isCrossUp)
{
_waitingForSmaFall = false;
if (isSmaRising)
{
TriggerSignal("BUY", "SMA Rising + HMA CrossUp (Immediate)");
_waitingForSmaRise = false;
}
else
{
_waitingForSmaRise = true;
}
}
else if (_waitingForSmaRise)
{
if (!isHmaAbove)
{
_waitingForSmaRise = false;
}
else if (isSmaRising)
{
TriggerSignal("BUY", "SMA Started Rising after HMA CrossUp (Delayed)");
_waitingForSmaRise = false;
}
}
}
else
{
_waitingForSmaRise = false;
}
if (AllowedDirection == TradingDirection.Both || AllowedDirection == TradingDirection.ShortOnly)
{
if (isCrossDown)
{
_waitingForSmaRise = false;
if (isSmaFalling)
{
TriggerSignal("SELL", "SMA Falling + HMA CrossDown (Immediate)");
_waitingForSmaFall = false;
}
else
{
_waitingForSmaFall = true;
}
}
else if (_waitingForSmaFall)
{
if (!isHmaBelow)
{
_waitingForSmaFall = false;
}
else if (isSmaFalling)
{
TriggerSignal("SELL", "SMA Started Falling after HMA CrossDown (Delayed)");
_waitingForSmaFall = false;
}
}
}
else
{
_waitingForSmaFall = false;
}
}
private void TriggerSignal(string type, string reason)
{
int index = Bars.Count - 1;
// Use converted Server Time to respect robot's TimeZone even in backtest.
string localTime = TimeZoneInfo.ConvertTimeFromUtc(TimeInUtc, TimeZone).ToString("yyyy-MM-dd HH:mm:ss");
if (type == "BUY")
{
Chart.DrawIcon("BuySignal_" + index, ChartIconType.UpArrow, index, Bars.LowPrices[index] - Symbol.PipSize * 5, Color.Green);
}
else if (type == "SELL")
{
Chart.DrawIcon("SellSignal_" + index, ChartIconType.DownArrow, index, Bars.HighPrices[index] + Symbol.PipSize * 5, Color.Red);
}
string logMessage = $"[{localTime}] {Symbol.Name} ({TimeFrame}) {type}: {reason}";
Print(logMessage);
// Suppress external notifications during backtesting.
if (!IsBacktesting)
{
PlaySoundAlert();
string emoji = (type == "BUY" ? "🟢" : (type == "SELL" ? "🔴" : ""));
string htmlMessage = $"{emoji} <b>{Symbol.Name}</b> ({TimeFrame})\n" +
$"<b>{type} Signal</b>\n" +
$"Reason: <i>{reason}</i>\n" +
$"Time: <code>{localTime}</code>";
Task.Run(() => SendTelegramMessageAsync(htmlMessage));
}
}
private void PlaySoundAlert()
{
try
{
if (!string.IsNullOrWhiteSpace(SoundFilePath))
{
Notifications.PlaySound(SoundFilePath);
}
}
catch (Exception ex)
{
Print("Error playing sound: {0}", ex.Message);
}
}
private async Task SendTelegramMessageAsync(string message)
{
if (string.IsNullOrWhiteSpace(TelegramBotToken) || string.IsNullOrWhiteSpace(TelegramChatId))
{
return;
}
try
{
string url = $"https://api.telegram.org/bot{TelegramBotToken}/sendMessage?chat_id={TelegramChatId}&text={Uri.EscapeDataString(message)}&parse_mode=HTML";
HttpResponseMessage response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
Print("Telegram Error: {0}", response.StatusCode);
}
}
catch (Exception ex)
{
Print("Exception sending Telegram message: {0}", 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>