Skip to content

Commit 5de08e1

Browse files
author
Jakub
committed
first commit to Gringotts
0 parents  commit 5de08e1

File tree

13 files changed

+317
-0
lines changed

13 files changed

+317
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
env/
2+
.idea/

gringotts/__init__.py

Whitespace-only changes.

gringotts/datastore/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from gringotts.datastore.account import Account
2+
from gringotts.datastore.amount import Amount
3+
from gringotts.datastore.balancesheet import BalanceSheet
4+
from gringotts.datastore.datastore import DataStore
5+
from gringotts.datastore.order import Order
6+
from gringotts.datastore.orderbook import OrderBook
7+
from gringotts.datastore.ratio import Ratio

gringotts/datastore/account.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import copy
2+
import uuid
3+
4+
import attr
5+
6+
from gringotts.datastore import Amount, Order
7+
8+
9+
class UnsupportedCurrencyError(Exception):
10+
pass
11+
12+
13+
class InsufficientFundsError(Exception):
14+
pass
15+
16+
17+
class OrderDoesNotExistError(Exception):
18+
pass
19+
20+
21+
@attr.s
22+
class AccountSnapshot:
23+
uuid = attr.ib(type=str)
24+
orders = attr.ib(type=dict)
25+
balances = attr.ib(type=dict)
26+
27+
@attr.s
28+
class Account:
29+
supported_currencies = {'BTC', 'USD'}
30+
31+
uuid = attr.ib(factory=uuid.uuid4)
32+
open_orders = attr.ib(factory=dict)
33+
balances = attr.ib(factory=dict)
34+
35+
def currency_valid(self, currency):
36+
if currency not in self.supported_currencies:
37+
raise UnsupportedCurrencyError(str(currency))
38+
39+
def get_balance(self, currency):
40+
self.currency_valid(currency=currency)
41+
42+
if self.balances.get(currency):
43+
return copy.deepcopy(self.balances[currency])
44+
else:
45+
return Amount.zero
46+
47+
def credit(self, currency, amount):
48+
balance = self.get_balance(currency=currency)
49+
self.balances[currency] = balance.add(amount=amount)
50+
return self.get_balance(currency=currency)
51+
52+
def debit(self, currency, amount):
53+
balance = self.get_balance(currency=currency)
54+
self.balances[currency] = balance.subtract(amount=amount)
55+
return self.get_balance(currency=currency)
56+
57+
def get_open_orders(self):
58+
return self.open_orders
59+
60+
def create_order(self, offered_currency, offered_amount, received_currency, received_amount):
61+
if self.get_balance(currency=offered_currency) > offered_amount:
62+
raise InsufficientFundsError(f'offered_currency: {str(offered_currency)} compared to {str(offered_amount)}')
63+
64+
self.debit(currency=offered_currency,
65+
amount=offered_amount)
66+
order = Order(offered_currency=offered_currency,
67+
offered_amount=offered_amount,
68+
received_currency=received_currency,
69+
received_amount=received_amount)
70+
71+
self.open_orders[order.uuid] = order
72+
73+
return order
74+
75+
def fill_order(self, order):
76+
if not self.open_orders.get(order):
77+
raise OrderDoesNotExistError(order.uuid)
78+
79+
self.credit(currency=order.received_currency,
80+
amount=order.received_amount)
81+
self.open_orders.pop(order.uuid, None)
82+
83+
def split_prder(self, order, amount):
84+
if not self.open_orders.get(order.uuid):
85+
raise OrderDoesNotExistError(order.uuid)
86+
87+
filled, remaining = order.split(amount=amount)
88+
self.fill_order(order=filled)
89+
self.open_orders[remaining.uuid] = remaining
90+
91+
return filled, remaining
92+
93+
def cancel_order(self, order):
94+
if not self.open_orders.get(order):
95+
raise OrderDoesNotExistError(order.uuid)
96+
97+
self.credit(currency=order.offered_currency, amount=order.offered_amount)
98+
self.open_orders.pop(order.uuid, None)
99+
100+
def create_snapshot(self):
101+
orders_snap = dict()
102+
for name, order in self.open_orders:
103+
orders_snap[name] = order.create_snapshot()
104+
105+
balances_snap = dict()
106+
for name, balance in self.balances:
107+
balances_snap[name] = str(balance)
108+
109+
snap = AccountSnapshot(uuid=str(self.uuid),
110+
orders=orders_snap,
111+
balances=balances_snap)
112+
113+
return attr.asdict(snap)
114+
115+
@staticmethod
116+
def load_snapshot(data):
117+
account = Account(data['uuid'])
118+
for name, order_snap in data['orders']:
119+
account.open_orders[name] = Order.load_snapshot(order_snap)
120+
for name, balance_snap in data['balances']:
121+
account.balances[name] = Amount(int(balance_snap))
122+
123+
return account

gringotts/datastore/amount.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import attr
2+
import copy
3+
4+
5+
@attr.s(init=False)
6+
class Amount(int):
7+
8+
def __init__(self, num):
9+
super().__init__(num)
10+
11+
def clone(self):
12+
return copy.deepcopy(self)
13+
14+
def create_snapshot(self):
15+
return str(self)
16+
17+
@staticmethod
18+
def load_snapshot(data):
19+
return Amount(data)
20+
21+
@staticmethod
22+
def zero():
23+
return Amount(0)
24+
25+
@staticmethod
26+
def one():
27+
return Amount(1)

gringotts/datastore/balancesheet.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import attr
2+
3+
from gringotts.datastore import Account
4+
5+
6+
class AccountNotFound(Exception):
7+
pass
8+
9+
10+
@attr.s
11+
class BalanceSheet:
12+
accounts = attr.ib(factory=dict)
13+
14+
def get_account(self, name):
15+
account = self.accounts.get(name)
16+
if not account:
17+
raise AccountNotFound(str(name))
18+
return account
19+
20+
def create_snapshot(self):
21+
snapshot = dict()
22+
23+
for name, account in self.accounts.items():
24+
snapshot[name] = account.create_snapshot()
25+
26+
return {'accounts': snapshot}
27+
28+
@staticmethod
29+
def load_snapshot(snap):
30+
bs = BalanceSheet()
31+
for name, snap_acc in snap.accounts:
32+
bs.accounts[name] = Account.load_snapshot(snap_acc)
33+
34+
return bs

gringotts/datastore/datastore.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import attr
2+
3+
from gringotts.datastore import BalanceSheet
4+
5+
6+
@attr.s
7+
class DataStore:
8+
balance_sheet = attr.ib(type=BalanceSheet)

gringotts/datastore/order.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import uuid
2+
import attr
3+
4+
5+
@attr.s
6+
class Order:
7+
offered_currency = attr.ib()
8+
offered_amount = attr.ib()
9+
received_currency = attr.ib()
10+
received_amount = attr.ib()
11+
12+
uuid = attr.ib(factory=uuid.uuid4)
13+
14+
def split(self, amount):
15+
16+
filled = Order()
17+
18+
remaining = Order()
19+
20+
return filled, remaining
21+
22+
def create_snapshot(self):
23+
return attr.asdict(self)
24+
25+
@staticmethod
26+
def load_snapshot(self, snap):
27+
return Order(uuid=snap['uuid']) # TODO

gringotts/datastore/orderbook.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import attr
2+
import copy
3+
4+
5+
@attr.s
6+
class BookStore:
7+
pass
8+
9+
10+
11+
@attr.s
12+
class OrderBook:
13+
store = attr.ib(factory=BookStore)
14+
15+
def fill_orders_with(self, order):
16+
orig_order = order
17+
order = copy.deepcopy(order)
18+
19+
closed = []
20+
amount_filled = Ratio()

gringotts/datastore/ratio.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Ratio:
2+
pass

0 commit comments

Comments
 (0)