August 10, 2020
We will answer the Question:
How far did the trade move away from breakeven (in the negative direction) before either getting stopped out or back on to take profit?
This can help us answer if we should set a maximum stop loss level. If the trade moves in x pips against us, then we know it has a high probability of being a loser and we should just get out early.
The free online tool at https://www.myfxbook.com collects the data, so we can export it and analyze it here. We can export to csv, remove the last "open trades" block of data, and save the file so we get a clean import into a pandas dataframe.
Note this type of analysis probably works best on an automated system that sets a well defined stop loss level.
# define all imports needed
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style='darkgrid')
import warnings
warnings.filterwarnings("ignore")
# define all of the functions we will use for this analysis
def calculateStats(df, pipLossListToStudy):
"""
Iterate through pipLossListToStudy to generate hypothetical results of different maximum stop loss levels
# list of hypothetical wins
# list of hypothetical losses
# total aggregate after each number
"""
verbose=False
totalpipsbefore = df['Pips'].sum()
pipswonbefore = df[df['Pips'] > 0]['Pips'].sum()
pipslostbefore = df[df['Pips'] < 0]['Pips'].sum()
numtrades = len(df)
if verbose:
print('Number of trades: ', numtrades)
print('Total pips before: ', totalpipsbefore)
print('Pips won before: ', pipswonbefore)
print('Pips lost before: ', pipslostbefore, '\n')
numwins=len(df[df['Pips'] > 0])
numlosses = len(df[df['Pips'] < 0])
simpips = []
simpipswonlist = []
simpipslostlist = []
for pipLossToStudy in pipLossListToStudy:
deadwins = len(df[df['Pips'] > 0][df['Min(pips)'] <= pipLossToStudy])
deadwinspips = df[df['Pips'] > 0][df['Min(pips)'] <= pipLossToStudy]['Pips'].sum()
cutlosses = len(df[df['Pips'] < 0][df['Min(pips)'] <= pipLossToStudy])
cutlossespips = df[df['Pips'] < 0][df['Min(pips)'] <= pipLossToStudy]['Pips'].sum()
simwins2losses=deadwins*pipLossToStudy
simlossescutshort=cutlosses*pipLossToStudy
simpipswon=pipswonbefore-deadwinspips
simpipslost=pipslostbefore-cutlossespips+simwins2losses+simlossescutshort
simtotalpips=simpipswon+simpipslost
simpips.append(simtotalpips)
simpipswonlist.append(simpipswon)
simpipslostlist.append(simpipslost)
if verbose:
print('\nTEST PIP LOSS STOP: ', pipLossToStudy)
print('Number of wins that would have been losses: ', deadwins, 'or ', round(deadwins/numwins,3)*100, '% of total wins')
print('Number of pips won that would have been losses: ', deadwinspips)
print('Number of losses that would have cut short: ', cutlosses, 'or ', round(cutlosses/numlosses,3)*100,'% of total losses')
print('Number of pips lost that would have been cut short: ', cutlossespips)
print('Wins would have been losses in pips: ', simwins2losses)
print('Losses would have reduced to this in pips: ', simlossescutshort)
print('Sim total pips won: ', simpipswon)
print('Sim total pips lost: ', simpipslost)
print('Total pips after making the change: ', simtotalpips)
return simpips, simpipswonlist, simpipslostlist
def plotMAE(df, pipLossToStudy):
"""
Generate a basic plot of MAE results split by wins and losses - similar to that on myfxbook MAE page
"""
# generate the standard MAE chart in pips
fig = plt.figure(figsize=(14, 6))
ax3 = fig.add_subplot(1,1,1)
ax3.scatter(x=df[df['Pips']>0]['Pips'], y=df[df['Pips']>0]['Min(pips)'],
color='green', label='Winning Trades')
ax3.scatter(x=df[df['Pips']<0]['Pips'], y=df[df['Pips']<0]['Min(pips)'],
color='red', label='Losing Trades')
ax3.set_xlabel('Pips')
ax3.set_ylabel('Min(pips) MAE')
ax3.axhline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax3.set_title('MAE vs. Trade Outcome (pips)')
ax3.legend()
fig.show()
# generate separate histograms of wins and losses
fig = plt.figure(figsize=(14, 6))
ax2 = fig.add_subplot(1, 2, 1)
ax2.hist(x = df[df['Pips'] < 0]['Min(pips)'], bins=25, color='red')
ax2.set_title('MAE Distribution for Losing Trades')
ax2.set_xlabel('Min(pips) - MAE')
ax2.set_ylabel('Number of Trades')
ax2.axvline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax2.legend()
ax1 = fig.add_subplot(1, 2, 2)
ax1.hist(df[df['Pips'] > 0]['Min(pips)'], bins=25,
color='green')
ax1.set_title('MAE Distribution for Winning Trades')
ax1.set_xlabel('Min(pips) - MAE')
ax1.set_ylabel('Number of Trades')
ax1.axvline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax1.legend()
fig.show()
return
def plotMFE(df, pipProfitToStudy):
"""
Generate a basic plot of MAE results split by wins and losses - similar to that on myfxbook MAE page
"""
# generate the standard MAE chart in pips
fig = plt.figure(figsize=(14, 6))
ax3 = fig.add_subplot(1,1,1)
ax3.scatter(x=df[df['Pips']>0]['Pips'], y=df[df['Pips']>0]['Max(pips)'],
color='green', label='Winning Trades')
ax3.scatter(x=df[df['Pips']<0]['Pips'], y=df[df['Pips']<0]['Max(pips)'],
color='red', label='Losing Trades')
ax3.set_xlabel('Pips')
ax3.set_ylabel('Max(pips) MFE')
ax3.axhline(pipProfitToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax3.set_title('MFE vs. Trade Outcome (pips)')
ax3.legend()
fig.show()
fig.show()
return
def plotPipChanges(pipLossListToStudy, pipLossToStudy, simpips, simpipswonlist, simpipslostlist):
"""
Generate a plot of aggregate total system results, win results, and loss results, after simulating
what would happen at each value in pipLossListToStudy
"""
fig = plt.figure(figsize=(14, 6))
ax3 = fig.add_subplot(1,3,1)
ax3.plot(pipLossListToStudy, simpips)
ax3.set_title('Total Pips vs. Stop Loss (MAE) Levels')
ax3.set_xlabel('Proposed Min(pips) - MAE')
ax3.set_ylabel('Total Pips Gained/Lost')
ax3.axvline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax1 = fig.add_subplot(1,3,2)
ax1.plot(pipLossListToStudy, simpipswonlist, color='green')
ax1.set_title('Total Pips Won vs. Stop Loss (MAE) Levels')
ax1.set_xlabel('Proposed Min(pips) - MAE')
ax1.set_ylabel('Total Pips Gained/Lost')
ax1.axvline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
ax2 = fig.add_subplot(1,3,3)
ax2.plot(pipLossListToStudy, simpipslostlist, color='red')
ax2.set_title('Total Pips Lost vs. Stop Loss (MAE) Levels')
ax2.set_xlabel('Proposed Min(pips) - MAE')
ax2.set_ylabel('Total Pips Gained/Lost')
ax2.axvline(pipLossToStudy,
color='blue', label='Proposed Max Stop', linestyle='--')
fig.show()
return
def plotByDate(df, pipLossToStudy):
"""
Plot trade results across time, colored by whether they would have been stopped,
or not, at the target pipLossToStudy
"""
fig = plt.figure(figsize=(14, 6))
ax = fig.add_subplot(1, 1, 1)
ax.scatter(pd.to_datetime(df[df['Min(pips)'] < pipLossToStudy]['Open Date']),
df[df['Min(pips)'] < pipLossToStudy]['Pips'], color='orange', label='Trades Stopped Early')
ax.scatter(pd.to_datetime(df[df['Min(pips)'] > pipLossToStudy]['Open Date']),
df[df['Min(pips)'] > pipLossToStudy]['Pips'], color='blue', label='Trades Not Changed')
ax.set_title('Date of Trades - When did trades that would have been losses happen?\nIs there a particular market environment that causes this phenomena for this system?')
ax.set_xlabel('Open Date of Trade')
ax.set_ylabel('Trade Result in Pips')
ax.axhline(pipLossToStudy,
color='red', label='Proposed Max Stop', linestyle='--')
ax.axhline(0, color='black', label='0 Pips')
ax.legend()
fig.show()
return
Below we will generate some charts of our system, using the functions defined above:
# after exporting & creating a csv file from myfxbook, read it in and perform the analysis
df = pd.read_csv('../strategies/Test_8C_BabyBounce/myfxbook-export-7.20.20.csv')
# what is the target pipLossToStudy? this plots the 'breakpoint line' in the charts below
pipLossToStudy = -75
# what list of pips do you want to study? this will generate the simulation visualization charts
pipLossListToStudy = [-15, -20, -25, -30, -35, -40, -45, -50, -55, -60, -65, -70, -75, -80, -85, -90, -95, -100, -105,
-110, -115, -120, -125, -130, -135, -140]
# finally, run the functions we've created
simpips, simpipswonlist, simpipslostlist = calculateStats(df, pipLossListToStudy)
plotMAE(df, pipLossToStudy)
plotPipChanges(pipLossListToStudy, pipLossToStudy, simpips, simpipswonlist, simpipslostlist)
plotByDate(df, pipLossToStudy)
df = pd.read_csv('../strategies/Test_8C_BabyBounce/myfxbook-export-7.20.20.csv')
pipLossToStudy = -75
pipLossListToStudy = [-70, -71, -72, -73, -74, -75, -76, -77, -78, -79, -80]
simpips, simpipswonlist, simpipslostlist = calculateStats(df, pipLossListToStudy)
plotMAE(df, pipLossToStudy)
plotPipChanges(pipLossListToStudy, pipLossToStudy, simpips, simpipswonlist, simpipslostlist)
plotByDate(df, pipLossToStudy)
This analysis has been performed on a test system that executes over the aggregate of 28 major currency pairs. This analysis could be fine-tuned and made more useful by selecting specific currency pairs that work well on this system, and implementing specific stop loss levels for each of the pairs. The only purpose of trading the system on all 28 major fx pairs is to collect statistics for analysis.