Skip to content

Instantly share code, notes, and snippets.

@AliAlhajji
Last active August 26, 2022 15:57
Show Gist options
  • Save AliAlhajji/2640a634ec40d9c9870f2b4e9fd5aca2 to your computer and use it in GitHub Desktop.
Save AliAlhajji/2640a634ec40d9c9870f2b4e9fd5aca2 to your computer and use it in GitHub Desktop.
import pandas
from ta import trend
import investpy
from columnar import columnar
#Get the historical data from investing.com
def get_stock_data(country:str, stock:str, from_date:str, to_date:str):
data = investpy.get_stock_historical_data(
country=country,
stock=stock,
from_date=from_date,
to_date=to_date
)
return data
#Attach the EMA column to the data.
def add_ema(data: pandas.Series, values:pandas.Series, ema_window: int, column:str):
ema = trend.ema_indicator(values, window=ema_window, fillna=False)
data[column] = ema
#Attach the SMA column to the data.
def add_sma(data: pandas.Series, values:pandas.Series, window: int, column:str):
sma = trend.sma_indicator(values, window=window, fillna=False)
data[column] = sma
def percent(price1:float, price2:float):
a = 100 * (price2 - price1) / price1
return a
def add_adx(data, column:str, window=14):
adx = trend.ADXIndicator(data['High'], data['Low'], data['Close'], window=window, fillna=True).adx()
data[column] = adx
def format_date(date: pandas.Timestamp):
return "{}-{}-{}".format(date.day, date.month, date.year)
def print_table(headers, data):
table = columnar(data, headers, justify="c", no_borders=False, column_sep=" ")
print(table)
import argparse
from datetime import datetime, timedelta
from common_methods import *
parser = argparse.ArgumentParser()
#The market's country (investing.com)
parser.add_argument("--country", type=str, required=True)
#Stock ticker (investing.com)
parser.add_argument("--stock", type=str, required=True)
#date format: dd/mm/yyyy
parser.add_argument("--from_date", type=str, required=True)
#date format: dd/mm/yyyy
parser.add_argument("--to_date", type=str, required=True)
#Small EMA used by strategy
parser.add_argument("--small_ema", type=int, required=False, default=10)
#Big EMA used by strategy
parser.add_argument("--big_ema", type=int, required=False, default=20)
#Stop loss (%)
parser.add_argument("--stop_loss", type=int, required=False, default=100)
#Target (%)
parser.add_argument("--target", type=int, required=False, default=500)
#Lowest distance the price can go down away from highest price since entry (%)
parser.add_argument("--gain_stop_loss", type=int, required=False, default=100)
args = parser.parse_args()
#Main fun happens here
def start_app():
from_date = datetime.strptime(args.from_date, "%d/%m/%Y")
delta_time = from_date - timedelta(days=args.big_ema+2)
data = get_stock_data(args.country, args.stock, delta_time.strftime("%d/%m/%Y"), args.to_date)
small_ema_column = "EMA{}".format(args.small_ema)
big_ema_column = "EMA{}".format(args.big_ema)
add_ema(data, data['Close'], args.small_ema, small_ema_column)
add_ema(data, data['Close'], args.big_ema, big_ema_column)
#in_trade is set to True when an entry point is found. False when there is not entry yet or when exit was found.
in_trade = False
#Number of success trades
hit = 0
#Number of failed trades
miss = 0
#Total number of possible trades
total_trades = 0
#Arbitrary budget to start with
start_budget = 100000
#Hit trade is set to True when a trade hits the target specified by user. False when the target has not been hit yet.
hit_target = False
#The actual accumlative gain in all the possible trades.
gain = start_budget
#The max accumlative gain that is possible if we exit at the highest price in all trades. This is used to evaluate how good the actual gain is.
max_gain = start_budget
table_headers = ["Exit reason", " Entry ", " Entry Date ", " Target "," Exit ", " Exit Date ", " P&L ", " Days ", " Highest ", " Max P&L " ]
trades = []
print("Stock: {}".format(args.stock))
print("FROM {} TO {}".format(args.from_date, args.to_date))
print("")
print("Possible deals:")
print("")
for i, row in data.iterrows():
small_ema = row[small_ema_column]
big_ema = row[big_ema_column]
#If currently not in trade, and an entry point was found:
if not in_trade and small_ema > big_ema and row['Close'] > small_ema:
#Set in_trade to true until exit.
in_trade = True
#The total days passed since entry
days_in_trade = 1
entry_date = format_date(i)
#Assume that the entry price is the close price of the day in which the entry point was found
entry = row['Close']
#The highest price achieved since entry. The highest of the entry day is the first high since trade.
highest = row['High']
#Calculate the target price of the trade. This price will be used as stop-loss for gains if the price goes higher than target and then declines.
target = entry * (1 + (args.target / 100))
#Calculate the stop loss price in case the price goes lower than entry price
stop_loss = entry * (1 - (args.stop_loss / 100))
#If we are in trade already:
elif in_trade:
days_in_trade = days_in_trade + 1
#If a new high is found, make it the highest since trade.
if row['High'] > highest:
highest = row['High']
#If today's close hits the trade minimum target, mark the target as "hit".
if highest >= target:
hit_target = True
#The following IF block is True when an exit point is found.
#Each condition here is an exit signal. At least one of the followins must be True:
#[1] Today close is lower than a certain EMA
#[2] Today close is X% lower than the highest price since entry
#[3] Today close is lower than stop loss
#[4] The target has been met but today close is lower than the target.
if row['Close'] < big_ema or percent(highest, row['Close']) < -1*args.gain_stop_loss or row['Close'] <= stop_loss or (hit_target and row['Close'] <= target):
exit_date = format_date(i)
total_trades = total_trades + 1
#Here we make assumptions on the exit price
#In reality, however, exit price can be higher or lower by some margin.
if row['Close'] <= stop_loss:
exit = stop_loss
exit_reason = "Stop loss"
elif hit_target and row['Close'] <= target:
exit = target
exit_reason = "Back to target"
elif percent(highest, row['Close']) < -1 * args.gain_stop_loss:
exit = highest * (1 - (args.gain_stop_loss / 100))
exit_reason = "Far from top"
elif row['Close'] < big_ema:
exit = row['Close']
exit_reason = "Close below EMA"
#P&L (%)
pl = percent(entry, exit)
#Max possible P&L (%) if we exit at highest price in each trade
max_pl = percent(entry, highest)
#Caluclate the number of good & bad trades
if pl <= 0:
miss = miss + 1
else:
hit = hit + 1
#Update the accumulative gains & max possible gains
gain = gain + (gain * pl/100)
max_gain = max_gain + (max_gain * max_pl/100)
#Add the trade to the list of trades
trade_row = [
exit_reason, "{:.2f}".format(entry), entry_date, "{:.2f}".format(target),
"{:.2f}".format(exit), exit_date, "{:.2f}%".format(pl), days_in_trade, "{:.2f}".format(highest), "{:.2f}%".format(max_pl)
]
trades.append(trade_row)
#Reset for next entry
hit_target = False
in_trade = False
if len(trades) > 0:
print_table(table_headers, trades)
print("")
print("Total trades: ({}) -- Hit: ({}) -- Miss: ({})".format(total_trades, hit, miss))
print("Actual gains: ({:.2f}%) -- Max possibe gains: ({:.2f}%)".format(percent(start_budget, gain), percent(start_budget, max_gain)))
else:
print("No possible trades")
start_app()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment