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.
Files changed (48) hide show
  1. data/.gitignore +26 -0
  2. data/HISTORY +27 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +36 -0
  5. data/Rakefile +25 -0
  6. data/VERSION +1 -0
  7. data/features/order_book.feature +9 -0
  8. data/features/step_definitions/order_book_steps.rb +0 -0
  9. data/features/support/env.rb +10 -0
  10. data/features/support/world.rb +12 -0
  11. data/lib/fin.rb +13 -0
  12. data/lib/fin/book.rb +50 -0
  13. data/lib/fin/book_manager.rb +42 -0
  14. data/lib/fin/changed_list.rb +49 -0
  15. data/lib/fin/container_list.rb +33 -0
  16. data/lib/fin/deal_list.rb +18 -0
  17. data/lib/fin/indexed_list.rb +74 -0
  18. data/lib/fin/models/deal.rb +75 -0
  19. data/lib/fin/models/instrument.rb +78 -0
  20. data/lib/fin/models/model.rb +39 -0
  21. data/lib/fin/models/money_limit.rb +81 -0
  22. data/lib/fin/models/order.rb +45 -0
  23. data/lib/fin/models/position.rb +57 -0
  24. data/lib/fin/order_list.rb +17 -0
  25. data/lib/legacy.rb +443 -0
  26. data/lib/version.rb +8 -0
  27. data/spec/fin/book_spec.rb +215 -0
  28. data/spec/fin/changed_list_spec.rb +16 -0
  29. data/spec/fin/container_list_spec.rb +63 -0
  30. data/spec/fin/deal_list_spec.rb +102 -0
  31. data/spec/fin/indexed_list_spec.rb +20 -0
  32. data/spec/fin/models/deal_spec.rb +140 -0
  33. data/spec/fin/models/instrument_spec.rb +54 -0
  34. data/spec/fin/models/model_spec.rb +109 -0
  35. data/spec/fin/models/money_limit_spec.rb +143 -0
  36. data/spec/fin/models/order_spec.rb +67 -0
  37. data/spec/fin/models/position_spec.rb +74 -0
  38. data/spec/fin/models/shared_examples.rb +5 -0
  39. data/spec/fin/order_list_spec.rb +140 -0
  40. data/spec/fin/shared_examples.rb +355 -0
  41. data/spec/spec_helper.rb +17 -0
  42. data/tasks/common.rake +18 -0
  43. data/tasks/doc.rake +14 -0
  44. data/tasks/gem.rake +40 -0
  45. data/tasks/git.rake +34 -0
  46. data/tasks/spec.rake +16 -0
  47. data/tasks/version.rake +71 -0
  48. metadata +155 -0
@@ -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.
@@ -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.
@@ -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
@@ -0,0 +1,9 @@
1
+ Feature: something something
2
+ In order to something something
3
+ A user something something
4
+ something something something
5
+
6
+ Scenario: something something
7
+ Given inspiration
8
+ When I create a sweet new gem
9
+ Then everyone should see how awesome I am
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+
3
+ require 'pathname'
4
+ require 'bundler'
5
+ Bundler.setup
6
+ Bundler.require :cucumber
7
+
8
+ require 'order_book'
9
+
10
+ BASE_PATH = Pathname.new(__FILE__).dirname + '../..'
@@ -0,0 +1,12 @@
1
+ module SystemHelper
2
+
3
+ def windows?
4
+ RUBY_PLATFORM =~ /mswin|windows|mingw/ || cygwin?
5
+ end
6
+
7
+ def cygwin?
8
+ RUBY_PLATFORM =~ /cygwin/
9
+ end
10
+ end
11
+
12
+ World(WinGui, SystemHelper)
@@ -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'
@@ -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