chock_a_block 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/chock_a_block/stock.rb +2 -2
- data/spec/cassettes/ChockABlock/_start_level/prints_errors_about_invalid_API_KEY.yml +41 -0
- data/spec/cassettes/ChockABlock/_start_level/starts_new_AlgoTrader.yml +56 -0
- data/spec/cassettes/ChockABlock_Stock/_best_bidding/retries_again.yml +137 -0
- data/spec/cassettes/ChockABlock_Stock/_best_bidding/return_best_bid.yml +79 -0
- data/spec/cassettes/ChockABlock_Stock/_get_orderbook/raises_StockNotFoundError.yml +38 -0
- data/spec/cassettes/ChockABlock_Stock/_get_orderbook/raises_VenueNotFoundError.yml +38 -0
- data/spec/cassettes/ChockABlock_Stock/_get_orderbook/return_JSON_Hash_of_the_order_book.yml +78 -0
- data/spec/cassettes/ChockABlock_Stock/_get_quote/raises_StockNotFoundError.yml +38 -0
- data/spec/cassettes/ChockABlock_Stock/_get_quote/return_JSON_Hash_of_the_quote.yml +53 -0
- data/spec/chock_a_block/stock_spec.rb +56 -0
- data/spec/chock_a_block_spec.rb +24 -4
- data/spec/spec_helper.rb +9 -0
- metadata +11 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c31a5f8019fffb3347eca48196a97cfece3af3f
|
4
|
+
data.tar.gz: 685411389aaabeef552b82fae307c1881e2ecdf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e5ffb538e95e55736417106d08580fb2f97d44f990553db2760595599cef41a6bf76a1469385525f9d6209e822a95750addb9ce3ec40d02c23c588067bef561
|
7
|
+
data.tar.gz: cb4c8e087f44f7424116025b38e1e274b4e2e560cd4b968337e244fd7899de5a9f97797e228e725111aa3961b62ba4e0be8e0d3580846c6370fb8dbab83fc4b7
|
data/lib/chock_a_block/stock.rb
CHANGED
@@ -37,8 +37,8 @@ module ChockABlock
|
|
37
37
|
# @return [Hash] representing the best current price/qty offered by bots
|
38
38
|
# or retry if best_bid is nil
|
39
39
|
def best_bidding
|
40
|
-
|
41
|
-
best_bid =
|
40
|
+
order_book = get_orderbook
|
41
|
+
best_bid = order_book['bids']&.last
|
42
42
|
|
43
43
|
return best_bid unless best_bid.nil?
|
44
44
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://www.stockfighter.io/gm/levels/chock_a_block
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
X-Starfighter-Authorization:
|
11
|
+
- API_KEY
|
12
|
+
Connection:
|
13
|
+
- close
|
14
|
+
Host:
|
15
|
+
- www.stockfighter.io
|
16
|
+
User-Agent:
|
17
|
+
- http.rb/1.0.2
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Server:
|
24
|
+
- nginx/1.8.0
|
25
|
+
Date:
|
26
|
+
- Sun, 21 Feb 2016 19:56:11 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Content-Length:
|
30
|
+
- '144'
|
31
|
+
Connection:
|
32
|
+
- close
|
33
|
+
X-Upstream-Debugging:
|
34
|
+
- 172.31.134.156:8181
|
35
|
+
body:
|
36
|
+
encoding: ASCII-8BIT
|
37
|
+
string: '{"ok":false,"error":"Auth/auth failed: %!(EXTRA *errors.errorString=Couldn''t
|
38
|
+
find that apiKey, service reports: No fighter with that API key.)"}'
|
39
|
+
http_version:
|
40
|
+
recorded_at: Sun, 21 Feb 2016 19:53:57 GMT
|
41
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,56 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://www.stockfighter.io/gm/levels/chock_a_block
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
X-Starfighter-Authorization:
|
11
|
+
- API_KEY
|
12
|
+
Connection:
|
13
|
+
- close
|
14
|
+
Host:
|
15
|
+
- www.stockfighter.io
|
16
|
+
User-Agent:
|
17
|
+
- http.rb/1.0.2
|
18
|
+
response:
|
19
|
+
status:
|
20
|
+
code: 200
|
21
|
+
message: OK
|
22
|
+
headers:
|
23
|
+
Server:
|
24
|
+
- nginx/1.8.0
|
25
|
+
Date:
|
26
|
+
- Sun, 21 Feb 2016 20:00:57 GMT
|
27
|
+
Content-Type:
|
28
|
+
- application/json
|
29
|
+
Transfer-Encoding:
|
30
|
+
- chunked
|
31
|
+
Connection:
|
32
|
+
- close
|
33
|
+
X-Upstream-Debugging:
|
34
|
+
- 172.31.134.156:8181
|
35
|
+
body:
|
36
|
+
encoding: ASCII-8BIT
|
37
|
+
string: |-
|
38
|
+
{
|
39
|
+
"ok": true,
|
40
|
+
"instanceId": 23585,
|
41
|
+
"account": "HAE78360086",
|
42
|
+
"instructions": {
|
43
|
+
"Instructions": "# Moving up in the world!\n\n**Reminder: These instructions are always available under the Instructions tab if you need to see them again.** \n\nAfter your impressive performance at your last job, you've moved on to Hollings \u0026 Eakins. Warning: **the gloves are off, now**.\nHere is **account HAE78360086** and a mandate from one of our clients, a pension fund: they want you to purchase them 100,000 shares of \nRockDeathBlossomify (*RDB*). (Their asset manager thinks the company will have market-beating performance over the next\nfew years. Maybe they're right, maybe they're not, but either way we get paid and I'm investing my bonus in cat food futures.)\n\nThis is called a **block trade** -- 100,000 shares is a lot of RDB to buy all at once, so the presence of someone wanting to\ndo it suggests that someone who is smart and rich has better-than-market information about the stock. Executing block trades\nis one of the core problems of trading.\n\nWhy? Here's one of the reasons we on the sell-side make the big bucks: the client could try to buy these shares themselves,\n but if they just did the obvious thing and put in an order for 100,000 shares, they'd get shellacked.\n\nWhy? Supply and demand: sudden demand for a product with limited supply, like liquid shares of a stock, makes the market \nclearing price go up. Also, supply and demand: if you supply a sucker who broadcasts his intentions loudly, capitalism demands that the rest of\nthe market eat the sucker alive. So instead you've got to be sneaky-sneaky about how you accumulate those 100,000 shares.\n\nLuckily, this market maker is dumb as a box of rocks. They don't seem to remember prior orders well at all. You could probably \nget the full allocation done just by calling them up all day and ordering a few shares here and a few shares there... \nbut I hear you're a programmer, and the stock exchange has [an API](https://starfighter.readme.io), so why do anything \nmanual and repetitive when you have a for loop available?\n\n## Your Goal\n* Purchase 100,000 shares of RDB.\n* Avoid causing a \"price impact\" -- our client wants these shares for the cheapest price possible, naturally.\n\n## You'll Be Fired If\n\n* You cause an excessive price impact in the stock. What's \"excessive\"? It's up to the risk desk. \n* You take too long. The client doesn't need their shares *this instant* but they want them *quickly*.\n* The market moves against you. What, that's unfair? Welcome to Wall Street, kid.\n\n### A Note From Starfighter:\n\nWe abstract the notion of time in Stockfighter. On this level, one trading day in the simulated world corresponds to about five\nseconds of wall-clock time. In real life, the market generally has off periods between trading days, but most of our levels don't --\nour bots don't need sleep. Well, OK, technically speaking they do sleep() while waiting on you... like right now, because you're\nreading while you could be trading. Get coding!\n",
|
44
|
+
"Market Makers": "A **market maker** is a market participant (sometimes a formal role, often not) who provides *liquidity* in a stock, by being willing\nto buy the stock or sell the stock, substantially all of the time. The market maker seeks to make a small amount of money on\na large amount of transactions by, on average, selling for slightly more than their buying price. The difference between the price\nthat they're willing to buy at and the price that they're willing to sell at (at any given moment) is called \"the *spread*\".\n\nMarket makers are crucial to the financial markets. A market without liquidity is a sucky market. The market for houses has very\nlow liquidity: if you want to sell your house on Tuesday, it is unlikely that anyone wants to buy *exactly* your house on *exactly*\nTuesday, so you'd probably get a very, very poor price for it. Similarly, if you wanted to buy *exactly* your dream house on *exactly*\nTuesday, you'd probably have to pay a ridiculous premium to the \"fair value\" of the house.\n\nImagine a world where there was someone you would call, name any address in the country, and immediately receive a buying\nprice and a selling price for that house. That person would be a market maker. They would make it much, much less difficult\nfor you to get into or out of your desired exposure to a house on Tuesday.\n\nMarket makers are the counter-parties to *most* transactions on the stock exchange. That sounds unlikely, but it is true: it is\nhighly unlikely that a pension fund seeking to unload 100,000 shares of Google executes that trade at *the exact instant* where\na stadium full of mom-and-pop investors are seeking to buy 2~5 shares of Google apiece.\n\n## What Do Market Makers Not Like?\n\nMarket makers don't like **inventory risk**. They get paid to take on a small, short-term risk of being long (holding stock) or \nshort (borrowing stock) in the stock they're making a market for, with the intention of quickly having an order in the opposite\ndirection get them out of that position.\n\nIt is dangerous for a market maker to trade with *informed order flow*, i.e. someone with better-than-market understanding of\nthe near term future for a stock. Someone who is trading a block (a lot of stock at once) is automatically informed order flow,\nsince they know that their current transaction for buying 100 shares is going to be followed not by a 50/50 chance of a sell but\nrather with a buy, a buy, another buy, some more buys, followed by more buying.\n\nIf a market maker continues to allow informed order flow to trade with them, they may find themselves with a larger position than\nthey anticipated at a price which is expensive to get out of, given the present state of the market.\n\n## How Do Market Makers Deal With This?\n\n1. *Profile incoming orders.* Market makers, beginning with humans with \"a sense of the flow of the market\" and continuing to\nHFT algorithms in the present day, are in a constant cat-and-mouse game with informed order flow. They'd prefer to transact\nminimally (if at all) with informed orders.\n\n2. *Hedge.* More sophisticated strategies are available after a market maker has multiple instruments they can trade in. For\nexample, they can hedge a long position in a stock with options which synthetically cancel out some features of the position.\nThey could also e.g. hedge a position in a particular stock with an offsetting position in a separate stock believe to be highly\ncorrelated with the original stock (e.g. if you're long Toyota, sell Honda), with indexes/exchange traded funds, or with other\nderivatives.\n\n## How Do I Make Markets?\n\nBuy low. Sell high. Trade frequently.\n\n## No, Really?\n\nObserve what the provided market makers do, in particular how they quote spreads and periodically cancel orders when\nthe market moves in either direction. The provided market makers are as unsophisticated as they can be and possibly work\n(n.b. under 100 lines of Ruby code). You should be able to easily exploit their blind spots. \n\nA freebie, since **people who\nactually read docs should be rewarded**: our least sophisticated market maker has no memory of closed orders and looks\nat the order book every N seconds as if the universe had just sprung into being. That probably suggests at least one way\nto trick it into doing what you want. \n\nBonus points: if what you do to get what you want doesn't leave a signature in the order\nbook, it will *never* catch on. (Note that a dumb market maker with smart participants next to it can function as a smart system\neven if they're not intentionally colluding, however. One of the core insights of markets in general is that they quickly converge\non solutions which are better than individual participants would have come up with. A market maker can surf on the market,\nwhich converges to a solution to your manipulation, without itself understanding that your manipulation even exists.)\n\nIn general, you want to \n\n1. quote a tighter spread (if the bot would quote $10 / $10.50, and you quote $10.05 / $10.45, you'll\nget the fills rather than the bot)\n2. attempt to profile incoming orders, for example by keeping state of your own recent orders and whether you appear to be\nhaving many similar ones happen in the same direction\n3. cancel and re-file your bids/asks when you develop inventory in either direction, potentially at a less favorable price if you're\nscared\n4. look for patterns in the market and in behavior of other participants. Patterns which suggest anomalous behavior, which\nroutinely divulge that an order is from a sophisticated or unsophisticated source, or which allow you to predict the near-term\nbehavior of another participant are *very valuable indeed.*\n\n## Stockfighter Notes\n\nTake a look at the API docs for viewing an order's fills or the details of an execution. Note that we don't expose any identifying\ninformation about your counterparty. Now, try to think of creative ways that one can \"cheat\" the non-availability of that information.\n"
|
45
|
+
},
|
46
|
+
"tickers": [
|
47
|
+
"STK"
|
48
|
+
],
|
49
|
+
"venues": [
|
50
|
+
"VENUE"
|
51
|
+
],
|
52
|
+
"secondsPerTradingDay": 5
|
53
|
+
}
|
54
|
+
http_version:
|
55
|
+
recorded_at: Sun, 21 Feb 2016 19:58:43 GMT
|
56
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,137 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 21:15:56 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json
|
27
|
+
Content-Length:
|
28
|
+
- '358'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
Strict-Transport-Security:
|
32
|
+
- max-age=31536000; includeSubdomains
|
33
|
+
body:
|
34
|
+
encoding: ASCII-8BIT
|
35
|
+
string: |-
|
36
|
+
{
|
37
|
+
"ok": true,
|
38
|
+
"venue": "VENUE",
|
39
|
+
"symbol": "STK",
|
40
|
+
"ts": "2016-02-21T21:14:04.301520932Z",
|
41
|
+
"bids": null,
|
42
|
+
"asks": [
|
43
|
+
{
|
44
|
+
"price": 10403,
|
45
|
+
"qty": 11898,
|
46
|
+
"isBuy": false
|
47
|
+
},
|
48
|
+
{
|
49
|
+
"price": 10454,
|
50
|
+
"qty": 11898,
|
51
|
+
"isBuy": false
|
52
|
+
},
|
53
|
+
{
|
54
|
+
"price": 10505,
|
55
|
+
"qty": 11898,
|
56
|
+
"isBuy": false
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
http_version:
|
61
|
+
recorded_at: Sun, 21 Feb 2016 21:13:42 GMT
|
62
|
+
- request:
|
63
|
+
method: get
|
64
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
65
|
+
body:
|
66
|
+
encoding: US-ASCII
|
67
|
+
string: ''
|
68
|
+
headers:
|
69
|
+
Connection:
|
70
|
+
- close
|
71
|
+
Host:
|
72
|
+
- api.stockfighter.io
|
73
|
+
User-Agent:
|
74
|
+
- http.rb/1.0.2
|
75
|
+
response:
|
76
|
+
status:
|
77
|
+
code: 200
|
78
|
+
message: OK
|
79
|
+
headers:
|
80
|
+
Server:
|
81
|
+
- nginx/1.8.0
|
82
|
+
Date:
|
83
|
+
- Sun, 21 Feb 2016 21:15:58 GMT
|
84
|
+
Content-Type:
|
85
|
+
- application/json
|
86
|
+
Content-Length:
|
87
|
+
- '579'
|
88
|
+
Connection:
|
89
|
+
- close
|
90
|
+
Strict-Transport-Security:
|
91
|
+
- max-age=31536000; includeSubdomains
|
92
|
+
body:
|
93
|
+
encoding: ASCII-8BIT
|
94
|
+
string: |-
|
95
|
+
{
|
96
|
+
"ok": true,
|
97
|
+
"venue": "VENUE",
|
98
|
+
"symbol": "STK",
|
99
|
+
"ts": "2016-02-21T21:14:07.142397315Z",
|
100
|
+
"bids": [
|
101
|
+
{
|
102
|
+
"price": 9891,
|
103
|
+
"qty": 13078,
|
104
|
+
"isBuy": true
|
105
|
+
},
|
106
|
+
{
|
107
|
+
"price": 9842,
|
108
|
+
"qty": 13078,
|
109
|
+
"isBuy": true
|
110
|
+
},
|
111
|
+
{
|
112
|
+
"price": 9793,
|
113
|
+
"qty": 13078,
|
114
|
+
"isBuy": true
|
115
|
+
}
|
116
|
+
],
|
117
|
+
"asks": [
|
118
|
+
{
|
119
|
+
"price": 9989,
|
120
|
+
"qty": 11921,
|
121
|
+
"isBuy": false
|
122
|
+
},
|
123
|
+
{
|
124
|
+
"price": 10038,
|
125
|
+
"qty": 11873,
|
126
|
+
"isBuy": false
|
127
|
+
},
|
128
|
+
{
|
129
|
+
"price": 10087,
|
130
|
+
"qty": 11873,
|
131
|
+
"isBuy": false
|
132
|
+
}
|
133
|
+
]
|
134
|
+
}
|
135
|
+
http_version:
|
136
|
+
recorded_at: Sun, 21 Feb 2016 21:13:44 GMT
|
137
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,79 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 20:56:13 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json
|
27
|
+
Content-Length:
|
28
|
+
- '574'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
Strict-Transport-Security:
|
32
|
+
- max-age=31536000; includeSubdomains
|
33
|
+
body:
|
34
|
+
encoding: ASCII-8BIT
|
35
|
+
string: |-
|
36
|
+
{
|
37
|
+
"ok": true,
|
38
|
+
"venue": "VENUE",
|
39
|
+
"symbol": "STK",
|
40
|
+
"ts": "2016-02-21T20:54:21.828875543Z",
|
41
|
+
"bids": [
|
42
|
+
{
|
43
|
+
"price": 2357,
|
44
|
+
"qty": 15434,
|
45
|
+
"isBuy": true
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"price": 2346,
|
49
|
+
"qty": 15434,
|
50
|
+
"isBuy": true
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"price": 2335,
|
54
|
+
"qty": 15434,
|
55
|
+
"isBuy": true
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"asks": [
|
59
|
+
{
|
60
|
+
"price": 2437,
|
61
|
+
"qty": 9066,
|
62
|
+
"isBuy": false
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"price": 2449,
|
66
|
+
"qty": 9565,
|
67
|
+
"isBuy": false
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"price": 2461,
|
71
|
+
"qty": 9565,
|
72
|
+
"isBuy": false
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
http_version:
|
77
|
+
recorded_at: Sun, 21 Feb 2016 20:53:59 GMT
|
78
|
+
recorded_with: VCR 3.0.1
|
79
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 500
|
19
|
+
message: Internal Server Error
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 20:47:12 GMT
|
25
|
+
Content-Type:
|
26
|
+
- text/plain; charset=utf-8
|
27
|
+
Content-Length:
|
28
|
+
- '65'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
body:
|
32
|
+
encoding: UTF-8
|
33
|
+
string: '{"ok":false,"error":"symbol STK does not exist on venue VENUE"}
|
34
|
+
|
35
|
+
'
|
36
|
+
http_version:
|
37
|
+
recorded_at: Sun, 21 Feb 2016 20:44:58 GMT
|
38
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 404
|
19
|
+
message: Not Found
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 20:45:37 GMT
|
25
|
+
Content-Type:
|
26
|
+
- text/plain; charset=utf-8
|
27
|
+
Content-Length:
|
28
|
+
- '61'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
body:
|
32
|
+
encoding: UTF-8
|
33
|
+
string: '{"ok":false,"error":"No venue exists with the symbol VENUE"}
|
34
|
+
|
35
|
+
'
|
36
|
+
http_version:
|
37
|
+
recorded_at: Sun, 21 Feb 2016 20:43:23 GMT
|
38
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,78 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 20:56:13 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json
|
27
|
+
Content-Length:
|
28
|
+
- '574'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
Strict-Transport-Security:
|
32
|
+
- max-age=31536000; includeSubdomains
|
33
|
+
body:
|
34
|
+
encoding: ASCII-8BIT
|
35
|
+
string: |-
|
36
|
+
{
|
37
|
+
"ok": true,
|
38
|
+
"venue": "VENUE",
|
39
|
+
"symbol": "STK",
|
40
|
+
"ts": "2016-02-21T20:54:21.828875543Z",
|
41
|
+
"bids": [
|
42
|
+
{
|
43
|
+
"price": 2357,
|
44
|
+
"qty": 15434,
|
45
|
+
"isBuy": true
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"price": 2346,
|
49
|
+
"qty": 15434,
|
50
|
+
"isBuy": true
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"price": 2335,
|
54
|
+
"qty": 15434,
|
55
|
+
"isBuy": true
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"asks": [
|
59
|
+
{
|
60
|
+
"price": 2437,
|
61
|
+
"qty": 9066,
|
62
|
+
"isBuy": false
|
63
|
+
},
|
64
|
+
{
|
65
|
+
"price": 2449,
|
66
|
+
"qty": 9565,
|
67
|
+
"isBuy": false
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"price": 2461,
|
71
|
+
"qty": 9565,
|
72
|
+
"isBuy": false
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
http_version:
|
77
|
+
recorded_at: Sun, 21 Feb 2016 20:53:59 GMT
|
78
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,38 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK/quote
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 404
|
19
|
+
message: Not Found
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 21:01:41 GMT
|
25
|
+
Content-Type:
|
26
|
+
- text/plain; charset=utf-8
|
27
|
+
Content-Length:
|
28
|
+
- '61'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
body:
|
32
|
+
encoding: UTF-8
|
33
|
+
string: '{"ok":false,"error":"No venue exists with the symbol VENUE"}
|
34
|
+
|
35
|
+
'
|
36
|
+
http_version:
|
37
|
+
recorded_at: Sun, 21 Feb 2016 20:59:27 GMT
|
38
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,53 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.stockfighter.io/ob/api/venues/VENUE/stocks/STK/quote
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Connection:
|
11
|
+
- close
|
12
|
+
Host:
|
13
|
+
- api.stockfighter.io
|
14
|
+
User-Agent:
|
15
|
+
- http.rb/1.0.2
|
16
|
+
response:
|
17
|
+
status:
|
18
|
+
code: 200
|
19
|
+
message: OK
|
20
|
+
headers:
|
21
|
+
Server:
|
22
|
+
- nginx/1.8.0
|
23
|
+
Date:
|
24
|
+
- Sun, 21 Feb 2016 21:02:32 GMT
|
25
|
+
Content-Type:
|
26
|
+
- application/json
|
27
|
+
Content-Length:
|
28
|
+
- '300'
|
29
|
+
Connection:
|
30
|
+
- close
|
31
|
+
Strict-Transport-Security:
|
32
|
+
- max-age=31536000; includeSubdomains
|
33
|
+
body:
|
34
|
+
encoding: ASCII-8BIT
|
35
|
+
string: |-
|
36
|
+
{
|
37
|
+
"ok": true,
|
38
|
+
"symbol": "STK",
|
39
|
+
"venue": "VENUE",
|
40
|
+
"bid": 2630,
|
41
|
+
"ask": 2656,
|
42
|
+
"bidSize": 14749,
|
43
|
+
"askSize": 9912,
|
44
|
+
"bidDepth": 44263,
|
45
|
+
"askDepth": 30412,
|
46
|
+
"last": 2656,
|
47
|
+
"lastSize": 338,
|
48
|
+
"lastTrade": "2016-02-21T21:00:36.605689657Z",
|
49
|
+
"quoteTime": "2016-02-21T21:00:36.605987278Z"
|
50
|
+
}
|
51
|
+
http_version:
|
52
|
+
recorded_at: Sun, 21 Feb 2016 21:00:18 GMT
|
53
|
+
recorded_with: VCR 3.0.1
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ChockABlock::Stock do
|
4
|
+
subject { ChockABlock::Stock.new('VENUE', 'STK') }
|
5
|
+
|
6
|
+
describe '#get_orderbook' do
|
7
|
+
it 'raises VenueNotFoundError', :vcr do
|
8
|
+
expect{ subject.get_orderbook }.to raise_error ChockABlock::VenueNotFoundError
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'raises StockNotFoundError', :vcr do
|
12
|
+
expect{ subject.get_orderbook }.to raise_error ChockABlock::StockNotFoundError
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'return JSON Hash of the order_book', :vcr do
|
16
|
+
order_book = subject.get_orderbook
|
17
|
+
expect(order_book).to be_a Hash
|
18
|
+
expect(order_book.keys).to\
|
19
|
+
include 'ok', 'venue', 'symbol', 'ts', 'bids', 'asks'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#get_quote' do
|
24
|
+
it 'raises StockNotFoundError', :vcr do
|
25
|
+
expect{ subject.get_quote }.to raise_error ChockABlock::StockNotFoundError
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'return JSON Hash of the quote', :vcr do
|
29
|
+
quote = subject.get_quote
|
30
|
+
expect(quote).to be_a Hash
|
31
|
+
expect(quote.keys).to\
|
32
|
+
include'ok', 'symbol', 'venue', 'bid', 'ask', 'bidSize', 'askSize',
|
33
|
+
'bidDepth', 'askDepth', 'last', 'lastSize', 'lastTrade', 'quoteTime'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#best_bidding' do
|
38
|
+
it 'return best_bid', :vcr do
|
39
|
+
best_bid = subject.best_bidding
|
40
|
+
|
41
|
+
expect(best_bid['price']).to be_an Integer
|
42
|
+
expect(best_bid['qty']).to be_an Integer
|
43
|
+
expect(best_bid['isBuy']).to be true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'retries again', :vcr do
|
47
|
+
expect_any_instance_of(Object).to receive(:sleep).with(2)
|
48
|
+
expect(subject).to receive(:best_bidding).twice.and_call_original
|
49
|
+
best_bid = subject.best_bidding
|
50
|
+
|
51
|
+
expect(best_bid['price']).to be_an Integer
|
52
|
+
expect(best_bid['qty']).to be_an Integer
|
53
|
+
expect(best_bid['isBuy']).to be true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/spec/chock_a_block_spec.rb
CHANGED
@@ -1,11 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe ChockABlock do
|
4
|
-
it '
|
5
|
-
expect(ChockABlock::
|
4
|
+
it 'BASE_URL' do
|
5
|
+
expect(ChockABlock::BASE_URL).not_to be_nil
|
6
6
|
end
|
7
7
|
|
8
|
-
it '
|
9
|
-
expect(
|
8
|
+
it 'GAME_URL' do
|
9
|
+
expect(ChockABlock::GAME_URL).not_to be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.start_level' do
|
13
|
+
it 'prints errors about invalid API_KEY', :vcr do
|
14
|
+
expect { ChockABlock.start_level('API_KEY') }.to output(
|
15
|
+
"\"Invalid API_KEY API_KEY\"\n" +
|
16
|
+
"\"Please use a correct API_KEY to start the level\"\n"
|
17
|
+
).to_stdout
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'starts new AlgoTrader', :vcr do
|
21
|
+
expect_any_instance_of(ChockABlock::AlgoTrader).to receive(:basic_strategy)
|
22
|
+
expect { ChockABlock.start_level('API_KEY') }.to output(
|
23
|
+
"\"Game Started\"\n" +
|
24
|
+
"\"Please Navigate to:\"\n" +
|
25
|
+
"\" https://www.stockfighter.io/ui/play/blotter#chock_a_block\"\n" +
|
26
|
+
"\"to see the game in action\"\n" +
|
27
|
+
"\"--------------------------------------------------------\"\n"
|
28
|
+
).to_stdout
|
29
|
+
end
|
10
30
|
end
|
11
31
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,2 +1,11 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'chock_a_block'
|
3
|
+
require 'vcr'
|
4
|
+
require 'webmock/rspec'
|
5
|
+
|
6
|
+
VCR.configure do |c|
|
7
|
+
c.cassette_library_dir = 'spec/cassettes'
|
8
|
+
c.hook_into :webmock
|
9
|
+
c.allow_http_connections_when_no_cassette = true
|
10
|
+
c.configure_rspec_metadata!
|
11
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chock_a_block
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmed Abdel-Razzak
|
@@ -80,20 +80,6 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: yard
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: pry-byebug
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -134,6 +120,16 @@ files:
|
|
134
120
|
- lib/chock_a_block/broker.rb
|
135
121
|
- lib/chock_a_block/errors.rb
|
136
122
|
- lib/chock_a_block/stock.rb
|
123
|
+
- spec/cassettes/ChockABlock/_start_level/prints_errors_about_invalid_API_KEY.yml
|
124
|
+
- spec/cassettes/ChockABlock/_start_level/starts_new_AlgoTrader.yml
|
125
|
+
- spec/cassettes/ChockABlock_Stock/_best_bidding/retries_again.yml
|
126
|
+
- spec/cassettes/ChockABlock_Stock/_best_bidding/return_best_bid.yml
|
127
|
+
- spec/cassettes/ChockABlock_Stock/_get_orderbook/raises_StockNotFoundError.yml
|
128
|
+
- spec/cassettes/ChockABlock_Stock/_get_orderbook/raises_VenueNotFoundError.yml
|
129
|
+
- spec/cassettes/ChockABlock_Stock/_get_orderbook/return_JSON_Hash_of_the_order_book.yml
|
130
|
+
- spec/cassettes/ChockABlock_Stock/_get_quote/raises_StockNotFoundError.yml
|
131
|
+
- spec/cassettes/ChockABlock_Stock/_get_quote/return_JSON_Hash_of_the_quote.yml
|
132
|
+
- spec/chock_a_block/stock_spec.rb
|
137
133
|
- spec/chock_a_block_spec.rb
|
138
134
|
- spec/spec_helper.rb
|
139
135
|
homepage: https://github.com/artmees/chock_a_block
|
@@ -161,4 +157,3 @@ signing_key:
|
|
161
157
|
specification_version: 4
|
162
158
|
summary: Solution to chock_a_block
|
163
159
|
test_files: []
|
164
|
-
has_rdoc:
|