fin 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +26 -0
- data/HISTORY +27 -0
- data/LICENSE +20 -0
- data/README.rdoc +36 -0
- data/Rakefile +25 -0
- data/VERSION +1 -0
- data/features/order_book.feature +9 -0
- data/features/step_definitions/order_book_steps.rb +0 -0
- data/features/support/env.rb +10 -0
- data/features/support/world.rb +12 -0
- data/lib/fin.rb +13 -0
- data/lib/fin/book.rb +50 -0
- data/lib/fin/book_manager.rb +42 -0
- data/lib/fin/changed_list.rb +49 -0
- data/lib/fin/container_list.rb +33 -0
- data/lib/fin/deal_list.rb +18 -0
- data/lib/fin/indexed_list.rb +74 -0
- data/lib/fin/models/deal.rb +75 -0
- data/lib/fin/models/instrument.rb +78 -0
- data/lib/fin/models/model.rb +39 -0
- data/lib/fin/models/money_limit.rb +81 -0
- data/lib/fin/models/order.rb +45 -0
- data/lib/fin/models/position.rb +57 -0
- data/lib/fin/order_list.rb +17 -0
- data/lib/legacy.rb +443 -0
- data/lib/version.rb +8 -0
- data/spec/fin/book_spec.rb +215 -0
- data/spec/fin/changed_list_spec.rb +16 -0
- data/spec/fin/container_list_spec.rb +63 -0
- data/spec/fin/deal_list_spec.rb +102 -0
- data/spec/fin/indexed_list_spec.rb +20 -0
- data/spec/fin/models/deal_spec.rb +140 -0
- data/spec/fin/models/instrument_spec.rb +54 -0
- data/spec/fin/models/model_spec.rb +109 -0
- data/spec/fin/models/money_limit_spec.rb +143 -0
- data/spec/fin/models/order_spec.rb +67 -0
- data/spec/fin/models/position_spec.rb +74 -0
- data/spec/fin/models/shared_examples.rb +5 -0
- data/spec/fin/order_list_spec.rb +140 -0
- data/spec/fin/shared_examples.rb +355 -0
- data/spec/spec_helper.rb +17 -0
- data/tasks/common.rake +18 -0
- data/tasks/doc.rake +14 -0
- data/tasks/gem.rake +40 -0
- data/tasks/git.rake +34 -0
- data/tasks/spec.rake +16 -0
- data/tasks/version.rake +71 -0
- metadata +155 -0
data/.gitignore
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
## MAC OS
|
2
|
+
.DS_Store
|
3
|
+
|
4
|
+
## TEXTMATE
|
5
|
+
*.tmproj
|
6
|
+
tmtags
|
7
|
+
|
8
|
+
## EMACS
|
9
|
+
*~
|
10
|
+
\#*
|
11
|
+
.\#*
|
12
|
+
|
13
|
+
## VIM
|
14
|
+
*.swp
|
15
|
+
|
16
|
+
## PROJECT::GENERAL
|
17
|
+
coverage
|
18
|
+
config
|
19
|
+
rdoc
|
20
|
+
pkg
|
21
|
+
log
|
22
|
+
tmp
|
23
|
+
.idea
|
24
|
+
.bundle
|
25
|
+
|
26
|
+
## PROJECT::SPECIFIC
|
data/HISTORY
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
== 0.0.0 / 2011-03-18
|
2
|
+
|
3
|
+
* Birthday!
|
4
|
+
|
5
|
+
== 0.0.1 / 2011-03-18
|
6
|
+
|
7
|
+
* Structure created
|
8
|
+
|
9
|
+
== 0.0.2 / 2011-03-19
|
10
|
+
|
11
|
+
* Namespace renamed to Orders
|
12
|
+
|
13
|
+
== 0.0.3 / 2011-03-19
|
14
|
+
|
15
|
+
* Feature-parity with legacy code
|
16
|
+
|
17
|
+
== 0.0.4 / 2011-03-21
|
18
|
+
|
19
|
+
* Item#to_s added
|
20
|
+
|
21
|
+
== 0.0.5 / 2011-03-21
|
22
|
+
|
23
|
+
* Iteration-safety added
|
24
|
+
|
25
|
+
== 0.1.0 / 2011-04-10
|
26
|
+
|
27
|
+
* Namespace renamed to Fin
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Arvicco
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= fin
|
2
|
+
by:: Arvicco
|
3
|
+
url:: http://github.com/arvicco/fin
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Domain models to represent Financial/DataFeed entities and basic data structures used
|
8
|
+
to store and access them. Initial project scope is only limited to representation of
|
9
|
+
RTS Plaza2 domain models. Next step will be generalization of domain models to encompass
|
10
|
+
other markets and other data feed types.
|
11
|
+
|
12
|
+
== FEATURES/PROBLEMS:
|
13
|
+
|
14
|
+
Domain models represent distinct individual entities that appear in market data feeds
|
15
|
+
(such as Quote, Order, Deal, Instrument, Position and whatnot). Intention is to keep
|
16
|
+
model API very similar to ActiveModel, with a goal of full ActiveModel compatibility
|
17
|
+
in the near future.
|
18
|
+
|
19
|
+
Enclosed data structures (Lists and such) are effectively containers for domain objects.
|
20
|
+
They are used to accumulate, analyse and visualize sets of domain objects, received from
|
21
|
+
market data feeds (such as RTS Plaza2 replication streams). These containers are required
|
22
|
+
by client and gateway components (data adapters) that deal with actual market data feeds
|
23
|
+
(DataStreams). In addition, they may be used downstream by intermediary and endpoint
|
24
|
+
data analysers for storage and retrieval of domain objects passing through them.
|
25
|
+
|
26
|
+
Ideally, container data structures need to be:
|
27
|
+
1) memory-efficient
|
28
|
+
2) thread-safe (without too much synchronization penalty)
|
29
|
+
3) iteration-safe (adding new element while iterating should not raise exception)
|
30
|
+
|
31
|
+
== INSTALL:
|
32
|
+
|
33
|
+
$ sudo gem install fin
|
34
|
+
|
35
|
+
== LICENSE:
|
36
|
+
Copyright (c) 2011 Arvicco. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
begin
|
2
|
+
require 'rake'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'rake', '~> 0.8.3.1'
|
6
|
+
require 'rake'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
BASE_PATH = Pathname.new(__FILE__).dirname
|
12
|
+
LIB_PATH = BASE_PATH + 'lib'
|
13
|
+
PKG_PATH = BASE_PATH + 'pkg'
|
14
|
+
DOC_PATH = BASE_PATH + 'rdoc'
|
15
|
+
|
16
|
+
$LOAD_PATH.unshift LIB_PATH.to_s
|
17
|
+
require 'version'
|
18
|
+
|
19
|
+
NAME = 'fin'
|
20
|
+
CLASS_NAME = Fin
|
21
|
+
|
22
|
+
# Load rakefile tasks
|
23
|
+
Dir['tasks/*.rake'].sort.each { |file| load file }
|
24
|
+
|
25
|
+
# Project-specific tasks
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
File without changes
|
data/lib/fin.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'version'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler/setup'
|
4
|
+
Bundler.require(:default)
|
5
|
+
|
6
|
+
require 'fin/container_list'
|
7
|
+
require 'fin/order_list'
|
8
|
+
require 'fin/deal_list'
|
9
|
+
require 'fin/models/deal'
|
10
|
+
require 'fin/models/order'
|
11
|
+
require 'fin/models/instrument'
|
12
|
+
require 'fin/models/position'
|
13
|
+
require 'fin/models/money_limit'
|
data/lib/fin/book.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fin/container_list'
|
2
|
+
|
3
|
+
module Fin
|
4
|
+
# Represents Book (OrderBook, DealBook, etc...) for one security(isin)
|
5
|
+
# It is used as additional index by BookedList subclass (OrderList, DealList)
|
6
|
+
class Book < ContainerList
|
7
|
+
|
8
|
+
attr_reader :isin_id
|
9
|
+
alias isin isin_id
|
10
|
+
|
11
|
+
def initialize opts = {}
|
12
|
+
@isin_id = opts[:isin_id]
|
13
|
+
@book_index = opts[:book_index]
|
14
|
+
@book_condition = opts[:book_condition]
|
15
|
+
raise "No isin_id given for #{self}" unless @isin_id
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
# Validation of the item being included
|
20
|
+
def check item
|
21
|
+
if item.is_a?(@item_type) && item.isin_id == isin_id
|
22
|
+
@book_condition ? @book_condition.call(item) : true
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def index item
|
29
|
+
if @book_index
|
30
|
+
@book_index.call(item)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def add? item
|
37
|
+
if super
|
38
|
+
item.book = self
|
39
|
+
item
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove? item
|
44
|
+
if super
|
45
|
+
item.book = nil
|
46
|
+
item
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'fin/book'
|
2
|
+
require 'fin/container_list'
|
3
|
+
|
4
|
+
module Fin
|
5
|
+
# Adds to including List a set of @books, each related to a single security(isin).
|
6
|
+
# @books is effectively an additional index in a List, a set of containers for
|
7
|
+
# its items, grouped by item's isin_id.
|
8
|
+
# For example: OrderBook, DealBook
|
9
|
+
#
|
10
|
+
module BookManager
|
11
|
+
|
12
|
+
def books
|
13
|
+
@books ||= Hash.new do |hash, key|
|
14
|
+
hash[key] = Book.new :isin_id => key,
|
15
|
+
:item_type => @item_type,
|
16
|
+
:book_index => @book_index,
|
17
|
+
:book_condition => @book_condition
|
18
|
+
hash[key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Overwrites/removes existing item with the same index
|
23
|
+
def add? item
|
24
|
+
if check item
|
25
|
+
old_item = self[index item]
|
26
|
+
remove old_item if old_item # Remove old item with the same index(id)
|
27
|
+
if super
|
28
|
+
books[item.isin_id].add item # Add item to appropriate order book
|
29
|
+
item
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def remove? item
|
35
|
+
if super
|
36
|
+
# Removing item from appropriate order book when it's deleted from order list
|
37
|
+
books[item.isin_id].remove item
|
38
|
+
item
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'fin/indexed_list'
|
2
|
+
|
3
|
+
module Fin
|
4
|
+
# Represents IndexedList that tracks its own changes (dirty-tracking),
|
5
|
+
# and updates (series of changes resulting in a consistent state)
|
6
|
+
class ChangedList < IndexedList
|
7
|
+
|
8
|
+
# Changed attribute is set automatically, whenever item is actually added
|
9
|
+
# or deleted from list (please note, change may leave data in inconsistent state!)
|
10
|
+
attr_accessor :changed
|
11
|
+
|
12
|
+
# Number of changes (successful item additions or removals)
|
13
|
+
attr_accessor :change_count
|
14
|
+
|
15
|
+
# Updated attribute should be set EXTERNALLY - only when data update
|
16
|
+
# transaction is completed, and list data is known to be consistent
|
17
|
+
# (e.g., when onStreamDataEnd event fires for DataStream)
|
18
|
+
attr_accessor :updated
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@updated = true
|
22
|
+
@changed = true
|
23
|
+
@change_count = 0
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
def add? item
|
28
|
+
if super
|
29
|
+
@changed = true # Mark List as changed
|
30
|
+
@change_count += 1
|
31
|
+
item
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove? item
|
36
|
+
if super
|
37
|
+
@changed = true # Mark List as changed
|
38
|
+
@change_count += 1
|
39
|
+
item
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Observers inform list that it's recent changes are reflected
|
44
|
+
def update_noted
|
45
|
+
@updated = false
|
46
|
+
@changed = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fin/changed_list'
|
2
|
+
|
3
|
+
module Fin
|
4
|
+
# Represents Container that holds Model objects (items) of only ONE specific type
|
5
|
+
# TODO: In future, merge ChangedList and ContainerList to flatten class hierarchy
|
6
|
+
class ContainerList < ChangedList
|
7
|
+
|
8
|
+
attr_reader :item_type
|
9
|
+
|
10
|
+
def initialize opts = {}
|
11
|
+
@item_type = opts[:item_type]
|
12
|
+
raise "Item type not given for #{self}" unless @item_type
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
def check item
|
17
|
+
item.is_a?(@item_type) && item.index
|
18
|
+
end
|
19
|
+
|
20
|
+
def index item
|
21
|
+
item.index if check item
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_record rec
|
25
|
+
add? @item_type.from_record(rec)
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_record rec, id
|
29
|
+
index = @item_type.index_for rec
|
30
|
+
remove? self[index]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'fin/models/deal'
|
2
|
+
require 'fin/book_manager'
|
3
|
+
|
4
|
+
module Fin
|
5
|
+
# Represents list of ALL Orders, indexed by id (replId)
|
6
|
+
# Its @books is a set of OrderBooks by isin. Each OrderBook lists Orders by price.
|
7
|
+
class DealList < ContainerList
|
8
|
+
|
9
|
+
include BookManager
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super :item_type => Fin::Deal
|
13
|
+
@book_index = proc { |item| item.deal_id }
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Fin
|
2
|
+
# Abstract (equivalent of SortedList)
|
3
|
+
# ������� ����� "������������� ������"
|
4
|
+
class IndexedList < Hash
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@iteration_mutex = Mutex.new
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns default list index for items
|
12
|
+
def index item
|
13
|
+
item.object_id
|
14
|
+
end
|
15
|
+
|
16
|
+
# Determines if item is worthy to be included in the list
|
17
|
+
def check item
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Adds new item to the list if it passes check
|
22
|
+
# (replaces item with the same index)
|
23
|
+
def add? item
|
24
|
+
# Checks that the item given looks like proper item first
|
25
|
+
self[index item] = item if check item
|
26
|
+
end
|
27
|
+
|
28
|
+
# Adds new item to the list, returning self for easy chaining
|
29
|
+
def add item
|
30
|
+
add? item
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
alias << add
|
35
|
+
|
36
|
+
# Removes item from the list, returns nil if nothing removed
|
37
|
+
def remove? item
|
38
|
+
# Checks that the item given looks like proper item first
|
39
|
+
delete(index(item)) if check item
|
40
|
+
end
|
41
|
+
|
42
|
+
# Removes item from the list
|
43
|
+
def remove item
|
44
|
+
remove? item
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
# Removes item with given index from the list
|
49
|
+
def delete_by_index index
|
50
|
+
remove self[index] if self[index]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Removes either all items, or items for which given block returns trueish value
|
54
|
+
def clear
|
55
|
+
if block_given?
|
56
|
+
each { |item| remove item if yield item }
|
57
|
+
else
|
58
|
+
each { |item| remove item }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Yields list items (but NOT keys!) in order of their index
|
63
|
+
def each
|
64
|
+
if block_given?
|
65
|
+
keys.sort.each { |key| yield self[key] }
|
66
|
+
else
|
67
|
+
keys.sort.map { |key| self[key] }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Make direct setter private
|
72
|
+
private :[]=
|
73
|
+
end
|
74
|
+
end
|