Initial commit
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user