Last active
August 26, 2022 15:57
-
-
Save AliAlhajji/2640a634ec40d9c9870f2b4e9fd5aca2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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