calculate highest price from certain time
calculate highest price from certain time
30 Jul 2022, 11:26
hello, is there a way to efficiently "find" the highest/lowest price based on a time window?
what I do want to accomplish is to measure the actual drawdown a certain position has made,
so the times would be entry and exit
the idea is simply to get the distance from entry to max drawdown the position has created, as this will lead to the "actual" exposure created by the position itself and will as a result provide better information about actual performance, which can be later then compared to where the initial SL has ben in order to analyse risk performance.
i tried to get this data via history but that does slow things down dramatically, so I think the best way of doing this would be to make the math when the position is being closed via OnPosition event, anyone have an idea how this can be accomplished?
paolo.panicali
30 Jul 2022, 19:56 ( Updated at: 21 Dec 2023, 09:22 )
Drawdowns and RunUps of each trade in Ctrader and results saved to csv file in order to make further investigations on Python
Hi I just made this, the code has to be carefully checked but it seems to work fine.
The bot buys randomly, Do not use this in live trading, remember to switch to a demo account when testing.
I found out that the Run ups distribution is normal while the drawdown distribution is not. Somebody might tell us if I made a mistake or it s ok.
The robot write on a CSV file with the data of the trades, it needs AccessRights = AccessRights.FileSystem if you do not want Ctrader to have access to your computer to save CSV file, you can remove the filewriter code and keep AccessRights = AccessRights.None
// Run Up and Draw Downs save to csv by paolo panicali july 2022
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
public class DDRUCtrader : Robot
{
System.Random random = new System.Random();
IndicatorDataSeries MaxDD_Eta;
IndicatorDataSeries BarsInTradeSerie;
IndicatorDataSeries MaxDD_Value;
IndicatorDataSeries MaxRU_Eta;
IndicatorDataSeries LossBarsInTradeSerie;
IndicatorDataSeries MaxRU_Value;
int DD_index = 0, RU_index = 0, counter = 0;
double SL = 25, TP = 35;
string filename;
StreamWriter _fileWriter;
protected override void OnStart()
{
Positions.Closed += PositionsOnClosed;
MaxDD_Eta = CreateDataSeries();
MaxDD_Value = CreateDataSeries();
BarsInTradeSerie = CreateDataSeries();
MaxRU_Eta = CreateDataSeries();
LossBarsInTradeSerie = CreateDataSeries();
MaxRU_Value = CreateDataSeries();
}
protected override void OnBar()
{
// prepare a csv file to store the results
if (counter == 0)
{
filename = Symbol.Name + "_Trades" + ".csv";
var filePath = Path.Combine("C:\\PATH", filename);
_fileWriter = File.AppendText(filePath);
_fileWriter.AutoFlush = true;
_fileWriter.WriteLine("DateTime" + ";" + "EntryPrice" + ";" + "Close" + ";" + "RunUp" + ";" + "DrawDown" + ";" + "index" + ";" + "OutCome");
counter = 1;
}
// BUY random hour from 1 to 20
int HourRnd = random.Next(1, 20);
if (HourRnd == Bars.OpenTimes.LastValue.Hour && Positions.Count() == 0)
{
ExecuteMarketOrder(TradeType.Buy, "EURUSD", 1000, "BUY RANDOM: " + Symbol.Name, SL, TP);
}
}
protected override void OnStop()
{
int TotalProfitTrades = MaxDD_Eta.Count();
MovingAverage DDETA_avg = Indicators.MovingAverage(MaxDD_Eta, TotalProfitTrades, MovingAverageType.Simple);
Print("Average Draw Down Bars ETA: " + DDETA_avg.Result[TotalProfitTrades - 1]);
MovingAverage DDValue_avg = Indicators.MovingAverage(MaxDD_Value, TotalProfitTrades, MovingAverageType.Simple);
Print("Average Draw Down in Pips : " + DDValue_avg.Result[TotalProfitTrades - 1]);
MovingAverage BarsInTrade_avg = Indicators.MovingAverage(BarsInTradeSerie, TotalProfitTrades, MovingAverageType.Simple);
Print("Average Bars in Trade : " + BarsInTrade_avg.Result[TotalProfitTrades - 1]);
///
int TotalLossTrades = MaxRU_Eta.Count();
MovingAverage RUETA_avg = Indicators.MovingAverage(MaxRU_Eta, TotalLossTrades, MovingAverageType.Simple);
Print("Average Run Up Bars ETA: " + RUETA_avg.Result[TotalLossTrades - 1]);
MovingAverage RUValue_avg = Indicators.MovingAverage(MaxRU_Value, TotalLossTrades, MovingAverageType.Simple);
Print("Average Run Up in Pips : " + RUValue_avg.Result[TotalLossTrades - 1]);
MovingAverage LossBarsInTrade_avg = Indicators.MovingAverage(LossBarsInTradeSerie, TotalLossTrades, MovingAverageType.Simple);
Print("Average Bars in Trade : " + LossBarsInTrade_avg.Result[TotalLossTrades - 1]);
Print("Stop Loss Pips :" + SL + " and Take profit Pips :" + TP);
// Close the CSV file with raw data on trades
_fileWriter.Close();
}
private void PositionsOnClosed(PositionClosedEventArgs args)
{
int EntryTime_position_index;
// find entry bar index
EntryTime_position_index = 0;
DateTime EntryTime_position = args.Position.EntryTime;
for (int i = 0; i < 1000; i++)
{
if (Bars.OpenTimes.Last(i) == EntryTime_position)
{
EntryTime_position_index = i + 1;
break;
}
}
// position properties
double EntryPrice_position = args.Position.EntryPrice;
double StopLoss_position = args.Position.StopLoss.Value;
double TakeProfit_position = args.Position.TakeProfit.Value;
double ClosingPrice_position = Bars.ClosePrices.LastValue;
//better calculating the closing price from the position profit
// drawdown data for positions went in profit
if (args.Position.NetProfit > 0)
{
Print("Position Closed In Profit :");
double Position_Min = Bars.LowPrices.Minimum(EntryTime_position_index);
//double Position_Max = Bars.HighPrices.Maximum(EntryTime_position_index);
double Position_DD = Math.Round((EntryPrice_position - Position_Min) / Symbol.PipSize);
//double Position_RU = Position_Max - EntryPrice_position;
// Draw Down Index
int MaxDD_position_index = 0;
for (int i = 0; i < 1000; i++)
{
if (Bars.LowPrices.Last(i) == Position_Min)
{
MaxDD_position_index = i;
break;
}
}
int MaxDD_ETA_index = EntryTime_position_index - MaxDD_position_index;
MaxDD_Eta[DD_index] = MaxDD_ETA_index;
MaxDD_Value[DD_index] = Position_DD;
BarsInTradeSerie[DD_index] = EntryTime_position_index;
Print("Bars in Trade : " + BarsInTradeSerie[DD_index] + " Max DD Bar index : " + MaxDD_Eta[DD_index] + " DD pips " + MaxDD_Value[DD_index]);
if (counter == 1)
{
_fileWriter.WriteLine(args.Position.EntryTime + ";" + args.Position.EntryPrice + ";" + Bars.ClosePrices.LastValue + ";" + " " + ";" + MaxDD_Value[DD_index] + ";" + MaxDD_Eta[DD_index] + ";" + "PROFIT");
}
DD_index = DD_index + 1;
}
// drawdown data for positions went in profit
if (args.Position.NetProfit < 0)
{
Print("Position Closed In LOSS :");
double Position_Max = Bars.HighPrices.Maximum(EntryTime_position_index);
double Position_RU = Math.Round((Position_Max - EntryPrice_position) / Symbol.PipSize);
// Draw Down Index
int MaxRU_position_index = 0;
for (int i = 0; i < 1000; i++)
{
if (Bars.HighPrices.Last(i) == Position_Max)
{
MaxRU_position_index = i;
break;
}
}
int MaxRU_ETA_index = EntryTime_position_index - MaxRU_position_index;
MaxRU_Eta[RU_index] = MaxRU_ETA_index;
MaxRU_Value[RU_index] = Position_RU;
LossBarsInTradeSerie[RU_index] = EntryTime_position_index;
if (counter == 1)
{
_fileWriter.WriteLine(args.Position.EntryTime + ";" + args.Position.EntryPrice + ";" + Bars.ClosePrices.LastValue + ";" + MaxRU_Value[RU_index] + ";" + " " + ";" + MaxRU_Eta[RU_index] + ";" + "LOSS");
}
Print("Bars in Trade : " + LossBarsInTradeSerie[RU_index] + " Max RU Bar index : " + MaxRU_Eta[RU_index] + " RU pips " + MaxRU_Value[RU_index]);
RU_index = RU_index + 1;
}
}
}
}
in order to import in python and see the distribution here is the code
# -*- coding: utf-8 -*-
"""
Created on Sat Jul 30 17:51:39 2022
@author: Paolo Panicali
"""
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
Symbol='EURUSD'
Trades=99999
DataPath='C:\\PATH\\'+Symbol +'_Trades.csv'
#carica dati dal file in df
path_load=DataPath
df = pd.read_csv(path_load,sep=";",decimal=",", engine='python')
df_profit=df[df['OutCome'] =='PROFIT']
df_loss=df[df['OutCome'] =='LOSS']
df_profit["DrawDown"] = pd.to_numeric(df_profit["DrawDown"])
df_loss["RunUp"] = pd.to_numeric(df_loss["RunUp"])
DDown = df_profit['DrawDown']
RUp=df_loss['RunUp']
## RUN THIS FOR DRAWDOWNS
plt.hist(DDown,alpha=0.5,bins=np.arange(0,40,1),color='blue', label='Draw Down')
plt.margins(x=0, y=0)
plt.gca().set(title=Symbol+' Draw Down Distribution', ylabel='Total Profit Trades: '+str(len(df_profit)));
plt.ylim(0,40)
plt.legend();
## RUND THIS FOR RUNUPS
plt.hist(RUp,alpha=0.5,bins=np.arange(0,40,1),color='red', label='Run Up')
plt.margins(x=0, y=0)
plt.gca().set(title=Symbol+' Run Up Distribution', ylabel='Total Loss Trades: '+str(len(df_profit)));
plt.ylim(0,40)
plt.legend();
Hope this will help, the code should be double checked and cleaned so please if you find any error or improvement to be made let me know.
Bye.
@paolo.panicali