-
Notifications
You must be signed in to change notification settings - Fork 14
/
Trader.py
185 lines (153 loc) · 6.41 KB
/
Trader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
from Brain import Brain
class Trader(object):
"""A Trader is meant to emulate the role of a trader in the stock
market.
A Trader will have a Portfolio and make decisions based on a set of
criteria.
Supported Portfolio Management:
- contributions every month/quarter/year
- rebalancing every month/quarter/year
Attributes:
portfolio: A Portfolio instance to manage
assets_of_interest: An array of strings, each of which
represent a ticker of interest
Todo:
- [new feature] reimplement withdrawals
- [new feature] dynamic strategies (need to plan specifics)
"""
def __init__(self, starting_cash, portfolio, market):
"""Initializes a Trader.
Args:
starting_cash: A value representing the amount of cash this
Trader starts with
portfolio: A Portfolio to be managed by this Trader
assets_of_interest: An array of strings, each of which
represent a ticker of interest
"""
# general setup
self._brain = Brain()
self._contributions = None
self.use_portfolio(portfolio)
self.use_market(market)
self.set_starting_cash(starting_cash)
def use_portfolio(self, portfolio):
"""Sets the Portfolio this Trader should use. Propagates the
Portfolio to the Brain as well.
Args:
portfolio: A Portfoio instance to use
"""
self.portfolio = portfolio
self._brain.use_portfolio(portfolio)
def use_market(self, market):
"""Sets the market this Trader should use for looking up
prices. Propagates the Market to the Brain as well.
Args:
market: A Market instance to use
"""
self._market = market
self._brain.use_market(market)
self.portfolio.use_market(market)
def set_starting_cash(self, amount):
"""Sets the starting cash of this trader.
Args:
amount: A value representing the starting cash
"""
self.starting_cash = float(amount)
self.portfolio.starting_cash = float(amount)
def set_contributions(self, amount, period):
"""Sets the amount of cash to contribute per period.
Args:
amount: A value representing the amount to contribute
period: A value representing the frequency of contributions
e.g. 'm' for monthly
"""
self._contributions = (amount, period)
def set_rebalancing_period(self, period):
"""Sets the rebalancing frequency. Propagates the value to the
Brain. The Trader will rebalance every new period, regardless
of ratios or if the Portfolio was recently rebalanced.
Args:
period: A value representing the frequency of rebalancing
e.g. 'm' for monthly
"""
self._brain.set_rebalancing_period(period)
def set_strategy(self, strategy):
"""Sets to one of the predefined strategies."""
self._brain.set_strategy(strategy)
def initialize_portfolio(self):
"""Sets up the portfolio to the current desired ratios.
Intended to run once at start."""
self.portfolio.add_cash(self.starting_cash)
self._brain.decide_needed_shares()
self._execute_trades()
def adjust_portfolio(self):
"""Decides a new portfolio asset allocation, if applicable, and
adjusts the Portfolio to it."""
self._contribute()
self._brain.decide_needed_shares()
self._execute_trades()
def get_assets_of_interest(self):
"""Returns this Trader's assets of interest.
Returns:
A list of assets
"""
return self._brain.assets_of_interest.copy()
def add_asset_of_interest(self, ticker):
"""Adds a ticker for an asset to the assets of interest.
Args:
ticker: A string representing the ticker of a desired asset
"""
self._brain.assets_of_interest.add(ticker)
self._brain.desired_ratios[ticker] = 0
self._brain.desired_shares[ticker] = 0
def add_assets_of_interest(self, tickers):
"""Adds a set tickers to the assets of interest.
Args:
ticker: A set of tickers
"""
self._brain.assets_of_interest |= tickers
for ticker in tickers:
self._brain.desired_ratios[ticker] = 0
self._brain.desired_shares[ticker] = 0
def set_desired_asset_ratio(self, ticker, ratio):
"""Sets an allocation for an asset.
Args:
ticker: A string representing the ticker of a desired asset
ratio: An int corresponding to the desired ratio for the
given ticker
"""
self._brain.desired_asset_ratios[ticker] = ratio
def _execute_trades(self):
"""Calculates the trades needed to be made to satisfy the
desired shares and executes the trades.
NOTE: Sometimes there isn't the right amount of cash for a buy,
in which case a buy/sell performs the maximum amount it can
do. As a result, the desired shares need to be updated to
avoid trying to buy/sell the remaining shares every
following day."""
# calculate trades needed
desired_trades = {'buy': {}, 'sell': {}}
for asset in self._brain.assets_of_interest:
change = (self._brain.desired_shares[asset]
- self.portfolio.shares_of(asset))
if change < 0:
desired_trades['sell'][asset] = abs(change)
elif change > 0:
desired_trades['buy'][asset] = abs(change)
# perform sells
for (ticker, amount) in desired_trades['sell'].items():
self.portfolio.sell(ticker, amount)
self._brain.desired_shares[ticker] \
= self.portfolio.shares_of(ticker)
# perform buys
for (ticker, amount) in desired_trades['buy'].items():
self.portfolio.buy(ticker, amount)
self._brain.desired_shares[ticker] \
= self.portfolio.shares_of(ticker)
def _contribute(self):
"""Contributes to the portfolio according to the set
contribution settings."""
if self._contributions == None:
return
if self._market.new_period[self._contributions[1]]:
self.portfolio.add_cash(float(self._contributions[0]))