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}") = "Webhook", "Webhook\Webhook.csproj", "{4b67b9be-4382-4e2b-ad2d-372da3d81a9a}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4b67b9be-4382-4e2b-ad2d-372da3d81a9a}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4b67b9be-4382-4e2b-ad2d-372da3d81a9a}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4b67b9be-4382-4e2b-ad2d-372da3d81a9a}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4b67b9be-4382-4e2b-ad2d-372da3d81a9a}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
Webhook setup:
|
||||
|
||||
1. cmd as admin:
|
||||
> netsh http add urlacl url=http://+:80/ctrader user=RAKI\Brummel
|
||||
|
||||
2. Windows Defender Firewall, Outgoing Rules:
|
||||
New rule, Ports-->TCP-->local port = 80, remote ports = all
|
||||
|
||||
3. FritzBox:
|
||||
New "MyFritz-Freigabe", HTTP-Server, Deviceport 80, external port 80, Folder "ctrader"
|
||||
|
||||
Result:
|
||||
Webhook in Tradingview: http://raki.1k9zmiax0jpc2iys.myfritz.net:80/ctrader
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Resources;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
using cAlgo.API;
|
||||
using cAlgo.API.Collections;
|
||||
using cAlgo.API.Indicators;
|
||||
using cAlgo.API.Internals;
|
||||
|
||||
namespace cAlgo.Plugins;
|
||||
|
||||
[Plugin(AccessRights = AccessRights.FullAccess)]
|
||||
public class Webhook : Plugin
|
||||
{
|
||||
private HttpListener listener;
|
||||
private string lastMsg = "";
|
||||
private bool msgProcessed = false;
|
||||
private readonly CancellationTokenSource cancellationTokenSource = new();
|
||||
private Task task;
|
||||
private TradeInfo tradeInfo;
|
||||
private Symbol symbol;
|
||||
private readonly List<Chart> charts = new();
|
||||
|
||||
private class TradeInfo
|
||||
{
|
||||
public string Symbol { get; set; }
|
||||
public double StopLoss { get; set; }
|
||||
public int Risk { get; set; }
|
||||
public double Buy { get; set; }
|
||||
public double Sell { get; set; }
|
||||
public double Open { get; set; }
|
||||
public double Low { get; set; }
|
||||
public double High { get; set; }
|
||||
public double Close { get; set; }
|
||||
public DateTime ReceiveTime { get; set; }
|
||||
public List<Chart> Charts { get; set; }
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
listener = new HttpListener();
|
||||
//listener.Prefixes.Add("http://localhost:3000/");
|
||||
listener.Prefixes.Add("http://+:80/ctrader/");
|
||||
listener.Start();
|
||||
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
|
||||
task = Task.Run(() =>
|
||||
{
|
||||
while( !cancellationToken.IsCancellationRequested )
|
||||
{
|
||||
// Wait for a request to come in
|
||||
var context = listener.GetContext();
|
||||
var request = context.Request;
|
||||
|
||||
// Read the request body
|
||||
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
|
||||
{
|
||||
string m = reader.ReadToEnd();
|
||||
|
||||
Monitor.Enter(this);
|
||||
try
|
||||
{
|
||||
if( m != lastMsg )
|
||||
{
|
||||
lastMsg = m;
|
||||
msgProcessed = false;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a response
|
||||
var response = context.Response;
|
||||
response.StatusCode = 200;
|
||||
response.ContentType = "text/plain";
|
||||
var buffer = Encoding.UTF8.GetBytes("OK - cTrader");
|
||||
response.ContentLength64 = buffer.Length;
|
||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
||||
response.OutputStream.Close();
|
||||
}
|
||||
}, cancellationToken);
|
||||
|
||||
Timer.Start(TimeSpan.FromSeconds(1));
|
||||
Positions.Opened += (a) => { UpdateStops(); };
|
||||
}
|
||||
|
||||
protected override void OnTimer()
|
||||
{
|
||||
ProcessMsg();
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
Timer.Stop();
|
||||
cancellationTokenSource.Cancel();
|
||||
task.Wait();
|
||||
listener.Stop();
|
||||
}
|
||||
|
||||
protected bool UpdateTradeInfo()
|
||||
{
|
||||
string m = "";
|
||||
|
||||
Monitor.Enter(this);
|
||||
try
|
||||
{
|
||||
if( !msgProcessed )
|
||||
{
|
||||
msgProcessed = true;
|
||||
m = lastMsg;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Monitor.Exit(this);
|
||||
}
|
||||
|
||||
if( m== "" )
|
||||
return false;
|
||||
|
||||
Print( "Received: {0}", m );
|
||||
tradeInfo = JsonSerializer.Deserialize<TradeInfo>(m);
|
||||
tradeInfo.ReceiveTime = DateTime.Now;
|
||||
symbol = Symbols.GetSymbol(tradeInfo.Symbol);
|
||||
if( symbol == null )
|
||||
{
|
||||
Print( "Error: unknown symbol {0}", tradeInfo.Symbol );
|
||||
return false;
|
||||
}
|
||||
|
||||
tradeInfo.StopLoss = Math.Round( tradeInfo.StopLoss, symbol.Digits );
|
||||
tradeInfo.Buy = Math.Round( tradeInfo.Buy, symbol.Digits );
|
||||
tradeInfo.Sell = Math.Round( tradeInfo.Sell, symbol.Digits );
|
||||
|
||||
charts.Clear();
|
||||
foreach (var frame in ChartManager)
|
||||
if (frame is ChartFrame chartFrame)
|
||||
if( chartFrame.Chart.Symbol == symbol )
|
||||
charts.Add( chartFrame.Chart );
|
||||
|
||||
foreach (var chart in charts )
|
||||
chart.DrawStaticText("Message", tradeInfo.ReceiveTime.ToString("HH:mm:ss")+ ": "+m, VerticalAlignment.Top, HorizontalAlignment.Right, chart.ColorSettings.ForegroundColor);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void ProcessMsg()
|
||||
{
|
||||
if( UpdateTradeInfo() )
|
||||
{
|
||||
foreach (var chart in charts )
|
||||
{
|
||||
chart.DrawHorizontalLine( "SL", tradeInfo.StopLoss, Color.White );
|
||||
if( tradeInfo.Buy > 0 )
|
||||
chart.DrawIcon("Buy" + chart.Bars.Count, ChartIconType.UpArrow, chart.Bars.Count - 1, chart.Bars.ClosePrices[chart.Bars.Count - 1], Color.Green);
|
||||
if( tradeInfo.Sell > 0 )
|
||||
chart.DrawIcon("Sell" + chart.Bars.Count, ChartIconType.DownArrow, chart.Bars.Count - 1, chart.Bars.ClosePrices[chart.Bars.Count - 1], Color.Red);
|
||||
}
|
||||
|
||||
ManageOrders();
|
||||
UpdateStops();
|
||||
}
|
||||
}
|
||||
|
||||
private PendingOrder pendingOrder;
|
||||
|
||||
private double CalculateVolume(double StopLoss)
|
||||
{
|
||||
var maxAmountRisked = Account.Equity * ((double)tradeInfo.Risk / 100);
|
||||
return symbol.NormalizeVolumeInUnits(maxAmountRisked / (StopLoss/symbol.PipSize * symbol.PipValue), RoundingMode.Down);
|
||||
|
||||
}
|
||||
|
||||
protected void ManageOrders()
|
||||
{
|
||||
return;
|
||||
|
||||
if( Positions.Count > 0 || tradeInfo.Symbol != "GER40" )
|
||||
return;
|
||||
|
||||
if( tradeInfo.Buy > 0 )
|
||||
{
|
||||
double buyPrice = Math.Round( tradeInfo.Buy+symbol.Spread, symbol.Digits );
|
||||
double stopLossPrice = Math.Round( tradeInfo.StopLoss-symbol.Spread, symbol.Digits );
|
||||
|
||||
if( pendingOrder != null )
|
||||
if( pendingOrder.TradeType != TradeType.Buy || pendingOrder.TargetPrice != buyPrice )
|
||||
{
|
||||
pendingOrder.Cancel();
|
||||
pendingOrder = null;
|
||||
}
|
||||
|
||||
if( pendingOrder == null )
|
||||
{
|
||||
TradeResult tradeResult = PlaceStopOrder(
|
||||
TradeType.Buy, tradeInfo.Symbol,
|
||||
2, // CalculateVolume( buyPrice-stopLossPrice ),
|
||||
buyPrice, "Managed long",
|
||||
(buyPrice-stopLossPrice)/symbol.PipSize, null);
|
||||
|
||||
if( tradeResult.IsSuccessful )
|
||||
pendingOrder = tradeResult.PendingOrder;
|
||||
}
|
||||
}
|
||||
else if( tradeInfo.Sell > 0 )
|
||||
{
|
||||
double sellPrice = Math.Round( tradeInfo.Sell-symbol.Spread, symbol.Digits );
|
||||
double stopLossPrice = Math.Round( tradeInfo.StopLoss+symbol.Spread, symbol.Digits );
|
||||
|
||||
if( pendingOrder != null )
|
||||
if( pendingOrder.TradeType != TradeType.Sell || pendingOrder.TargetPrice != sellPrice )
|
||||
{
|
||||
pendingOrder.Cancel();
|
||||
pendingOrder = null;
|
||||
}
|
||||
|
||||
if( pendingOrder == null )
|
||||
{
|
||||
TradeResult tradeResult = PlaceStopOrder(
|
||||
TradeType.Sell, tradeInfo.Symbol,
|
||||
2, // CalculateVolume( stopLossPrice-sellPrice ),
|
||||
sellPrice, "Managed short",
|
||||
(stopLossPrice-sellPrice)/symbol.PipSize, null);
|
||||
|
||||
if( tradeResult.IsSuccessful )
|
||||
pendingOrder = tradeResult.PendingOrder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void UpdateStops()
|
||||
{
|
||||
foreach (var position in Positions)
|
||||
{
|
||||
if (position.Symbol != symbol)
|
||||
continue;
|
||||
|
||||
if( position.TradeType == TradeType.Buy )
|
||||
{
|
||||
double sl = Math.Round( tradeInfo.StopLoss-symbol.Spread, symbol.Digits );
|
||||
|
||||
if( !position.StopLoss.HasValue || position.StopLoss < sl )
|
||||
ModifyPosition(position, sl, position.TakeProfit);
|
||||
/*
|
||||
if( tradeInfo.Close < sl )
|
||||
{
|
||||
position.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
double be = Math.Round( position.EntryPrice+symbol.Spread, symbol.Digits );
|
||||
double sl_be = Math.Min( sl, be );
|
||||
|
||||
if( !position.StopLoss.HasValue || position.StopLoss < sl_be )
|
||||
ModifyPosition(position, sl_be, position.TakeProfit);
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
double sl = Math.Round( tradeInfo.StopLoss+symbol.Spread, symbol.Digits );
|
||||
|
||||
if( !position.StopLoss.HasValue || position.StopLoss > sl )
|
||||
ModifyPosition(position, sl, position.TakeProfit);
|
||||
/*
|
||||
if( tradeInfo.Close > sl )
|
||||
{
|
||||
position.Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
double be = Math.Round( position.EntryPrice-symbol.Spread, symbol.Digits );
|
||||
double sl_be = Math.Max( sl, be );
|
||||
|
||||
if( !position.StopLoss.HasValue || position.StopLoss > sl_be )
|
||||
ModifyPosition(position, sl_be, position.TakeProfit);
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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