Skip to content

Commit

Permalink
edits before v3.1 pre-release
Browse files Browse the repository at this point in the history
  • Loading branch information
ozdemirozcelik committed Jun 2, 2022
1 parent 72d89a6 commit b0ba82b
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 22 deletions.
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Version 3 of the Flask-RESTful API.

(Release: v3.1)

Built from the ground-up with Flask-RESTful & Flask-SQLAlchemy & Flask-JWT-Extended.
Configured to be used with SQLite3 for local use.

Expand All @@ -24,7 +26,7 @@ https://api-pairs-v3.herokuapp.com/

With Pairs-API v3 you can:
- catch webhooks from trading platforms or signal generators
- list, save, update and delete stocks and pairs with API calls
- list, save, update and delete stocks/pairs, order and price details with API calls
- enable and disable stocks and pairs for active trading
- use access tokens for authentication purposes with login system backend
- TODO: send real time orders to exchange (possibly via Interactive Brokers)
Expand Down Expand Up @@ -170,6 +172,7 @@ Resources defined with flask_restful are:
```python
api.add_resource(SignalWebhook, "/v3/webhook")
api.add_resource(SignalPrice, "/v3/price")
api.add_resource(SignalList, "/v3/signals/<string:number_of_items>")
api.add_resource(SignalListStatus,"/v3/signals/status/<string:order_status>/<string:number_of_items>")
api.add_resource(SignalListTicker, "/v3/signals/ticker/<string:ticker_name>/<string:number_of_items>")
Expand Down Expand Up @@ -383,6 +386,48 @@ Response:
]
}
```
### POST request to save filled order prices by order id
```python
'http://api-pairs-v3.herokuapp.com/v3/signal/fillprice'
```
Request Body:
```json
{
"passphrase": "webhook",
"order_id": 945,
"stk_price": 100.756
}
```
Response:
(fill_price & slip is calculated automatically)
```json
{
"rowid": 47,
"ticker": "MA-3*V",
"order_action": "buy",
"order_contracts": 20,
"order_price": -2.0,
"mar_pos": "long",
"mar_pos_size": 20,
"pre_mar_pos": "flat",
"pre_mar_pos_size": 0,
"order_comment": "Enter Long",
"order_status": "filled",
"ticker_type": "pair",
"stk_ticker1": "MA",
"stk_ticker2": "V",
"hedge_param": 3.0,
"order_id1": 944,
"order_id2": 945,
"stk_price1": 300.1,
"stk_price2": 100.756,
"fill_price": -2.168,
"slip": 0.168,
"error_msg": null
}
```
### POST request to login with a user
```python
Expand Down
2 changes: 2 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from resources.pairs import PairRegister, PairList, Pair
from resources.signals import (
SignalWebhook,
SignalFillPrice,
SignalList,
SignalListTicker,
SignalListStatus,
Expand Down Expand Up @@ -175,6 +176,7 @@ def my_revoked_token_callback(jwt_header, jwt_payload):
# Resource definitions (Start)

api.add_resource(SignalWebhook, "/v3/webhook")
api.add_resource(SignalFillPrice, "/v3/signal/fillprice")
api.add_resource(SignalList, "/v3/signals/<string:number_of_items>")
api.add_resource(
SignalListStatus,
Expand Down
27 changes: 27 additions & 0 deletions models/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,3 +606,30 @@ def splitticker(self) -> bool:
@classmethod
def get_avg_slip(cls, ticker_name) -> List:
pass

@classmethod
def find_by_orderid(cls, orderid) -> "SignalModel":

return cls.query.filter((cls.order_id1 == orderid) | (cls.order_id2 == orderid)).first()

# @classmethod
# def update_price_by_orderid(cls, order_id, stk_price) -> "SignalModel":
#
# item = cls.query.filter((cls.order_id1 == order_id) | (cls.order_id2 == order_id)).first()
#
# if item:
#
# if item.order_id1 == order_id:
# print("*")
# item.stk_ticker1 = stk_price
# print(item.stk_ticker1)
#
# if item.order_id2 == order_id:
# print("**")
# item.stk_ticker2 = stk_price
#
# db.session.commit()
#
# return_item = SignalModel.find_by_rowid(item.rowid)
#
# return return_item
75 changes: 73 additions & 2 deletions resources/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,75 @@
NOT_FOUND = "item not found."
PRIV_ERR = "'{}' privilege required."

class SignalFillPrice(Resource):
parser = reqparse.RequestParser()
parser.add_argument(
"passphrase", type=str, required=True, help=EMPTY_ERR.format("passphrase")
)
parser.add_argument("order_id", type=int)
parser.add_argument("stk_price", type=float)

@staticmethod
def put():
data = SignalFillPrice.parser.parse_args()

# format return message inline with flask_restful parser errors
if SignalModel.passphrase_wrong(data["passphrase"]):
return_msg = {"message": {"passphrase": PASS_ERR}}
# return {"message": PASS_ERR}, 400 # Old return Bad Request
return return_msg, 400 # Old return Bad Request

# get signal with rowid
item = SignalModel.find_by_orderid(data["order_id"])

if item:

if item.order_id1 == data["order_id"]:
item.stk_price1 = data["stk_price"]
item.order_status = "filled(...)"

if item.order_id2 == data["order_id"]:
item.stk_price2 = data["stk_price"]
item.order_status = "filled(...)"

if item.ticker_type == "pair":

# if both orders are filled for pairs
if item.stk_price1 and item.stk_price2:
if item.order_action == "buy":
item.fill_price = round(item.stk_price1 - item.hedge_param*item.stk_price2, 4)
else:
item.fill_price = -round(item.stk_price1 - item.hedge_param*item.stk_price2, 4)
item.order_status = "filled"
item.slip = round(item.order_price - item.fill_price, 4)
else:
if item.stk_price1:
item.fill_price = item.stk_price1
item.order_status = "filled"
if item.order_action == "buy":
item.slip = round(item.order_price - item.fill_price, 4)
else:
item.slip = -round(item.order_price - item.fill_price, 4)


try:
item.update(item.rowid)

except Exception as e:
print("Error occurred - ", e)
return (
{"message": UPDATE_ERR},
500,
) # Return Interval Server Error

return_json = item.json()

return_json.pop("timestamp")

return return_json

return {"message": NOT_FOUND}, 404 # Return Not Found


class SignalWebhook(Resource):
parser = reqparse.RequestParser()
Expand Down Expand Up @@ -136,7 +205,6 @@ def post():
) # Return Successful Creation of Resource

@staticmethod
@jwt_required(fresh=True) # need fresh token
def put():
data = SignalWebhook.parser.parse_args()

Expand Down Expand Up @@ -272,7 +340,10 @@ def get(order_status, number_of_items="0"):
username = get_jwt_identity()

# limit the number of items to get if not logged-in
notoken_limit = 5
if order_status == "waiting":
notoken_limit = 20
else:
notoken_limit = 5

# without Authorization header, returns None.
# with Authorization header, returns username
Expand Down
8 changes: 4 additions & 4 deletions static/signals.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,15 +560,15 @@ async function listSignals() {
str = signals_data.signals[key].order_action + " | " + signals_data.signals[key].order_contracts + " | " + signals_data.signals[key].ticker;
str = str.toUpperCase()

if (signals_data.signals[key].order_status == "waiting" || signals_data.signals[key].order_status == "rerouted"){
if (signals_data.signals[key].order_status.includes("waiting") || signals_data.signals[key].order_status.includes("rerouted")) {
li.innerHTML = "<span title='waiting' class='numberCircle' style='background-color: whitesmoke';>"+ signals_data.signals[key].rowid +"</span>";
} else if (signals_data.signals[key].order_status.includes("err") || signals_data.signals[key].order_status.includes("cancel")) {
// show error messages in a tip box
li.innerHTML = "<span class='field-tip'><span title='error' class='numberCircle' style='background-color: orange';>"+ signals_data.signals[key].rowid +"</span><span class='tip-content'>" + signals_data.signals[key].error_msg + "</span></span>";
} else if (signals_data.signals[key].order_status == "filled") {
} else if (signals_data.signals[key].order_status.includes("filled")) {
li.innerHTML = "<span title='filled' class='numberCircle' style='background-color: lightgreen';>"+ signals_data.signals[key].rowid +"</span>";
} else if (signals_data.signals[key].order_status == "created") {
li.innerHTML = "<span title='created' class='numberCircle' style='background-color: lightblue';>"+ signals_data.signals[key].rowid +"</span>";
} else if (signals_data.signals[key].order_status.includes("created")) {
li.innerHTML = "<span class='field-tip'><span title='created' class='numberCircle' style='background-color: lightblue';>"+ signals_data.signals[key].rowid +"</span><span class='tip-content'>" + signals_data.signals[key].error_msg + "</span></span>";
}else {
li.innerHTML = "<span class='numberCircle';>"+ signals_data.signals[key].rowid +"</span>";
}
Expand Down
12 changes: 4 additions & 8 deletions templates/dash.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
{% if "error" in signal.order_status or "err" in signal.order_status or "cancel" in signal.order_status%}
<td style="background-color:orange"><span class='field-tip'>{{ signal.order_status }}<span class='tip-content'>{{ signal.error_msg }}</span></span></td>
{% elif "created" in signal.order_status %}
<td style="background-color:lightblue">{{ signal.order_status }}({{ (signal.timestamp|timediff) }} min)</td>
<td style="background-color:lightblue"><span class='field-tip'>{{ signal.order_status }}({{ (signal.timestamp|timediff) }} min)<span class='tip-content'>{{ signal.error_msg }}</span></span>
{% elif "waiting" in signal.order_status or "rerouted" in signal.order_status %}
<td style="background-color:whitesmoke">{{ signal.order_status }}({{ (signal.timestamp|timediff) }} min)</td>
{% elif "filled" in signal.order_status %}
Expand All @@ -38,14 +38,10 @@
<td>{{ signal.order_status }}</td>
{% endif %}
<td>{{ signal.fill_price }}</td>
{% if signal.fill_price == "" %}
<td></td>
{% elif signal.order_action=="buy" %}
<td>{{ signal.slip|float|round(3,) }}</td>
{% elif signal.order_action=="sell" %}
<td>{{ -(signal.slip|float|round(3,)) }}</td>
{% if signal.fill_price == None %}
<td></td>
{% else %}
<td></td>
<td>{{ signal.slip|float|round(3,) }} | ${{ (signal.slip|float*signal.order_contracts)|round(1,) }} </td>
{% endif %}
</tr>
{% endfor %}
Expand Down
10 changes: 3 additions & 7 deletions templates/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,10 @@
<td>{{ signal.order_status }}</td>
{% endif %}
<td>{{ signal.fill_price }}</td>
{% if signal.fill_price == "" %}
<td></td>
{% elif signal.order_action=="buy" %}
<td>{{ signal.slip|float|round(3,) }}</td>
{% elif signal.order_action=="sell" %}
<td>{{ -(signal.slip|float|round(3,)) }}</td>
{% if signal.fill_price == None %}
<td></td>
{% else %}
<td></td>
<td>{{ signal.slip|float|round(3,) }} | ${{ (signal.slip|float*signal.order_contracts)|round(1,) }} </td>
{% endif %}
</tr>
{% endfor %}
Expand Down

0 comments on commit b0ba82b

Please sign in to comment.