-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclassdef.py
110 lines (82 loc) · 3.49 KB
/
classdef.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
class Market():
"""Process and store share orders."""
def __init__(self):
"""Define internal structures for object instance."""
# The total shares available for each side of the ledger, bid "B"
# and sale "S"
self.shares = dict(B=0, S=0)
# The running ledger of buy and sell order details
self.orders = dict()
def add(self, side, order_id, price, size):
"""Ingest a new buy or sell order.
:param side: Either bid "B" or sell "S"
:param order_id: Unique order identifier
:param price: Price offered or asked for a share
:param size: Number of shares
:type side: str
:type order_id: str
:type price: float
:type size: int
"""
# Add the order details as an n-tuple to the ledger dictionary
self.orders[order_id] = (price, size, side)
# Increase the shares available on the appropriate side
self.shares[side] += size
def reduce(self, order_id, size):
"""Reduce shares from an existing order.
:param order_id: Unique order identifier
:param size: Number of shares
:type order_id: str
:type size: int
"""
# Decrease the number of shares available on the appropriate side.
# If an order is missing due to an earlier input message error,
# the reduce order will fail gracefully.
try:
side = self.orders.get(order_id)[2]
self.shares[side] -= size
except TypeError as e:
return
# Remove the add order if the reduction order is the same size
if self.orders.get(order_id)[1] <= size:
self.orders.pop(order_id, None)
# Decrease the share size of the add order if reduce order is smaller
else:
self.orders[order_id] = (self.orders.get(order_id)[0],
self.orders.get(order_id)[1] - size,
self.orders.get(order_id)[2])
def trade(self, target, action):
"""Calculate the expense or income incurred when buying or
selling a specified number of shares.
:param target: Number of shares to trade
:param action: Type of trade to calculate, 'buy' or 'sell'
:return total: Calculated expense or income
:type target: int
:type action: str
:rtype total: float
.. note::
orders is a dictionary of tuples in the form,
{order_id: (price, size, side), ...}
here it is reduced to sorted ledger side in the form,
column = [(price, size), ...]
"""
total = 0
column = list()
# Build a list of (price, size) tuples for the specified ledger side
order_details = self.orders.values()
side = 'S' if action == 'buy' else 'B'
for order in order_details:
if order[2] == side:
column.append((order[0], order[1]))
# Sort appropriatly, buy low and sell high
reverse_switch = False if action == 'buy' else True
# Loop over sorted orders until target shares aquired
for order in sorted(column, reverse=reverse_switch):
available_shares = order[1]
price = order[0]
debit_shares = min(available_shares, target)
total = round(total + (price * debit_shares), 2)
target = target - debit_shares
if target == 0:
break
return total