-
Notifications
You must be signed in to change notification settings - Fork 0
/
_ranger.py
269 lines (226 loc) · 9.16 KB
/
_ranger.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
"""
The range trading bot would work on price swings in a preset range
IMPORTANT: You have to configure it to what you believe the trading corridor is!
DISCLAIMER: all losses or harm resulting from the use of this code is YOUR SOLE responsibility
2013-12-12 piramida, based on balancer bot by prof7bit
goxtool is property of prof7bit
"""
from datetime import datetime
import strategy
import time
FIAT_COLD = 0 # FIAT which will always be left in the account
BTC_COLD = 0.4 # BTC which will always be left in the account
RANGE_MIN = 900 # minimum possible price - at this price, we would be all BTC
RANGE_MAX = 1100 # maximum possible price - at this price, we would be all FIAT
PERCENT_STEP = 2 # each level is this much % above the next; add a prime to not hit walls
MARKER = 9 # lowest digit of price to identify bot's own orders
COIN = 1E8 # number of satoshi per coin, this is a constant.
VERSION = 1.0 # version of the bot
def add_marker(price, marker):
"""encode a marker in the price value to find bot's own orders"""
return price / 10 * 10 + marker
def has_marker(price, marker):
"""return true if the price value has the marker"""
return (price % 10) == marker
def mark_own(price):
"""return the price with our own marker embedded"""
return add_marker(price, MARKER)
def is_own(price):
"""return true if this price has our own marker"""
return has_marker(price, MARKER)
class Strategy(strategy.Strategy):
"""a range trading bot"""
_levels = [] # store price levels
def __init__(self, gox):
strategy.Strategy.__init__(self, gox)
self.temp_halt = False
def slot_keypress(self, gox, (key)):
"""a key has been pressed"""
if key == ord("c"):
# cancel existing orders and suspend trading
self.debug("canceling all orders")
self.temp_halt = True
self.cancel_orders()
if key == ord("p"):
# create the initial orders and start trading.
# market order at current price.
self.debug("adding 6 orders around current price and enabling trading")
self.temp_halt = False
self.place_all_orders()
if key == ord("i"):
# print some information into the log file about
# current trading level
try:
level = self.closest_level()
except IndexError:
level = 0
self.debug("Closest level: %f [%d]" % (
gox.quote2float(self.levels[level]), level))
# self.debug([self.gox.quote2float(x) for x in self.levels])
def cancel_orders(self):
"""cancel all trading orders, we identify
them through the marker in the price value"""
must_cancel = []
for order in self.gox.orderbook.owns:
if is_own(order.price):
must_cancel.append(order)
for order in must_cancel:
self.gox.cancel(order.oid)
@property
def price_now(self):
if self.gox.orderbook.bid == 0 or self.gox.orderbook.ask == 0:
return 0
return (self.gox.orderbook.bid + self.gox.orderbook.ask) / 2
@property
def total_fiat_now(self):
""" total fiat at curr market price """
fiat = self.gox.quote2float(self.fiat_now)
btc = self.gox.base2float(self.btc_now)
price = self.gox.quote2float(self.price_now)
return (fiat -FIAT_COLD) + (btc - BTC_COLD) * price
@property
def fiat_now(self):
tmp_fiat = self.gox.wallet[self.gox.curr_quote]-(FIAT_COLD*1E5)
if tmp_fiat >0:
return tmp_fiat
else:
return 0
@property
def btc_now(self):
tmp_btc = self.gox.wallet[self.gox.curr_base] - BTC_COLD*COIN
if tmp_btc > 0:
return tmp_btc
else:
return 0
@property
def levels(self):
""" list of prices where we would trade """
if not self._levels:
self._levels = []
val = self.gox.quote2int(RANGE_MIN)
while val < self.gox.quote2int(RANGE_MAX):
self._levels.append(mark_own(val))
val = int(val * (1.0 + PERCENT_STEP / 100.0))
return self._levels
def closest_level(self, price=None):
""" return a trade level closest to the current price """
if not price:
price = self.price_now
if price == 0:
return -1 # not yet initialized to have correct price
lvl = self.levels
return min(range(len(lvl)), key=lambda i: abs(lvl[i]-price))
def sell_amount(self, price):
#self.debug("muh macht die kuh hier mit btc_now %s und fiat_now %s" %(self.btc_now,self.fiat_now))
""" how much to sell, in gox btc - our btc divided by number of steps left"""
idx = self.closest_level()
ratio = sum([1.0 * price / x for x in self.levels[idx+1:]])
if ratio == 0:
return -1
return int(self.btc_now / ratio)
def buy_amount(self, price):
""" how much to buy, in gox btc - our fiat divided by number of steps left"""
idx = self.closest_level()
if idx < 1:
return -1
#self.debug(self.fiat_now)
return self.gox.base2int(1.0 * (self.fiat_now) / idx / price)
def place_all_orders(self):
""" set initial orders at all levels; currently limited to 6 (GOX timelimit) """
cur_lvl = self.closest_level()
if cur_lvl == -1:
return
start_idx = cur_lvl > 3 and cur_lvl - 3 or 0
end_idx = (cur_lvl < len(self.levels) - 3) and cur_lvl + 4 or len(self.levels)
for idx in range(start_idx, end_idx):
if idx == cur_lvl:
continue
if idx > cur_lvl:
self.place_level_order(idx, True)
else:
self.place_level_order(idx, False)
def place_orders(self):
"""place two orders above and below current level """
idx = self.closest_level()
if idx == -1:
return False
op = None
if self.place_level_order(idx + 1, True):
op = "BOUGHT"
if self.place_level_order(idx - 1, False):
op = "SOLD"
if op:
self.debug("*** %s at %d (%d) $%d: %s" % (
op,
int(self.gox.quote2float(self.levels[idx])),
idx,
int(self.total_fiat_now),
datetime.now()
))
def place_level_order(self, idx, is_sale):
""" Set an order at the specified level """
if idx > len(self.levels) or idx < 0:
self.debug("!!! DONE creating orders since hit the border of range [%s-%s]" % (RANGE_MIN, RANGE_MAX))
self.temp_halt = True
return False # done trading
if self.find_level_in_orderbook(idx):
return False # already set, ignore
price = self.levels[idx]
if is_sale:
amount = self.sell_amount(price)
if amount < 0.01 * COIN:
self.debug("*** ERR not enough BTC to sell(%s)! Halting trading" %(self.gox.base2float(amount)))
self.temp_halt = True
return False
op = "ask"
self.gox.sell(price, amount)
else:
amount = self.buy_amount(price)
if amount < 0.01 * COIN:
self.debug("*** ERR not enough fiat to buy(%s)! Halting trading" %(self.gox.base2float(amount)))
self.temp_halt = True
return False
op = "bid"
self.gox.buy(price, amount)
self.debug("*** new %s %f at %f (%d)" % (
op,
self.gox.base2float(amount),
self.gox.quote2float(price),
idx
))
return True
def slot_trade(self, gox, (date, price, volume, typ, own)):
"""a trade message has been receivd"""
# not interested in other people's trades
if not own:
return
# not interested in manually entered (not bot) trades
if not is_own(price):
return
self.check_trades()
def slot_owns_changed(self, orderbook, _dummy):
"""status or amount of own open orders has changed"""
self.check_trades()
def find_level_in_orderbook(self, level):
""" returns true if the level is filled with some kind of order """
if level < 0:
return True
price = self.levels[level]
for order in self.gox.orderbook.owns:
#self.debug("volume %s" %(order.volume))
if order.price == price and order.volume > 0.00000001 * COIN:
return True
# No Matches
return False
def check_trades(self):
"""find out if we need to place new orders and do it if neccesary"""
# bot temporarily disabled
if self.temp_halt:
return
# still waiting for submitted orders,
# can wait for next signal
if self.gox.count_submitted:
return
if not self.find_level_in_orderbook(self.closest_level()):
# not found our order in the orderbook - try setting the orders!
self.place_orders()