Skip to content

Commit c20bc3f

Browse files
committed
trading system script provided
1 parent 671adbf commit c20bc3f

File tree

1 file changed

+227
-0
lines changed

1 file changed

+227
-0
lines changed

tradingsystem/trading_system.py

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
"""
2+
Trading system as a script.
3+
See the instructions in the notebook on how this works.
4+
The only change here is that the bhav file is
5+
automatically picked up from the web. You need to
6+
change this manually in the utils.py file if there is a holiday.
7+
"""
8+
9+
# Import libraries
10+
import pandas as pd
11+
import os
12+
import datetime
13+
import json
14+
from jinja2 import Environment, FileSystemLoader
15+
from utils import *
16+
17+
# PARAMETERS
18+
# Only change the parameters to
19+
20+
UNIVERSE = 'NIFTY50' # Universe to be searched
21+
STOP_LOSS = 3 # Stop loss for the order
22+
NUM_STOCKS = 5 # Number of stocks to sell
23+
CAPITAL = 20000
24+
LEVERAGE = 1
25+
ORDERFILE_PREFIX = 'orders_' # Prefix file name to store
26+
DIVERSIFY = False # whether to diversify data among sectors
27+
ACCOUNTID = 'XXXXXX' # For any user
28+
APIKEY = 'xxxxxxxxxxxxxxxx' # For zerodha
29+
30+
# Prepare the dataframe
31+
yesterday, today = pd.bdate_range(end=datetime.datetime.now(), periods=2)
32+
preopen = fetch_preopen_data()
33+
eod = get_bhav_copy(yesterday)
34+
symbols = pd.read_excel('universe.xlsx', sheet_name=UNIVERSE, header=None).values.ravel()
35+
sectors = pd.read_csv('sectors.csv')
36+
df = eod[eod['SYMBOL'].isin(symbols)]
37+
df = df[df['SERIES'] == "EQ"].reset_index(drop=True)
38+
df = df.merge(sectors)
39+
40+
41+
# Diversify function for diversifying sectors
42+
def diversify(frame, n=5):
43+
"""
44+
Diversify stocks among sectors
45+
frame
46+
dataframe
47+
n
48+
number of stocks
49+
"""
50+
industry = set()
51+
stocks = []
52+
for k,v in frame.iterrows():
53+
if v.at['Industry'] not in industry:
54+
industry.add(v.at['Industry'])
55+
stocks.append(v)
56+
if len(stocks) == n:
57+
break
58+
f = pd.DataFrame(stocks)
59+
if len(f) == 0:
60+
return pd.DataFrame()
61+
else:
62+
return f
63+
64+
# Trading logic
65+
66+
df['RET'] = (df['CLOSE']/df['PREVCLOSE']) - 1
67+
if DIVERSIFY:
68+
result = diversify(df.sort_values(by='RET', ascending=False), NUM_STOCKS)
69+
else:
70+
result = df.sort_values(by='RET', ascending = False).iloc[:NUM_STOCKS]
71+
trading_capital = CAPITAL * LEVERAGE
72+
orders = result.merge(preopen, on='SYMBOL')
73+
num_stock = len(orders)
74+
75+
# Pricing logic goes here
76+
77+
orders['trigger_price'] = (orders['OpenPrice'] - 0.05).round(2)
78+
orders['price'] = (orders['trigger_price'] - 0.05).round(2)
79+
80+
# I prefer a constant percentage change instead of value
81+
# If you prefer it, uncomment the below two lines
82+
83+
#orders['trigger_price'] = orders['OpenPrice'] * 0.9985
84+
#orders['price'] = orders['trigger_price'] - 0.05
85+
86+
orders['stop_loss'] = (orders['price'] * (1 + STOP_LOSS * 0.01)).apply(tick).round(2)
87+
orders['qty'] = (trading_capital/num_stock/orders['price']).astype(int)
88+
orders['order'] = 'SELL'
89+
filename = ORDERFILE_PREFIX + datetime.datetime.today().strftime('%Y-%m-%d') + '.csv'
90+
orders.to_csv('orders/' + filename, index=False)
91+
92+
def generate_nest():
93+
"""
94+
Most brokers and software provide an option to place basket orders.
95+
So we are going to create a basket order from our orders. We would be using the ``create_order`` function from ``utils.py`` file.
96+
To do this for your specific broker, do the following steps
97+
1. Know the format of your broker; you can do this by placing a basket order and exporting it
98+
2. The format would usually have a list of columns to be filled up. We need to fill all the columns to import our order.
99+
3. We separate the columns into columns that are already in our dataframe and columns to be included
100+
4. We prepare a list of matching columns and rename them
101+
5. For new columns, we create a python dictionary with keys as column names and values as the value for the column (we assume that these columns have a single value)
102+
103+
Thanks @vjay for providing the necessary support
104+
"""
105+
# List of columns to be included in the output
106+
107+
cols = [
108+
'Segment', 'InstrumentName', 'Symbol', 'Option Type', 'Strike Price',
109+
'ExpiryDate', 'Price', 'Qty', 'LTP', 'Buy/Sell', 'Order Type',
110+
'TriggerPrice', 'Pro/Cli', 'P Type', 'Validity', 'AccountId',
111+
'Validity Date', 'Remarks', 'Participant code', 'Validity Time',
112+
'Market Proc'
113+
]
114+
115+
# These columns are common for all orders - columns with a single name
116+
columns = {
117+
'Segment': 'NSE',
118+
'InstrumentName': 'EQ',
119+
'Option Type': 'NA',
120+
'Strike Price': 'NA',
121+
'ExpiryDate': 'NA',
122+
'LTP': 0,
123+
'Disclosed Qty': 0,
124+
'AccountId': ACCOUNTID,
125+
'Pro/Cli': 'CLI',
126+
'Validity': 'DAY',
127+
'P Type': 'MIS',
128+
'Remarks': '',
129+
'Validity Date': 'NA',
130+
'Participant code': '',
131+
'Validity Time': 'NA',
132+
'Market Proc': 'NA',
133+
'Order Type': 'SL'
134+
}
135+
136+
# These are columns to be renamed
137+
rename = {
138+
'order': 'Buy/Sell',
139+
'price': 'Price',
140+
'qty': 'Qty',
141+
'trigger_price': 'TriggerPrice',
142+
'price': 'Price' ,
143+
'SYMBOL': 'Symbol'
144+
}
145+
146+
147+
# Generating orders in the required format
148+
entry_orders = orders.copy()
149+
entry = create_orders(entry_orders, rename=rename, **columns)
150+
151+
# Exit orders order type and price to be changed
152+
exit_orders = orders.copy()
153+
exit_orders['order'] = 'BUY'
154+
exit_orders['price'] = 0
155+
exit_orders['trigger_price'] = stop_loss(orders['price'], 3, order='S').round(2)
156+
columns.update({'Order Type': 'SL-M'})
157+
exit = create_orders(exit_orders, rename=rename, **columns)
158+
159+
# File generation
160+
entry.append(exit, sort=False)[cols].to_csv('orders_to_place.csv',
161+
index=False, header=False)
162+
163+
print('File generated for NEST')
164+
165+
def generate_zerodha():
166+
"""
167+
Order generation for Kite Zerodha
168+
1. Sign up for a zerodha publisher api key at(https://kite.trade/)
169+
2. Update the API key in parameters
170+
3. This would create an html file **zerodha_order.html** in your present working directory.
171+
4. Open the HTML file and click the submit button to log into zerodha and place your orders.
172+
5. Check the updated at time to make sure that this is the latest generated order.
173+
"""
174+
175+
# List of columns to be included in the output
176+
cols = [
177+
'tradingsymbol', 'exchange', 'transaction_type', 'order_type',
178+
'quantity', 'product', 'validity', 'price', 'trigger_price'
179+
]
180+
181+
# These columns are common for all orders - columns with a single name
182+
columns = {
183+
'exchange': 'NSE',
184+
'product': 'MIS',
185+
'validity': 'DAY',
186+
'order_type': 'SL'
187+
}
188+
189+
# These are columns to be renamed
190+
rename = {
191+
'order': 'transaction_type',
192+
'price': 'Price',
193+
'qty': 'quantity',
194+
'trigger_price': 'trigger_price',
195+
'price': 'price' ,
196+
'SYMBOL': 'tradingsymbol'
197+
}
198+
199+
# Generating orders in the required format
200+
entry_orders = orders.copy()
201+
entry = create_orders(entry_orders, rename=rename, **columns)
202+
203+
# Exit orders order type and price to be changed
204+
exit_orders = orders.copy()
205+
exit_orders['order'] = 'BUY'
206+
exit_orders['price'] = 0
207+
exit_orders['trigger_price'] = stop_loss(orders['price'], 3, order='S').round(2)
208+
columns.update({'order_type': 'SL-M'})
209+
exit = create_orders(exit_orders, rename=rename, **columns)
210+
trades = entry.append(exit, sort=False)[cols].to_dict(orient='records')
211+
212+
# Order HTML file generated
213+
env = Environment(loader=FileSystemLoader('.'))
214+
template = env.get_template('template.html')
215+
output_from_parsed_template = template.render(api_key = APIKEY,
216+
orders=json.dumps(trades),
217+
date=str(datetime.datetime.now()))
218+
219+
with open('zerodha_order.html', 'w') as f:
220+
f.write(output_from_parsed_template)
221+
print('Zerodha order file generated')
222+
223+
if __name__ == "__main__":
224+
generate_nest()
225+
generate_zerodha()
226+
227+

0 commit comments

Comments
 (0)