-
Notifications
You must be signed in to change notification settings - Fork 2
/
shared.coffee
347 lines (255 loc) · 7.85 KB
/
shared.coffee
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
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
if !module?
module = {}
is_server = ->
global? && !bus?.remote
module.exports =
# because statebus orphans nested, unkeyed objects on save
refresh: (pos, trade) ->
console.assert pos.key
if pos.entry && trade.entry
pos.entry
else if pos.exit
pos.exit
else
trade
get_settings: (name) ->
get_settings.cache ||= {}
if name of get_settings.cache
return get_settings.cache[name]
if is_server() && name[0] == '/'
name = deslash name
if name == 'all' || deslash(name) in get_strategies()
get_settings.cache[name] = null
else if bus.fetch(name).settings?
settings = bus.fetch(name).settings
get_settings.cache[name] = Object.freeze(bus.clone(settings))
get_settings.cache[name]
get_strategies: ->
key = if is_server() then 'operation' else '/operation'
operation = bus.fetch(key)
strategies = []
for name, strategy of operation when name != 'key'
strategies.push name
strategies
get_dealers: (load_from_cache) ->
if get_dealers.cache?
return get_dealers.cache.slice()
key = if is_server() then 'operation' else '/operation'
operation = if load_from_cache then from_cache(key) else bus.fetch key
loaded = Object.keys(operation).length > 1
dealers = []
for name, strategy of operation when name != 'key'
for dealer in strategy.dealers
settings = get_settings(dealer)
loaded &&= settings?
if settings? && !settings.series
dealer = if is_server() then deslash(dealer) else dealer
console.assert dealer != 'all',
message: 'NO WAY!'
name: name
strategy: strategy
dealer: dealer
dealers.push dealer
if loaded
get_dealers.cache = dealers
dealers.slice()
get_series: (load_from_cache) ->
if get_series.cache?
return get_series.cache.slice()
key = if is_server() then 'operation' else '/operation'
operation = if load_from_cache then from_cache(key) else bus.fetch key
loaded = Object.keys(operation).length > 1
series = []
for name, strategy of operation when name != 'key'
for dealer in strategy.dealers
settings = get_settings(dealer)
loaded &&= settings?
if settings?.series
dealer = if is_server() then deslash(dealer) else dealer
series.push dealer #from_cache(s).parent
if loaded
get_series.cache = series
series.slice()
get_all_actors: (load_from_cache) ->
if get_all_actors.cache?
return get_all_actors.cache.slice()
dealers = get_dealers(load_from_cache)
series = get_series(load_from_cache)
actors = dealers.slice().concat(series)
if get_series.cache? && get_dealers.cache?
get_all_actors.cache = actors
actors.slice()
dealer_params: (name) ->
dealer_params.cache ||= {}
if name of dealer_params.cache
return dealer_params.cache[name]
parts = name.split('&')
params = {}
for p in (parts or [])
param = p.split('=')
params[param[0]] = param[1]
dex = params
dealer_params.cache[name] = params
dex
dealer_name: (name) ->
dealer_name.cache ||= {}
key = name
if key of dealer_name.cache
dealer_name.cache[key]
else if name == 'all'
dealer_name.cache.all = 'all'
'all'
else
params_cache[name] ?= name.split('&')
parts = params_cache[name]
all_params = {}
for dealer in get_dealers()
for param, val of dealer_params(dealer)
all_params[param] ?= {}
all_params[param][val] = true
differentiating_params = ( "#{param}=#{val}" for param, val of (dealer_params[name] or {}) \
when Object.keys(all_params[param] or {}).length > 1 )
name = differentiating_params.join(' ')
dealer_name.cache[key] = name
name
extend: (obj) ->
obj ||= {}
for arg, idx in arguments
if idx > 0
for own name,s of arg
if !obj[name]? || obj[name] != s
obj[name] = s
obj
defaults: (o) ->
obj = {}
for arg, idx in arguments by -1
for own name,s of arg
obj[name] = s
extend(o, obj)
uniq: (ar) ->
ops = {}
for e in ar
key = e.key or JSON.stringify(e)
return false if key of ops
ops[key] = 1
true
make_global: (exports) ->
top = if window? then window else global
for k,v of exports
top[k] = v
now: -> Math.floor((new Date()).getTime() / 1000)
get_buy: (pos) ->
if pos.entry.type == 'buy' then pos.entry else pos.exit
get_sell: (pos) ->
if pos.entry.type == 'buy' then pos.exit else pos.entry
get_name: (base, params, differentiating_params) ->
if !is_server() && base[0] != '/'
name = "/#{base}"
else
name = base
for k,v of params
if differentiating_params && k not of differentiating_params
continue
if Math.is_num v
if Math.abs(v) < 1
v *= 100
name += "&#{k}=#{v}"
else
if v.constructor == Array || v.constructor == Object
v = JSON.stringify(v)
name += "&#{k}=#{v}"
name
from_cache: (key) ->
if is_server() && key[0] == '/'
key = deslash key
bus.cache[key] || bus.fetch[key] || {key}
deslash: (key) ->
if key?[0] == '/'
key = key.substr(1)
else
key
wait_for_bus: (cb) ->
if !bus?
setTimeout ->
wait_for_bus(cb)
else
cb()
get_ip_address: ->
ifaces = require('os').networkInterfaces()
address = null
for i, details of ifaces
for detail in details
if detail.family == 'IPv4' && detail.internal == false
address = detail.address
address or "localhost"
module.exports.make_global module.exports
extend Math,
is_num: (n) ->
if n[0] == '$'
n = n.substring(1)
f = parseFloat n
!isNaN(parseFloat(f)) && isFinite(f) && (n.indexOf && n.indexOf(' ') == -1)
to_precision: (num, precision) ->
precision ||= 1
parseFloat(num.toPrecision(precision))
summation: (data) ->
data.reduce((sum, value) ->
sum + value
, 0)
standard_dev: (values) ->
avg = Math.average values
variance = 0
for val in values
variance += (val - avg) * (val - avg)
variance /= values.length
Math.sqrt variance
average: (data) ->
sum = data.reduce((sum, value) ->
sum + value
, 0)
avg = sum / data.length
avg
weighted_average: (current_val, previous_val, alpha) ->
alpha ||= .5
if !previous_val? || previous_val == null
current_val
else if current_val == null
previous_val
else
alpha * current_val + (1 - alpha) * previous_val
smma: (series, idx) ->
if idx == series.length - 1
series[idx]
else
series[idx] + (1 - 1 / series.length) * Math.smma(series, idx + 1)
derivative: (cur, prev) ->
return 0 if !prev? || prev == null
cur - prev
median: (values, already_sorted) ->
if !already_sorted
values.sort (a, b) ->
a - b
half = Math.floor(values.length / 2)
if values.length % 2
values[half]
else
(values[half - 1] + values[half]) / 2.0
quartiles: (data) ->
data.sort (a,b) -> a - b
m = Math.median data, true
split = Math.floor(data.length / 2)
more = data.slice split, data.length
less = data.slice 0, split
data =
q1: if less.length == 0 then m else Math.median(less, true)
q2: m
q3: if more.length == 0 then m else Math.median(more, true)
max: data[data.length - 1]
min: data[0]
data
greatest_common_divisor: (nums) ->
gcd2 = (a,b) -> if !b then a else gcd2(b, a % b)
result = nums[0]
for num,idx in nums when idx > 0
result = gcd2 result, num
result