Skip to content

Commit d2178b6

Browse files
committed
First Commit
0 parents  commit d2178b6

File tree

8 files changed

+586819
-0
lines changed

8 files changed

+586819
-0
lines changed

.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Folder with "instance" values
2+
instance/
3+
4+
# Dependecy 'pipenv' file(s)
5+
Pipfile
6+
7+
# Folder with *REAL & Functioning Strategies
8+
strategy/*_cross.py
9+
10+
# Folder with code snippets, bugs and issues found along the way, code issue documentation
11+
other/
12+
13+
# Folder containning the final `backtested` strategy data stored in JSON format
14+
strat_perm_data/

MarketOnClosePortfolio.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
from strategy.dependecies import *
2+
3+
# class MarketOnClosePortfolio(Portfolio):
4+
class MarketOnClosePortfolio():
5+
"""
6+
Here we define our portfolio to keep track of our indicators & strategies
7+
8+
Return:
9+
Pandas DataFrame for a set strategy analysis.
10+
"""
11+
12+
def __init__(self, initial_capital, cap_on_order, inital_num_shares, comission_per_trade, symbol, data, signal_hist):
13+
# Class Constructor
14+
# super().__init__()
15+
self.initial_capital = initial_capital # Intial Amount of Cash that can be lost (Euros)
16+
self.inital_num_shares = inital_num_shares # Initial Amount of Shares (before the start of trading)
17+
self.cap_on_order = cap_on_order # Fixed Amount of Cash set to be placed on each trade order
18+
self.num_share_to_buy = 0.068 # Fixed Amount of Shares set to be placed on each trade order
19+
self.comission_per_trade = comission_per_trade # Comission (%) per trade
20+
self.symbol = symbol # Market Ticker Symbol
21+
self.signal_hist = signal_hist # Signal History DataFrame
22+
self.df = data # Target Timeframe based DataFrame for a Trading Period of `X`
23+
self.positions = self.generate_positions() # Postiton Signal History DataFrame
24+
25+
def generate_positions(self):
26+
# Generate a pandas DataFrame to store quantity held at any “bar” timeframe
27+
positions = pd.DataFrame(index=self.signal_hist.index).fillna(0.0)
28+
positions[self.symbol] = 0.068 * self.signal_hist['trade_signal'] # Transact 100 shares on a signal
29+
30+
# DEV
31+
# print (positions)
32+
33+
return positions
34+
35+
def backtest_portfolio(self):
36+
# Create a new DataFrame ‘portfolio’ to store the market value of an open position
37+
portfolio = self.positions.multiply(self.df['p_open'], axis=0)
38+
pos_diff = self.positions.diff()
39+
40+
portfolio.dropna(inplace=True)
41+
42+
print (portfolio)
43+
print (pos_diff)
44+
45+
portfolio['comission ($)'] = (self.df['p_open'] * (self.comission_per_trade / 100)) * self.num_share_to_buy
46+
47+
# Create a ‘holdings’ Series that totals all open position market values
48+
# and a ‘cash’ column that stores remaining cash in account
49+
portfolio['holdings'] = self.positions.multiply(self.df['p_open'], axis=0).sum(axis=1)
50+
portfolio['cash'] = self.initial_capital - (pos_diff.multiply(self.df['p_open'], axis=0)).sum(axis=1).cumsum()
51+
52+
# Sum up the cash and holdings to create full account ‘equity’, then create the percentage returns
53+
portfolio['total'] = portfolio['cash'] + portfolio['holdings'] - portfolio['comission ($)']
54+
portfolio['returns'] = portfolio['total'].pct_change()
55+
portfolio[portfolio.eq(0.00000)] = np.nan
56+
57+
col_round = ['holdings', 'cash', 'total', 'comission ($)']
58+
portfolio[col_round] = portfolio[col_round].round(2)
59+
portfolio = portfolio.round({'returns': 5})
60+
print(portfolio.tail(50))
61+
62+
# DEV
63+
print(portfolio)
64+
65+
return portfolio
66+
67+
def backtest_portfolio_v2(self):
68+
# (Filter) Remove 0.0 'position' values, as they are unecessary for the end analysis result
69+
self.signal_hist = self.signal_hist[self.signal_hist['position'] != 0.0]
70+
# Create a new DataFrame ‘portfolio’ to store the market value of an open position
71+
portfolio = pd.DataFrame(index=self.signal_hist.index).fillna(0.0)
72+
73+
# Buy Order DataFrame
74+
buy = self.signal_hist[self.signal_hist.position == 1.0]
75+
buy['Buy Order Time'] = self.df.open_time
76+
buy['Buy Price'] = buy['SMA20']
77+
buy.reset_index(inplace=True)
78+
79+
# Sell Order DataFrame
80+
sell = self.signal_hist[self.signal_hist.position == -1.0]
81+
sell['Sell Order Time'] = self.df.open_time
82+
sell['Sell Price'] = sell['SMA20']
83+
sell.reset_index(inplace=True)
84+
85+
# Using 'concat' because it works with NaN Column Values
86+
portfolio = pd.concat([buy['Buy Order Time'],
87+
buy['Buy Price'],
88+
sell['Sell Order Time'],
89+
sell['Sell Price']], axis=1)
90+
91+
# Place Buy Order at SMA20 Price (Leading Indicator)
92+
# portfolio['BTC'] = cap_on_order / self.signal_hist.loc[(self.signal_hist['position'] == 1.0)]['SMA20']
93+
94+
# (Optional) Add `comission` to the portfolio ie: comission per trade in ($) dollars
95+
portfolio['Comission ($)'] = self.cap_on_order * (2 * self.comission_per_trade / 100)
96+
# Add `Share Quantity` to Portfolio to View how many Shares I own at the time of the trade
97+
portfolio['Share Quantity'] = self.cap_on_order / buy['Buy Price']
98+
# Add `Net Trade` to Portfolio to view the total Net profit/loss
99+
portfolio['total_net'] = self.cap_on_order / (sell['Sell Price'] - buy['Buy Price'] - portfolio['Comission ($)'])
100+
# Add `Percentage Change`
101+
portfolio['(%) Change'] = portfolio['total_net'] / buy['Buy Price'] * 100
102+
# Add `Portfolio Total` to Portfolio
103+
portfolio['Portfolio Total'] = (self.initial_capital - portfolio['Comission ($)'])
104+
# Add `Available Cash` to Portfolio
105+
portfolio['Available Cash ($)'] = self.initial_capital - (portfolio['Share Quantity'] * portfolio['Sell Price'])
106+
107+
# Dataframe Options and Filters
108+
portfolio.reset_index(inplace=True)
109+
portfolio.fillna(method='ffill', inplace=True)
110+
111+
# DEV
112+
# print(portfolio)
113+
114+
return portfolio
115+
116+
def backtest_portfolio_v3(self):
117+
118+
# FIXME: Fic the "Available Cash ($)" column data values and conditions
119+
120+
# (Filter) Remove 0.0 'position' values, as they are unecessary for the end analysis result
121+
self.signal_hist = self.signal_hist[self.signal_hist['position'] != 0.0]
122+
# Create a new DataFrame ‘portfolio’ to store the market value of an open position
123+
portfolio = pd.DataFrame(index=self.signal_hist.index).fillna(0.0)
124+
share_diff = self.positions.diff()
125+
126+
# print(share_diff)
127+
128+
# Buy Order DataFrame
129+
buy = self.signal_hist[self.signal_hist.position == 1.0]
130+
buy['Buy Order Time'] = self.df.open_time
131+
buy['Buy Price'] = buy['SMA20']
132+
buy['Available Cash ($)'] = self.initial_capital - (share_diff.multiply(buy['Buy Price'], axis=0)).sum(axis=1)
133+
buy['Holdings ($)'] = (self.positions.multiply(buy['Buy Price'], axis=0)).sum(axis=1)
134+
buy['Share Quantity'] = self.positions
135+
buy.reset_index(inplace=True)
136+
137+
# Sell Order DataFrame
138+
sell = self.signal_hist[self.signal_hist.position == -1.0]
139+
sell['Sell Order Time'] = self.df.open_time
140+
sell['Sell Price'] = sell['SMA20']
141+
sell['Return Cash ($)'] = - (share_diff.multiply(sell['Sell Price'], axis=0)).sum(axis=1)
142+
sell.reset_index(inplace=True)
143+
144+
# Using 'concat' because it works with NaN Column Values
145+
portfolio = pd.concat([buy['Available Cash ($)'],
146+
buy['Buy Order Time'],
147+
buy['Buy Price'],
148+
buy['Holdings ($)'],
149+
buy['Share Quantity'],
150+
sell['Sell Order Time'],
151+
sell['Sell Price'],
152+
sell['Return Cash ($)']], axis=1)
153+
154+
# Add `Percentage Change`
155+
portfolio['(%) Change'] = (portfolio['Return Cash ($)'] - portfolio['Holdings ($)']) / portfolio['Holdings ($)'] * 100
156+
# portfolio['Running Total'] = (portfolio['Available Cash ($)'] + (portfolio['Available Cash ($)'] * (portfolio['(%) Change'] * 100)))
157+
158+
# (Optional) Add `comission` to the portfolio ie: comission per trade in ($) dollars
159+
portfolio['Comission ($)'] = (portfolio['Holdings ($)'] + portfolio['Return Cash ($)']) * (self.comission_per_trade / 100)
160+
161+
# Dataframe Options and Filters
162+
portfolio.fillna(method='ffill', inplace=True)
163+
164+
# DEV
165+
# print(portfolio)
166+
167+
return portfolio

0 commit comments

Comments
 (0)