Skip to content
This repository was archived by the owner on Oct 5, 2021. It is now read-only.

Commit 485e79a

Browse files
committed
working trading on gemini again
1 parent c39a692 commit 485e79a

File tree

6 files changed

+134
-53
lines changed

6 files changed

+134
-53
lines changed

algocoin/__main__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import threading
44
from .custom_strategies import CustomStrategy
55
from .lib.strategies.sma_crosses_strategy import SMACrossesStrategy
6+
from .lib.strategies.test_strat import TestStrategy
67
from .trading import TradingEngine
78
# from .lib.parser import parse_command_line_config, parse_config
89
from .lib.parser import parse_command_line_config
@@ -25,10 +26,12 @@ def main(argv: list) -> None:
2526
application = ServerApplication(te)
2627

2728
# A sample strategy that impelements the correct interface
28-
ts = CustomStrategy(50)
29-
ts2 = SMACrossesStrategy(5, 10)
29+
# ts = CustomStrategy(50)
30+
# ts2 = SMACrossesStrategy(5, 10)
31+
ts = TestStrategy()
3032
te.registerStrategy(ts)
31-
te.registerStrategy(ts2)
33+
# te.registerStrategy(ts)
34+
# te.registerStrategy(ts2)
3235

3336
# for i in [5, 10, 20, 25, 50, 100]:
3437
# for j in [10, 20, 25, 75, 100, 150, 200]:

algocoin/lib/enums.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,6 @@ class OrderSubType(BaseEnum):
7070
class TradeResult(BaseEnum):
7171
NONE = 0
7272
PENDING = 1
73-
REJECTED = 2
73+
PARTIAL = 2
7474
FILLED = 3
75+
REJECTED = 4

algocoin/lib/exchanges/gemini.py

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from websocket import create_connection
33
from ..oe.gemini import GeminiSession
44
from ..config import ExchangeConfig
5-
from ..enums import TradingType, ExchangeType, TickType
5+
from ..enums import TradingType, ExchangeType, TickType, TradeResult
66
from ..exchange import Exchange
77
from ..structs import TradeRequest, TradeResponse, Account
88
from ..utils import get_keys_from_environment, str_to_currency_type
@@ -78,13 +78,76 @@ def buy(self, req: TradeRequest) -> TradeResponse:
7878
'''execute a buy order'''
7979
params = GeminiExchange.trade_req_to_params(req)
8080
log.warn("Buy params: %s", str(params))
81-
return self._client.buy(params)
81+
order = self._client.new_order(params['product_id'],
82+
params['size'],
83+
params['price'],
84+
'buy',
85+
client_order_id=None,
86+
order_execution=None)
87+
# FIXME check these
88+
slippage = float(params['price'])-float(order['avg_execution_price'])
89+
txn_cost = 0.0
90+
status = TradeResult.NONE
91+
92+
if (float(order['executed_amount']) - req.volume) < 0.001:
93+
status = TradeResult.FILLED
94+
elif order.get('is_cancelled', ''):
95+
status = TradeResult.REJECTED
96+
elif float(order.get('remaining_amount', 0.0)):
97+
status = TradeResult.PARTIAL
98+
else:
99+
status = TradeResult.PENDING
100+
101+
resp = TradeResponse(request=req,
102+
side=req.side,
103+
volume=float(order['executed_amount']),
104+
price=float(order['avg_execution_price']),
105+
currency=req.currency,
106+
slippage=slippage,
107+
transaction_cost=txn_cost,
108+
status=status,
109+
order_id=order['order_id'],
110+
remaining=float(order.get('remaining_amount', 0.0)),
111+
)
112+
return resp
82113

83114
def sell(self, req: TradeRequest) -> TradeResponse:
84115
'''execute a sell order'''
85116
params = GeminiExchange.trade_req_to_params(req)
86117
log.warn("Sell params: %s", str(params))
87-
return self._client.sell(params)
118+
order = self._client.new_order(params['product_id'],
119+
params['size'],
120+
params['price'],
121+
'sell',
122+
client_order_id=None,
123+
order_execution=None)
124+
125+
# FIXME check these
126+
slippage = float(params['price'])-float(order['avg_execution_price'])
127+
txn_cost = 0.0
128+
status = TradeResult.NONE
129+
130+
if (float(order['executed_amount']) - req.volume) < 0.001:
131+
status = TradeResult.FILLED
132+
elif order.get('is_cancelled', ''):
133+
status = TradeResult.REJECTED
134+
elif float(order.get('remaining_amount', 0.0)):
135+
status = TradeResult.PARTIAL
136+
else:
137+
status = TradeResult.PENDING
138+
139+
resp = TradeResponse(request=req,
140+
side=req.side,
141+
volume=float(order['executed_amount']),
142+
price=float(order['avg_execution_price']),
143+
currency=req.currency,
144+
slippage=slippage,
145+
transaction_cost=txn_cost,
146+
status=status,
147+
order_id=order['order_id'],
148+
remaining=float(order.get('remaining_amount', 0.0)),
149+
)
150+
return resp
88151

89152
def receive(self) -> None:
90153
jsn = json.loads(self.ws.recv())

algocoin/lib/exchanges/helpers.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,27 @@ def trade_req_to_params(req) -> dict:
136136
p = {}
137137
p['price'] = str(req.price)
138138
p['size'] = str(req.volume)
139-
p['product_id'] = GDAXHelpersMixin.currency_to_string(req.currency)
140-
p['type'] = GDAXHelpersMixin.order_type_to_string(req.order_type)
139+
p['product_id'] = GeminiHelpersMixin.currency_to_string(req.currency)
140+
p['type'] = GeminiHelpersMixin.order_type_to_string(req.order_type)
141141

142142
if req.order_sub_type == OrderSubType.FILL_OR_KILL:
143143
p['time_in_force'] = 'FOK'
144144
elif req.order_sub_type == OrderSubType.POST_ONLY:
145145
p['post_only'] = '1'
146146
return p
147147

148+
@staticmethod
149+
def currency_to_string(cur: CurrencyType) -> str:
150+
if cur == CurrencyType.BTC:
151+
return 'BTCUSD'
152+
153+
@staticmethod
154+
def order_type_to_string(typ: OrderType) -> str:
155+
if typ == OrderType.LIMIT:
156+
return 'limit'
157+
elif typ == OrderType.MARKET:
158+
return 'market'
159+
148160

149161
class ItBitHelpersMixin(object):
150162
@staticmethod

algocoin/lib/oe/gemini.py

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import json
1212
import hmac
1313

14+
1415
class GeminiSession:
1516
"""Defines a Gemini API session.
1617
A session uses one Gemini API key. An account can have multiple sessions.
@@ -27,15 +28,15 @@ def __init__(self, api_key, api_secret, sandbox=False):
2728
self.api_url = 'https://api.sandbox.gemini.com/v1/'
2829

2930
def get_symbols(self):
30-
"""Returns all available symbols for trading."""
31-
try:
31+
"""Returns all available symbols for trading."""
32+
try:
3233
return requests.get(self.api_url + 'symbols').json()
3334
except requests.exceptions.RequestException as e:
3435
raise e
3536

3637
def get_ticker(self, symbol):
3738
"""Returns recent trading activity for a symbol"""
38-
try:
39+
try:
3940
return requests.get(self.api_url + 'pubticker/' + symbol).json()
4041
except requests.exceptions.RequestException as e:
4142
raise e
@@ -45,11 +46,11 @@ def get_current_order_book(self, symbol, limit_bids=None, limit_asks=None):
4546
4647
Args:
4748
symbol (str): Symbol such as btcusd.
48-
limit_bids (int): Optional. Max number of bids (offers to buy) to
49-
return. Default is 50.
49+
limit_bids (int): Optional. Max number of bids (offers to buy) to
50+
return. Default is 50.
5051
limit_asks (int): Optional. Max number of asks (offers to sell) to
5152
return. Default is 50.
52-
53+
5354
Returns:
5455
A JSON object with two arrays, one for bids and one for asks.
5556
"""
@@ -59,19 +60,18 @@ def get_current_order_book(self, symbol, limit_bids=None, limit_asks=None):
5960
limits["limit_bids"] = limit_bids
6061
if limit_asks is not None:
6162
limits["limit_asks"] = limit_asks
62-
63+
6364
try:
6465
return requests.get(self.api_url + symbol, params=limits).json()
6566
except requests.exceptions.RequestException as e:
6667
raise e
6768

68-
def new_order(self, symbol, amount, price, side, client_order_id=None,
69-
order_execution=None):
69+
def new_order(self, symbol, amount, price, side, client_order_id=None, order_execution=None):
7070
"""Place a new order
7171
7272
Args:
7373
symbol (str): Symbol such as btcusd.
74-
amount (str): Decimal amount of BTC to purchase. Note that this
74+
amount (str): Decimal amount of BTC to purchase. Note that this
7575
should be a string.
7676
price (str): Decimal amount of USD to spend per BTC. Note that this
7777
should be a string.
@@ -80,10 +80,10 @@ def new_order(self, symbol, amount, price, side, client_order_id=None,
8080
"maker-or-cancel" and "immediate-or-cancel" are the currently
8181
supported options.
8282
83-
Returns:
83+
Returns:
8484
A JSON object with information about the order.
8585
"""
86-
86+
8787
fields = {
8888
'request': '/v1/order/new',
8989
'nonce': self._nonce(),
@@ -100,16 +100,16 @@ def new_order(self, symbol, amount, price, side, client_order_id=None,
100100

101101
if client_order_id is not None:
102102
fields['client_order_id'] = client_order_id
103-
103+
104104
if order_execution is not None:
105105
fields['order_execution'] = [order_execution]
106-
106+
107107
try:
108-
return requests.post(self.api_url + 'order/new',
109-
headers=self._create_payload(fields)).json()
108+
return requests.post(self.api_url + 'order/new',
109+
headers=self._create_payload(fields)).json()
110110
except requests.exceptions.RequestException as e:
111111
raise e
112-
112+
113113
def cancel_order(self, order_id):
114114
"""Cancels an existing order with the given order_id"""
115115
fields = {
@@ -119,8 +119,8 @@ def cancel_order(self, order_id):
119119
}
120120

121121
try:
122-
return requests.post(self.api_url + 'order/cancel',
123-
headers=self._create_payload(fields)).json()
122+
return requests.post(self.api_url + 'order/cancel',
123+
headers=self._create_payload(fields)).json()
124124
except requests.exceptions.RequestException as e:
125125
raise e
126126

@@ -132,11 +132,11 @@ def cancel_all_session_orders(self):
132132
}
133133

134134
try:
135-
return requests.post(self.api_url + 'order/cancel/session',
136-
headers=self._create_payload(fields)).json()
135+
return requests.post(self.api_url + 'order/cancel/session',
136+
headers=self._create_payload(fields)).json()
137137
except requests.exceptions.RequestException as e:
138138
raise e
139-
139+
140140
def cancel_all_active_orders(self):
141141
"""Cancels all orders across all sessions"""
142142
fields = {
@@ -145,8 +145,8 @@ def cancel_all_active_orders(self):
145145
}
146146

147147
try:
148-
return requests.post(self.api_url + 'order/cancel/all',
149-
headers=self._create_payload(fields)).json()
148+
return requests.post(self.api_url + 'order/cancel/all',
149+
headers=self._create_payload(fields)).json()
150150
except requests.exceptions.RequestException as e:
151151
raise e
152152

@@ -159,8 +159,8 @@ def get_order_status(self, order_id):
159159
}
160160

161161
try:
162-
return requests.post(self.api_url + 'order/status',
163-
headers=self._create_payload(fields)).json()
162+
return requests.post(self.api_url + 'order/status',
163+
headers=self._create_payload(fields)).json()
164164
except requests.exceptions.RequestException as e:
165165
raise e
166166

@@ -172,21 +172,21 @@ def get_all_order_status(self):
172172
}
173173

174174
try:
175-
return requests.post(self.api_url + 'order',
176-
headers=self._create_payload(fields)).json()
175+
return requests.post(self.api_url + 'order',
176+
headers=self._create_payload(fields)).json()
177177
except requests.exceptions.RequestException as e:
178178
raise e
179179

180180
def get_past_trades(self, symbol, limit_trades=None, timestamp=None):
181181
"""Returns information about past trades.
182-
182+
183183
Args:
184-
limit_trades (int): Optional. The max number of trades to return.
184+
limit_trades (int): Optional. The max number of trades to return.
185185
Default is 50, max is 100.
186-
timestamp (int): Optional. Can be provided to only return trades
186+
timestamp (int): Optional. Can be provided to only return trades
187187
after timestamp. Can be in milliseconds or seconds.
188188
189-
Returns:
189+
Returns:
190190
Array of trade information items.
191191
"""
192192

@@ -203,16 +203,16 @@ def get_past_trades(self, symbol, limit_trades=None, timestamp=None):
203203
fields["timestamp"] = timestamp
204204

205205
try:
206-
return requests.post(self.api_url + 'mytrades',
207-
headers=self._create_payload(fields)).json()
206+
return requests.post(self.api_url + 'mytrades',
207+
headers=self._create_payload(fields)).json()
208208
except requests.exceptions.RequestException as e:
209209
raise e
210210

211211
def get_trade_volume(self):
212212
"""Get trade volume information for the account
213213
214-
Returns:
215-
Array where each element contains information about one day of
214+
Returns:
215+
Array where each element contains information about one day of
216216
trading activity.
217217
"""
218218
fields = {
@@ -222,13 +222,13 @@ def get_trade_volume(self):
222222

223223
try:
224224
return requests.post(self.api_url + 'tradevolume',
225-
headers=self._create_payload(fields)).json()
225+
headers=self._create_payload(fields)).json()
226226
except requests.exceptions.RequestException as e:
227227
raise e
228228

229229
def get_balances(self):
230230
"""Get available balances in the supported currencies
231-
231+
232232
Returns:
233233
Array where each element is for a different currency.
234234
"""
@@ -239,12 +239,12 @@ def get_balances(self):
239239

240240
try:
241241
return requests.post(self.api_url + 'balances',
242-
headers=self._create_payload(fields)).json()
242+
headers=self._create_payload(fields)).json()
243243
except requests.exceptions.RequestException as e:
244244
raise e
245245

246246
def heartbeat(self):
247-
"""Prevents a session from timing out if the require heartbeat flag is
247+
"""Prevents a session from timing out if the require heartbeat flag is
248248
set when the API key was provisioned.
249249
"""
250250
fields = {
@@ -254,7 +254,7 @@ def heartbeat(self):
254254

255255
try:
256256
return requests.post(self.api_url + 'heartbeat',
257-
headers=self._create_payload(fields)).json()
257+
headers=self._create_payload(fields)).json()
258258
except requests.exceptions.RequestException as e:
259259
raise e
260260

@@ -269,8 +269,8 @@ def _create_payload(self, fields):
269269
headers = {
270270
'X-GEMINI-APIKEY': self.api_key,
271271
'X-GEMINI-PAYLOAD': encodedFields,
272-
'X-GEMINI-SIGNATURE': hmac.new(self.api_secret.encode(),
273-
encodedFields, digestmod=hashlib.sha384).hexdigest()
272+
'X-GEMINI-SIGNATURE': hmac.new(self.api_secret.encode(),
273+
encodedFields, digestmod=hashlib.sha384).hexdigest()
274274
}
275275

276-
return headers
276+
return headers

0 commit comments

Comments
 (0)