img

How I Coded a Trading Bot for a 60% YoY Return — And You Can Too!

img
valuezone 04 May 2024

How I Coded a Trading Bot for a 60% YoY Return — And You Can Too!

The Strategy We Will Be Developing For


The Strategy

Code Implementation

1st Step: Download Data

import yfinance as yf
from datetime import datetime

symbol = "EURUSD=X"
start_date = "2023-01-01"
end_date = datetime.now().strftime('%Y-%m-%d') # Today's date in 'YYYY-MM-DD' format
interval = "1h"
data = yf.download(symbol, start=start_date, end=end_date, interval=interval)
data.to_csv('EURUSD_1H_01.01.2023-Today.csv')

2nd Step: Convert CSV Data To Pandas DataFrame

import pandas as pd
import pandas_ta as ta

# Load data from a CSV file
df = pd.read_csv("EURUSD_1H_01.01.2023-Today.csv")

df['Datetime'] = pd.to_datetime(df['Datetime'], utc=True)
df.set_index('Datetime', inplace=True)

# Remove any rows where the High and Low are the same (no price change)
df = df[df['High'] != df['Low']]

3rd Step: Calculate & Add Indicators To DataFrame

df["EMA"]=ta.ema(df.Close, length=30)
df['RSI']=ta.rsi(df.Close, length=10)
my_bbands = ta.bbands(df.Close, length=15, std=1.5)
df['ATR']=ta.atr(df.High, df.Low, df.Close, length=7)
df=df.join(my_bbands)
df # Check out the output 👇

4th Step: Define the EMA Signal

def ema_signal(df, current_candle, backcandles):
df_slice = df.reset_index().copy()

df_slice = df_slice.loc[current_candle-backcandles:current_candle, ["Open", "Close", "EMA"]]
dnt = 0 if (df_slice[["Open", "Close"]].max(axis=1) >= df_slice["EMA"]).any() else 1
upt = 0 if (df_slice[["Open", "Close"]].min(axis=1) <= df_slice["EMA"]).any() else 1

if upt==1 and dnt==1:
return 3
elif upt==1:
return 2
elif dnt==1:
return 1
else:
return 0

from tqdm import tqdm
tqdm.pandas()
df.reset_index(inplace=True)

5th Step: Define The Total Signal Based On The EMA Signal

from pandas import DataFrame

def total_signal(df: DataFrame, current_candle, backcandles):
if (ema_signal(df, current_candle, backcandles)==2
and df.Close[current_candle]<=df['BBL_15_1.5'][current_candle]
#and df.RSI[current_candle]<60
):
return 2
if (ema_signal(df, current_candle, backcandles)==1
and df.Close[current_candle]>=df['BBU_15_1.5'][current_candle]
#and df.RSI[current_candle]>40
):

return 1
return 0

# To ensure teh DataFrame wasn't modified or being sliced before the progress apply
df = df.copy()

df['TotalSignal'] = df.progress_apply(lambda row: total_signal(df, row.name, 7), axis=1)

df[df.TotalSignal != 0].head(10)
import plotly.graph_objects as go

st=100
dfpl = df[st:st+350]
#dfpl.reset_index(inplace=True)
fig = go.Figure(data=[go.Candlestick(x=dfpl.index,
open=dfpl['Open'],
high=dfpl['High'],
low=dfpl['Low'],
close=dfpl['Close']),

go.Scatter(x=dfpl.index, y=dfpl['BBL_15_1.5'],
line=dict(color='green', width=1),
name="BBL"),
go.Scatter(x=dfpl.index, y=dfpl['BBU_15_1.5'],
line=dict(color='green', width=1),
name="BBU"),
go.Scatter(x=dfpl.index, y=dfpl['EMA'],
line=dict(color='black', width=1),
name="EMA") ])

fig.show()

7th Step: Finally, Lets Define Our Strategy & Backtest

def TOTAL_SIGNAL():
return df.TotalSignal
from backtesting import Strategy
from backtesting import Backtest

class MyStrat(Strategy):
mysize = 0.99
slcoef = 1.2 #1.3
TPSLRatio = 2 # 1.8
def init(self):
super().init()
self.signal1 = self.I(TOTAL_SIGNAL)

def next(self):
super().next()
slatr = self.slcoef*self.data.ATR[-1]
TPSLRatio = self.TPSLRatio

if len(self.trades)>0:
if self.trades[-1].is_long and self.data.RSI[-1]>=90:
self.trades[-1].close()
elif self.trades[-1].is_short and self.data.RSI[-1]<=10:
self.trades[-1].close()

if self.signal1==2 and len(self.trades)==0:
sl1 = self.data.Close[-1] - slatr
tp1 = self.data.Close[-1] + slatr*TPSLRatio
self.buy(sl=sl1, tp=tp1, size=self.mysize)

elif self.signal1==1 and len(self.trades)==0:
sl1 = self.data.Close[-1] + slatr
tp1 = self.data.Close[-1] - slatr*TPSLRatio
self.sell(sl=sl1, tp=tp1, size=self.mysize)

bt = Backtest(df, MyStrat, cash=250, margin=1/30, commission=0.00)
stats, heatmap = bt.optimize(slcoef=[i/10 for i in range(10, 21)],
TPSLRatio=[i/10 for i in range(10, 21)],
maximize='Return [%]', max_tries=300,
random_state=0,
return_heatmap=True)

# To see the generated statistics
stats

# To check the strategy results
stats["_strategy"]

# This will run your backtest
# & give you a plot, where each trade was placed in time
bt.run()
bt.plot()


Boom 💥

Advice On Backtesting & Coefficient Choice

import seaborn as sns
import matplotlib.pyplot as plt

# Convert multiindex series to dataframe
heatmap_df = heatmap.unstack()
plt.figure(figsize=(10, 8))
sns.heatmap(heatmap_df, annot=True, cmap='viridis', fmt='.0f')
plt.show()