Skip to content

Commit

Permalink
changed trades in strategy to a pd.df - started implementing Strategy…
Browse files Browse the repository at this point in the history
…Analysis
  • Loading branch information
97gamjak committed Apr 2, 2024
1 parent fa0ea58 commit e322047
Show file tree
Hide file tree
Showing 11 changed files with 523 additions and 430 deletions.
237 changes: 116 additions & 121 deletions notebooks/strategy1.ipynb

Large diffs are not rendered by default.

23 changes: 9 additions & 14 deletions stockMarket/technicalAnalysis/_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ def _write_strategy_json(cls) -> None:

@classmethod
def write_trades(cls,
trades: Dict[str, List[Trade]],
trades: pd.DataFrame,
dir_path: pathlib.Path,
) -> None:
"""
Function to write all trades from a strategy to a json file.
Parameters
----------
trades : Dict[str, List[Trade]]
trades : List[Trade]
A dictionary with the ticker as key and a list of trades as value.
dir_path : pathlib.Path
The directory path where the json files are stored.
Expand All @@ -138,26 +138,22 @@ def write_trades(cls,

@classmethod
def _trades_to_json(cls,
trades: Dict[str, List[Trade]]
trades: List[Trade],
) -> Dict[str, List[Dict[str, Any]]]:
"""
Function to convert trades to a json serializable format.
Parameters
----------
trades : Dict[str, List[Trade]]
trades : List[Trade]
A dictionary with the ticker as key and a list of trades as value.
Returns
-------
Dict[str, List[Dict[str, Any]]]
A dictionary with the ticker as key and a list of json serializable trades as value.
"""
json_trades = {}
for ticker, ticker_trades in trades.items():
json_trades[ticker] = [trade.to_json() for trade in ticker_trades]

return json_trades
return [trade.to_json() for trade in trades]

@classmethod
def write_earnings_calendar(cls,
Expand Down Expand Up @@ -337,7 +333,7 @@ def _read_trades(cls) -> Dict[str, List[Trade]]:
Returns
-------
Dict[str, List[Trade]]
List[Trade]
A dictionary with the ticker as key and a list of trade objects as value.
"""
with open(cls.trades_json_file, "r") as file:
Expand All @@ -353,16 +349,15 @@ def _trades_from_json(cls,
Parameters
----------
json_data : Dict[str, List[Dict[str, Any]]]
json_data : List[Dict[str, Any]]
A dictionary with the ticker as key and a list of json serializable trades as value.
Returns
-------
Dict[str, List[Trade]]
List[Trade]
A dictionary with the ticker as key and a list of trade objects as value.
"""
return {ticker: [Trade.init_from_json(trade) for trade in ticker_trades]
for ticker, ticker_trades in json_data.items()}
return [Trade.init_from_json(trade) for trade in json_data]

@classmethod
def _read_earnings_calendar(cls) -> Dict[str, List[dt.date]]:
Expand Down
66 changes: 16 additions & 50 deletions stockMarket/technicalAnalysis/plotter/strategyPlotter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, strategy: Strategy):
self.strategy = strategy
self.finalize_commands = strategy.finalize_commands

self.trades = np.concatenate(list(strategy.trades.values()))
self.trades = self.strategy.trades

def plot_all(self):
self.plot_PL_histogram()
Expand All @@ -25,37 +25,27 @@ def plot_PL_histogram(self,
max_bin: float = 6.0,
) -> None:

trades_df = self.trades.copy()

bins = np.arange(min_bin, max_bin, bin_size)
bins = np.append(bins, np.inf)

win_PL = [
trade.PL
for trade in self.trades
if trade.outcome_status == TradeOutcome.WIN
]

loss_PL = [
trade.PL
for trade in self.trades
if trade.outcome_status == TradeOutcome.LOSS
]

win_PL = np.array(win_PL)
loss_PL = np.array(loss_PL)
# add bins to self.trade dataframe for PL - PL is already in the dataframe
trades_df['bin'] = np.digitize(trades_df.PL, bins) - 1

# create 2d numpy array of win and loss PL values for each bin
win_PL_indices = np.digitize(win_PL, bins) - 1
loss_PL_indices = np.digitize(loss_PL, bins) - 1
win_PL = trades_df[trades_df.outcome_status == TradeOutcome.WIN].PL
loss_PL = trades_df[trades_df.outcome_status ==
TradeOutcome.LOSS].PL

win_PL_per_bin = [win_PL[win_PL_indices == i]
win_PL_per_bin = [win_PL[trades_df.bin == i]
for i in range(0, len(bins))]
loss_PL_per_bin = [loss_PL[loss_PL_indices == i]
loss_PL_per_bin = [loss_PL[trades_df.bin == i]
for i in range(0, len(bins))]

amount_wins_per_bin = np.array([len(win_PL_per_bin[i])
for i in range(0, len(bins))])
amount_losses_per_bin = np.array([len(loss_PL_per_bin[i])
for i in range(0, len(bins))])
amount_wins_per_bin = np.array(
[len(win_PL_per_bin[i]) for i in range(0, len(bins))])
amount_losses_per_bin = np.array(
[len(loss_PL_per_bin[i]) for i in range(0, len(bins))])

# Calculate the total number of trades in each bin
total_trades_per_bin = amount_wins_per_bin + amount_losses_per_bin
Expand Down Expand Up @@ -109,31 +99,7 @@ def plot_PL_histogram(self,
@finalize
def plot_trades_vs_time(self, max_loss: float = 1.0):

trade_data = [
(
trade.INVESTMENT,
trade.OUTCOME,
trade.trade_status,
trade.EXIT_date,
trade.ENTRY_date
)
for trade in self.trades
]

# Create a DataFrame from the trades
trades_df = pd.DataFrame(
trade_data,
columns=['INVESTMENT',
'OUTCOME',
'trade_status',
'EXIT_date',
'ENTRY_date'
]
)
trades_df['INVESTMENT'] = trades_df['INVESTMENT'] * max_loss
trades_df['OUTCOME'] = trades_df['OUTCOME'] * max_loss
trades_df['INVESTMENT'] = trades_df['INVESTMENT'] * max_loss
trades_df['OUTCOME'] = trades_df['OUTCOME'] * max_loss
trades_df = self.trades.copy()
trades_df = trades_df.dropna(subset=['ENTRY_date'])

executed_trades = trades_df[
Expand Down Expand Up @@ -168,7 +134,7 @@ def plot_trades_vs_time(self, max_loss: float = 1.0):

# calculate amount trades where trade_status is open or EXIT_date is larger than the date index of result_df and trade_status is closed
def update_result(row):
date = row.name
date = row.name.date()
df = executed_trades.copy()
df = df.drop(df[df['ENTRY_date'] > date].index)

Expand Down
40 changes: 29 additions & 11 deletions stockMarket/technicalAnalysis/strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@
from .trade import (
Trade,
TradeSettings,
TradeOutcome,
TradeStatus,
)
from ._json import StrategyJSON
from ._common import finalize
from .strategyFileSettings import StrategyFileSettings
from .strategyXLSXWriter import StrategyXLSXWriter
from .strategyAnalysis import StrategyAnalysis
from stockMarket.utils import Period
from stockMarket.yfinance._common import get_weekly_candle_range, get_daily_candle_range

Expand Down Expand Up @@ -147,7 +146,8 @@ def __clean_init__(self,

self.strategy_objects = strategy_objects
self.rule_enums = rule_enums
self.trades: Dict[str, List[Trade]] = {}
self.trades = pd.DataFrame()
self.trade_objects = []
self.error_logger = {}
self.use_earnings_dates = use_earnings_dates
self.finalize_commands = finalize_commands
Expand Down Expand Up @@ -211,7 +211,15 @@ def __init_from_json__(self, dir_path: Path):

self.strategy_objects = StrategyJSON.strategy_objects
self.rule_enums = StrategyJSON.rule_enums
self.trades = StrategyJSON.trades

self.trade_objects = StrategyJSON.trades
self.trades = pd.DataFrame(
[
trade.trade_dictionary
for trade in self.trade_objects
]
)

self.use_earnings_dates = StrategyJSON.use_earnings_dates
self.earnings_calendar = StrategyJSON.earnings_calendar
self.start_date = StrategyJSON.start_date
Expand Down Expand Up @@ -334,10 +342,20 @@ def screen(self, tickers: List[str] | str) -> None:
pricing, pricing_daily = self.populate_pricing_data(ticker)
self._screen_single_ticker(ticker, pricing, pricing_daily)

self.xlsx_writer.write_xlsx_file(self.trades, self.earnings_calendar)
self.trades = pd.DataFrame(
[
trade.trade_dictionary
for trade in self.trade_objects
]
)

self.xlsx_writer.write_xlsx_file(
StrategyAnalysis(self.trades),
self.earnings_calendar
)

StrategyJSON.write_trades(
trades=self.trades,
trades=self.trade_objects,
dir_path=self.dir_path
)

Expand Down Expand Up @@ -391,8 +409,6 @@ def _screen_single_ticker(self,
if pricing is None:
return

self.trades[ticker] = []

end_index = _calculate_end_date_index(
pricing,
self.end_date
Expand All @@ -419,13 +435,15 @@ def _screen_single_ticker(self,
ticker, pricing.iloc[index], self.trade_settings)

try:
trade.execute_trade(pricing, pricing_daily)
trade.execute_trade(
pricing,
pricing_daily
)
except Exception as e:
print(f"Error executing trade for ticker {ticker}")
raise e

trade.condition = rule_enum.value
self.trades[ticker].append(trade)
self.trade_objects.append(trade)


def _check_dates(start_date: str, end_date: str) -> tuple[dt.date, dt.date]:
Expand Down
32 changes: 32 additions & 0 deletions stockMarket/technicalAnalysis/strategyAnalysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pandas as pd

from .trade import TradeOutcome, TradeStatus


class StrategyAnalysis:
def __init__(self,
trades: pd.DataFrame
) -> None:

self.trades = trades

@property
def average_PL(self) -> float:
return self.trades[self.trades.trade_status == TradeStatus.CLOSED].PL.mean()

@property
def average_R_PL(self) -> float:
return self.trades[self.trades.trade_status == TradeStatus.CLOSED].R_PL.mean()

@property
def win_rate(self) -> float:
_trades = self.trades[self.trades.trade_status == TradeStatus.CLOSED]
return len(_trades[_trades.outcome_status == TradeOutcome.WIN]) / len(_trades)

@property
def number_of_wins(self) -> int:
return len(self.trades[self.trades.outcome_status == TradeOutcome.WIN])

@property
def number_of_losses(self) -> int:
return len(self.trades[self.trades.outcome_status == TradeOutcome.LOSS])
Loading

0 comments on commit e322047

Please sign in to comment.