forked from google/arithmancer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
315 lines (286 loc) · 11 KB
/
main.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
#!/usr/bin/python
#
# Copyright 2017 Google, Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Main: Provides the routes and application logic"""
from flask import Flask
from google.appengine.ext import ndb
from google.appengine.api import users
import datetime
import math
import logging
import os
from flask import render_template
from flask import redirect
from flask import request
from flask import jsonify
from flask import flash
from models import Prediction
from models import Trade
from models import LedgerRecords
from models import Profile
from models import Price
from models import get_price_for_trade
from models import verification_of_trade
from models import check_if_user_profile
from models import calculate_trade_from_likelihood
app = Flask(__name__)
app.config['DEBUG'] = True
app.secret_key = os.environ['SECRET_KEY']
@app.route('/')
def CheckSignIn():
user = users.get_current_user()
if not user:
login_url = users.create_login_url('/')
greeting = '<a href="{}">Sign in</a>'.format(login_url)
return render_template('splash.html', login=login_url)
else:
profile = check_if_user_profile(user.user_id())
return redirect('/predictions')
@app.route('/predictions', methods=['GET'])
def GetPredictions():
"""Returns all of the predictions (and can filter by org)."""
org_filter = request.args.get('org', False)
if org_filter:
predictions = Prediction.query(Prediction.org == org_filter).fetch()
else:
predictions = Prediction.query().fetch()
for prediction in predictions:
# TODO(goldhaber): add these to the datastore
prediction.url = 'predictions/' + prediction.key.urlsafe()
prediction.price = GetPriceByPredictionId(prediction.key.urlsafe()) * 100
return render_template('predictions.html', predictions=predictions)
@app.route('/predictions/<string:prediction_id>/price', methods=['GET'])
def GetPriceByPredictionId(prediction_id):
"""Returns the current market price of contract one for a prediction_id."""
prediction_key = ndb.Key(urlsafe=prediction_id)
pred = prediction_key.get()
price = math.pow(math.e, (pred.contract_one / pred.liquidity)) / (
math.pow(math.e, (pred.contract_two / pred.liquidity)) + math.pow(
math.e, (pred.contract_two / pred.liquidity)))
return float(price)
@app.route('/predictions/<string:prediction_id>/pricelist', methods=['GET'])
def GetPrices(prediction_id):
prices = Price.query(Price.prediction_id == ndb.Key(urlsafe=prediction_id)).order(-Price.date).fetch(30)
print(str(prices))
prices = [{'price': p.value, 'date': {'year': p.date.year, 'month': p.date.month, 'day': p.date.day}} for p in prices]
return prices
@app.route('/predictions/<string:prediction_id>', methods=['GET'])
def GetPredictionById(prediction_id):
"""Returns a prediction by prediction_id."""
try:
prediction_key = ndb.Key(urlsafe=prediction_id)
prediction = prediction_key.get()
portfolio = GetUserPortfolioByAuth(prediction_id)
except:
return render_template('404.html')
return render_template(
'prediction.html',
prediction=prediction,
price=GetPriceByPredictionId(prediction_id),
pricelist=GetPrices(prediction_id),
prediction_id=prediction_id,
portfolio=portfolio)
@app.route('/predictions/create', methods=['GET'])
def CreatePrediction():
return render_template('prediction_create.html')
@app.route('/predictions/create/new', methods=['POST'])
def NewPrediction():
try:
prediction = Prediction(liquidity=float(request.form['liquidity']),
info=request.form['info'],
statement=request.form['statement'],
end_time=datetime.datetime.strptime(request.form['endtime'], "%Y-%m-%d"),
org=request.form['org'])
prediction_id = prediction.put()
flash('You created a new prediction!')
return redirect('/predictions/' + prediction_id.urlsafe())
except:
flash('error')
return redirect('/predictions/create')
@app.route('/users/create', methods=['GET'])
def CreateUser():
"""Route for checking if user exists."""
profile = check_if_user_profile(users.get_current_user().user_id())
return str(profile)
@app.route('/users/me', methods=['GET'])
def GetUserByAuth():
"""Returns current users profile."""
user_key = users.get_current_user().user_id()
user_key = ndb.Key('Profile', user_key)
profile = user_key.get()
for ledger in profile.user_ledger:
try:
price = GetPriceByPredictionId(ledger.prediction_id)
ledger.value = math.fabs((price * ledger.contract_one) - (
price * ledger.contract_two))
ledger.prediction_statement = ndb.Key(
urlsafe=ledger.prediction_id).get().statement
except:
ledger.value = 404
ledger.prediction_statement = 'ERROR'
return render_template('profile.html', profile=profile)
@app.route('/users/me/balance', methods=['GET'])
def GetUserBalanceByAuth():
"""Returns current users balance."""
user_key = ndb.Key('Profile', users.get_current_user().user_id())
profile = user_key.get()
return str(profile.balance)
# TODO(goldhaber): change to GetUserPortfolioByAuth By Prediction ID
@app.route('/users/me/portfolio', methods=['GET'])
def GetUserPortfolioByAuth(prediction_id):
"""Returns current users porfolio by prediction_id."""
user_key = ndb.Key('Profile', users.get_current_user().user_id())
profile = user_key.get()
portfolio = []
if prediction_id:
portfolio = [
i for i in profile.user_ledger if i.prediction_id == prediction_id
]
return portfolio
@app.route('/trades/create', methods=['POST'])
def CreateTrade():
"""Creates a trade for the user."""
user_id = users.get_current_user().user_id()
user_key = ndb.Key('Profile', user_id)
current_user = user_key.get()
prediction_key = ndb.Key(urlsafe=request.form['prediction_id'])
prediction = prediction_key.get()
if request.form['is_likelihood'] == 'true':
user_id = users.get_current_user().user_id()
user_key = ndb.Key('Profile', user_id)
current_user = user_key.get()
trade = calculate_trade_from_likelihood(
float(request.form['likelihood']), prediction, current_user)
print trade
else:
trade = Trade(
prediction_id=prediction_key,
user_id=user_key,
direction=request.form['direction'],
contract=request.form['contract'],
quantity=float(request.form['quantity']))
err = CreateTradeAction(prediction, current_user, trade)
#TODO replace with error
if err != 'error':
flash('You successfully predicted!')
return redirect('/predictions/' + trade.prediction_id.urlsafe())
def CreateTradeAction(prediction, current_user, trade):
verification_of_trade(trade)
price = get_price_for_trade(prediction, trade)
# TODO(goldhaber):replace flag
new_ledger_record = False
if trade.direction == 'BUY':
if current_user.balance >= price:
current_user.balance -= price
# TODO(goldhaber): check results and/or better formatting
current_user_portfolio = [
i for i in current_user.user_ledger
if i.prediction_id == trade.prediction_id.urlsafe()
]
if len(current_user_portfolio) == 0:
ledger_records = LedgerRecords()
ledger_records.prediction_id = trade.prediction_id.urlsafe()
ledger_records.user_id = current_user.user_id
if trade.contract == 'CONTRACT_ONE':
ledger_records.contract_one = trade.quantity
ledger_records.contract_two = 0.00
else:
ledger_records.contract_two = trade.quantity
ledger_records.contract_one = 0.00
current_user.user_ledger.append(ledger_records)
new_ledger_record = True
else:
ledger_record_to_update = current_user_portfolio[0]
if trade.contract == 'CONTRACT_ONE':
ledger_record_to_update.contract_one += trade.quantity
else:
ledger_record_to_update.contract_two += trade.quantity
if trade.contract == 'CONTRACT_ONE':
prediction.contract_one += trade.quantity
else:
prediction.contract_two += trade.quantity
else:
# TODO(goldhaber): throw error
return 'error'
else:
current_user_portfolio = [
i for i in current_user.user_ledger
if i.prediction_id == trade.prediction_id.urlsafe()
]
if (trade.contract == 'CONTRACT_ONE' and
current_user_portfolio[0].contract_one >= trade.quantity) or (
trade.contract == 'CONTRACT_TWO' and
current_user_portfolio[0].contract_two >= trade.quantity):
ledger_record_to_update = current_user_portfolio[0]
if trade.contract == 'CONTRACT_ONE':
ledger_record_to_update.contract_one -= trade.quantity
prediction.contract_one -= trade.quantity
else:
ledger_record_to_update.contract_two -= trade.quantity
prediction.contract_two -= trade.quantity
current_user.balance -= price
else:
return 'error'
trade.put()
if new_ledger_record:
ledger_records.put()
current_user.put()
prediction.put()
@app.route('/trades/sell', methods=['POST'])
def SellStake():
user_id = users.get_current_user().user_id()
user_key = ndb.Key('Profile', user_id)
current_user = user_key.get()
prediction_key = ndb.Key(urlsafe=request.form['prediction_id'])
prediction = prediction_key.get()
portfolio = GetUserPortfolioByAuth(request.form['prediction_id'])
for ledger in portfolio:
if ledger.contract_one > 0:
contract = 'CONTRACT_ONE'
quantity = ledger.contract_one
else:
contract = 'CONTRACT_TWO'
quantity = ledger.contract_two
trade = Trade(
prediction_id=prediction_key,
user_id=user_key,
direction='SELL',
contract=contract,
quantity=float(quantity))
err = CreateTradeAction(prediction, current_user, trade)
if err != 'error':
flash('You sold your stake!')
return redirect('/users/me')
@app.route('/testtrade/<string:prediction_id>', methods=['GET'])
def GetTradesForPredictionId(prediction_id):
user = users.get_current_user()
trades = Trade.query(ndb.AND(Trade.prediction_id == ndb.Key(urlsafe=prediction_id),
Trade.user_id == ndb.Key('Profile', user.user_id()))).fetch()
return str(trades)
@app.route('/faq', methods=['GET'])
def GetFaq():
return render_template('faq.html')
@app.errorhandler(404)
def page_not_found(e):
"""Return a custom 404 error."""
return render_template('404.html'), 404
@app.context_processor
def inject_balance():
user = users.get_current_user()
if not user:
return dict(balance=0)
user_key = ndb.Key('Profile', user.user_id())
profile = user_key.get()
return dict(balance=profile.balance)