fin 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,75 @@
1
+ require 'fin/models/model'
2
+
3
+ module Fin
4
+ # Represents a single deal (trade) for one security
5
+ # Source table: FORTS_FUTTRADE_REPL::deal � c�����
6
+ #
7
+ class Deal < Model
8
+
9
+ # Properties as per P2ClientGate API
10
+ prop_accessor [:repl_id, :id], [:repl_rev, :rev],
11
+ [:isin_id, :isin], # i4 ���������� �������� ������������� �����������
12
+ :price, # price d16.5 ����
13
+ :amount, # amount i4 �����, ���-�� ������ �����������
14
+ [:deal_id, :id_deal], # id_deal i8 ����� ������
15
+ [:sess_id, :session_id], # sess_id i4 ������������� ������
16
+ :moment, # moment t ����� ���������� ������.
17
+ :pos, # i4 ���-�� ������� �� ����������� �� ����� ����� ������.
18
+ :nosystem, # i1 1-������������ ������, 0-�������
19
+ [:repo_id, :repo, :id_repo], # i8 ����� ������ ����� ������ ����
20
+ :id_deal_multileg # i8 ����� ������ �� ������
21
+
22
+ # ��� ���� ����������� ������ ��� ����� ������:
23
+ prop_accessor :code_sell, # c7 ��� ��������:status_sell,
24
+ :id_ord_sell, # i8 ����� ������ ��������
25
+ :ext_id_sell, # i4 ������� ����� �� ������ ��������
26
+ :comment_sell, # c20 ����������� �� ������ ��������.
27
+ :trust_sell, # i1 ������� �� (�������������� ����������) �� ������ ��������
28
+ :status_sell, # i4 ������ ������ �� ������� ��������
29
+ :hedge_sell, # i1 ������� �������� ������ �� ������� ��������
30
+ :fee_sell, # d26.2 ���� �� ������ ��������
31
+ :login_sell, # c20 ����� ������������ ��������
32
+ :code_rts_sell, # c7 ��� ��� ��������
33
+ :code_buy, # c7 ��� ����������
34
+ :id_ord_buy, # i8 ����� ������ ����������.
35
+ :ext_id_buy, # i4 ������� ����� �� ������ ����������
36
+ :comment_buy, # c20 ����������� �� ������ ����������
37
+ :trust_buy, # i1 ������� �� (�������������� ����������) �� ������ ����������
38
+ :status_buy, # i4 ������ ������ �� ������� ����������
39
+ :hedge_buy, # i1 ������� �������� ������ �� ������� ����������
40
+ :fee_buy, # d26.2 ���� �� ������ ����������
41
+ :login_buy, # c20 ����� ������������ ����������
42
+ :code_rts_buy # c7 ��� ��� ����������
43
+
44
+ attr_accessor :book
45
+
46
+ def self.from_record rec
47
+ new :isin_id => rec.GetValAsLong('isin_id'),
48
+ :deal_id => rec.GetValAsLong('id_deal'),
49
+ :id => rec.GetValAsString('replID').to_i,
50
+ :rev => rec.GetValAsString('replRev').to_i,
51
+ :price => rec.GetValAsString('price').to_f,
52
+ :moment => rec.GetValAsString('moment'),
53
+ :amount => rec.GetValAsString('amount').to_i
54
+ end
55
+
56
+ def self.index_for rec
57
+ rec.GetValAsLong('id_deal')
58
+ end
59
+
60
+ def index
61
+ @deal_id
62
+ end
63
+
64
+ def price= val
65
+ val = val.to_f
66
+ @price = val.round == val ? val.to_i : val
67
+ end
68
+
69
+ def inspect
70
+ "#{moment}:#{id}[#{isin}] #{price}>#{amount}"
71
+ end
72
+
73
+ alias to_s inspect
74
+ end
75
+ end
@@ -0,0 +1,78 @@
1
+ require 'fin/models/model'
2
+
3
+ module Fin
4
+ # Represents information about one tradeable security
5
+ # Source table: FORTS_FUTINFO_REPL::fut_sess_contents
6
+ #
7
+ class Instrument < Model
8
+
9
+ # Properties as per P2ClientGate API
10
+ prop_accessor [:repl_id, :id], [:repl_rev, :rev],
11
+ [:sess_id, :sess, :session, :session_id], # i4 ����� ������.
12
+ :isin_id, # i4 ���������� �������� ��� �����������.
13
+ :isin, # c25 ���������� ��� �����������
14
+ :short_isin, # c25 ��������� �����������.
15
+ :name, # c75 ������������ �����������.
16
+ :inst_term, # i4 �������� �� �����.
17
+ :code_vcb, # c25 ��� ���������.
18
+ # Not extracted from record yet...
19
+ :is_limited, # i1 ������� ������� ������� � ������.
20
+ :limit_up, # d16.5 ������� ����� ����.
21
+ :limit_down, # d16.5 ������ ����� ����.
22
+ :old_kotir, # d16.5 ����������������� ��������� ���� ���������� ������.
23
+ :buy_deposit, # d16.2 �� ����������.
24
+ :sell_deposit, # d16.2 �� ��������.
25
+ :roundto, # i4 ���������� ������ ����� ������� � ����.
26
+ :min_step, # d16.5 ����������� ��� ����.
27
+ :step_price, # d16.5 ��������� ���� ����.
28
+ :d_pg, # t ���� ��������� ��������� �����������.
29
+ :is_spread, # i1 ������� ��������� �������� � ����������� �����.
30
+ # 1 � ������; 0 � �� ������.
31
+ :coeff, # d9.6 ����������� ������������ ������.
32
+ :d_exp, # t ���� ���������� �����������.
33
+ :is_percent, # i1 ������� ����, ��� ������� ��������� � ���������.
34
+ # 1 - ��������� ���������, 0 � ��������� �� � ���������
35
+ :percent_rate, # d6.2 ���������� ������ ��� ������� ������������ �����
36
+ # �� ���������� ���������.
37
+ :last_cl_quote, # d16.5 ��������� ����� ���������� ��������.
38
+ :signs, # i4 ���� ���������.
39
+ :is_trade_evening, # i1 ������� �������� � �������� ������.
40
+ :ticker, # i4 ���������� �������� ��� �������� �����.
41
+ :state, # i4 ��������� �������� �� �����������
42
+ :price_dir, # i1 ����������� ���� �����������
43
+ :multileg_type, # i1 ��� ������
44
+ :legs_qty, # i4 ���������� ������������ � ������
45
+ :step_price_clr, # d16.5 C�������� ���� ���� ��������� ��������
46
+ :step_price_interclr, # d16.5 ��������� ���� ���� ����. ��������
47
+ :step_price_curr # d16.5 ��������� ������������ ���� ����, ���������� � ������
48
+
49
+ def self.from_record rec
50
+ new :isin_id => rec.GetValAsLong('isin_id'),
51
+ :isin => rec.GetValAsString('isin'),
52
+ :short_isin => rec.GetValAsString('short_isin'),
53
+ :name => rec.GetValAsString('name'),
54
+ :inst_term => rec.GetValAsLong('inst_term'),
55
+ :code_vcb => rec.GetValAsString('code_vcb'),
56
+
57
+ end
58
+
59
+ def self.index_for rec
60
+ rec.GetValAsLong('isin_id')
61
+ end
62
+
63
+ def index
64
+ @isin_id
65
+ end
66
+
67
+ def price= val
68
+ val = val.to_f
69
+ @price = val.round == val ? val.to_i : val
70
+ end
71
+
72
+ def inspect
73
+ "#{name}:#{short_isin}[#{isin}]"
74
+ end
75
+
76
+ alias to_s inspect
77
+ end
78
+ end
@@ -0,0 +1,39 @@
1
+ module Fin
2
+ # Represents business domain model for a single item (Order, Deal, Instrument, etc...)
3
+ # currently it is only used to extract common functionality from record wrappers,
4
+ # down the road it may be subclassed from ActiveModel
5
+ class Model
6
+ def self.prop_reader *args
7
+ args.each do |arg|
8
+ aliases = [arg].flatten
9
+ name = aliases.shift
10
+ instance_eval do
11
+ attr_reader name
12
+ aliases.each { |ali| alias_method "#{ali}", name }
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.prop_accessor *args
18
+ args.each do |arg|
19
+ aliases = [arg].flatten
20
+ name = aliases.shift
21
+ instance_eval do
22
+ attr_accessor name
23
+ aliases.each do |ali|
24
+ alias_method "#{ali}", name
25
+ alias_method "#{ali}=", "#{name}="
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def initialize opts = {}
32
+ opts.each { |key, value| send "#{key}=", value }
33
+ end
34
+
35
+ def index
36
+ object_id # TODO: @repl_id?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,81 @@
1
+ require 'fin/models/model'
2
+
3
+ module Fin
4
+ # Represents information about client`s money, vm and various limits
5
+ # Source table: FORTS_ PART_REPL::part � ���������� � ��������� � �������
6
+ #
7
+ class MoneyLimit < Model
8
+ # Properties as per P2ClientGate API
9
+ prop_accessor [:repl_id, :id], [:repl_rev, :rev],
10
+ :client_code, # c7 ��� �������
11
+ :money_old, # d26.2 ����� �� ������ ������
12
+ :money_amount, # d26.2 ����� �����
13
+ :money_free, # d26.2 �������� �����
14
+ :money_blocked, # d26.2 ������������� �����
15
+ :pledge_old, # d26.2 ������� �� ������ ������
16
+ :pledge_amount, # d26.2 ����� �������
17
+ :pledge_free, # d26.2 �������� �������
18
+ :pledge_blocked, # d26.2 ������������� �������
19
+ :vm_reserve, # d26.2 �����, ����������������� ��� ������������ �����
20
+ :vm_intercl, # d26.2 M����, ��������� ��� ���������� � ����. �������
21
+ :fee, # d26.2 ��������� ����
22
+ :fee_reserve, # d26.2 ��������������� ������ ����� ��� ������
23
+ :limit_spot_buy, # d26.2 ����� �� ������� ������.
24
+ :limit_spot_buy_used, # d26.2 �������������� ����� �� ������� ������
25
+ :coeff_go, # d16.5 ����������� ����������� ��
26
+ :coeff_liquidity, # d16.5 ����������� �����������
27
+ :premium, # d26.2 ������
28
+ :premium_order_reserve, # f ������ ������ ��� ������
29
+
30
+ # Not extracted from record:
31
+ :is_auto_update_limit, # i1 ������� ������������� ������ �� ��������
32
+ # ������ ��� ������� ����� ��������: 0-���, 1-������.
33
+ :is_auto_update_spot_limit, # i1 ������� ������������� ������� �� ������
34
+ # (�� �������, � �� �������) ��� ������� ����� ��������: 0-���, 1-������.
35
+ :no_fut_discount, # i1 ���������� ������������� ������ ��
36
+ # ���������: 1-������, 0-���.
37
+ :limits_set # i1 ������� �������. 0 � ������ �����������
38
+
39
+
40
+ def self.from_record rec
41
+ new :repl_id => rec.GetValAsString('replID').to_i,
42
+ :repl_rev => rec.GetValAsString('replRev').to_i,
43
+ :client_code => rec.GetValAsString('client_code'),
44
+ :money_old => rec.GetValAsString('money_old').to_f,
45
+ :money_amount => rec.GetValAsString('money_amount').to_f,
46
+ :money_free => rec.GetValAsString('money_free').to_f,
47
+ :money_blocked => rec.GetValAsString('money_blocked').to_f,
48
+ :pledge_old => rec.GetValAsString('pledge_old').to_f,
49
+ :pledge_amount => rec.GetValAsString('pledge_amount').to_f,
50
+ :pledge_free => rec.GetValAsString('pledge_free').to_f,
51
+ :pledge_blocked => rec.GetValAsString('pledge_blocked').to_f,
52
+ :vm_reserve => rec.GetValAsString('vm_reserve').to_f,
53
+ :vm_intercl => rec.GetValAsString('vm_intercl').to_f,
54
+ :fee => rec.GetValAsString('vm_intercl').to_f,
55
+ :fee_reserve => rec.GetValAsString('vm_reserve').to_f,
56
+ :limit_spot_buy => rec.GetValAsString('vm_reserve').to_f,
57
+ :limit_spot_buy_used => rec.GetValAsString('vm_reserve').to_f,
58
+ :coeff_go => rec.GetValAsString('coeff_go').to_f,
59
+ :coeff_liquidity => rec.GetValAsString('coeff_liquidity').to_f,
60
+ :premium => rec.GetValAsString('premium').to_f,
61
+ :premium_order_reserve => rec.GetValAsString('premium_order_reserve').to_f,
62
+ end
63
+
64
+ def self.index_for rec
65
+ rec.GetValAsLong('replID')
66
+ end
67
+
68
+ def index
69
+ @repl_id
70
+ end
71
+
72
+ def inspect
73
+ "Money: Old #{money_old} Amt #{money_amount} Free #{money_free} Blck #{money_blocked} " +
74
+ "Pledge: Old #{pledge_old} Amt #{pledge_amount} Free #{pledge_free} Blck #{pledge_blocked} " +
75
+ "VM: Reserve #{vm_reserve} Intercl #{vm_intercl} Fee: #{fee} Reserve #{fee_reserve} " +
76
+ "Limit Spot: Buy #{limit_spot_buy} Used #{limit_spot_buy_used}"
77
+ end
78
+
79
+ alias to_s inspect
80
+ end
81
+ end
@@ -0,0 +1,45 @@
1
+ require 'fin/models/model'
2
+
3
+ module Fin
4
+ class Order < Model
5
+
6
+ # Properties as per P2ClientGate API
7
+ prop_accessor [:repl_id, :id], [:repl_rev, :rev],
8
+ [:isin_id, :isin],
9
+ :price, # d16.5 ���� ���������
10
+ :volume, # i8 ����� �������������� ���������
11
+ [:dir, :buysell], # i1 ����������� ���������: ������� (1) /������� (2)
12
+ :moment # t ����� ���������� ���������� ���������
13
+
14
+ attr_accessor :book
15
+
16
+ def self.from_record rec
17
+ new :isin_id => rec.GetValAsLong('isin_id'),
18
+ :repl_id => rec.GetValAsString('replID').to_i,
19
+ :repl_rev => rec.GetValAsString('replRev').to_i,
20
+ :price => rec.GetValAsString('price').to_f,
21
+ :volume => rec.GetValAsString('volume').to_f,
22
+ :moment => rec.GetValAsString('moment'),
23
+ :dir => rec.GetValAsLong('dir')
24
+ end
25
+
26
+ def self.index_for rec
27
+ rec.GetValAsLong('replID')
28
+ end
29
+
30
+ def index
31
+ @repl_id
32
+ end
33
+
34
+ def price= val
35
+ val = val.to_f
36
+ @price = val.round == val ? val.to_i : val
37
+ end
38
+
39
+ def inspect
40
+ "#{repl_id}:#{price}>#{volume}#{dir == 1 ? '+' : '-'}"
41
+ end
42
+
43
+ alias to_s inspect
44
+ end
45
+ end
@@ -0,0 +1,57 @@
1
+ require 'fin/models/model'
2
+
3
+ module Fin
4
+ # Represents a client`s position for one security
5
+ # Source table: FORTS_POS_REPL::position
6
+ #
7
+ class Position < Model
8
+
9
+ # Properties as per P2ClientGate API
10
+ prop_accessor [:repl_id, :id], [:repl_rev, :rev],
11
+ [:isin_id, :isin],
12
+ :client_code,
13
+ :open_qty,
14
+ :buys_qty,
15
+ :sells_qty,
16
+ :pos,
17
+ :net_volume_rur,
18
+ :last_deal_id
19
+ # isin_id i4 ���������� �������� ������������� �����������
20
+ # client_code c7 ��� �������
21
+ # open_qty i4 ���������� ������� �� ������ ������
22
+ # buys_qty i4 ���������� ��������� ���������� � ���� ������
23
+ # sells_qty i4 ���������� ��������� ���������� � ���� ������
24
+ # pos i4 ������� �������
25
+ # net_volume_rur d26.2
26
+ # H����-����� �����, � ������, �� ������� ���� ��������� ������.
27
+ # ������������� ����� � ������ ��������, ������������� � ������ �������������
28
+ # last_deal_id i8 ������������� ��������� ������
29
+
30
+ def self.from_record rec
31
+ new :isin_id => rec.GetValAsLong('isin_id'),
32
+ :repl_id => rec.GetValAsString('replID').to_i,
33
+ :repl_rev => rec.GetValAsString('replRev').to_i,
34
+ :client_code => rec.GetValAsString('client_code'),
35
+ :open_qty => rec.GetValAsLong('open_qty'),
36
+ :buys_qty => rec.GetValAsLong('buys_qty'),
37
+ :sells_qty => rec.GetValAsLong('sells_qty'),
38
+ :pos => rec.GetValAsLong('pos'),
39
+ :last_deal_id => rec.GetValAsString('replID').to_i,
40
+ :net_volume_rur => rec.GetValAsString('net_volume_rur').to_f
41
+ end
42
+
43
+ def self.index_for rec
44
+ rec.GetValAsLong('isin_id')
45
+ end
46
+
47
+ def index
48
+ @isin_id
49
+ end
50
+
51
+ def inspect
52
+ "#{repl_id}[#{isin_id}] #{pos}, open: #{open_qty}, buys: #{buys_qty}, sells: #{sells_qty}"
53
+ end
54
+
55
+ alias to_s inspect
56
+ end
57
+ end
@@ -0,0 +1,17 @@
1
+ require 'fin/models/order'
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 OrderList < ContainerList
8
+
9
+ include BookManager
10
+
11
+ def initialize
12
+ super :item_type => Fin::Order
13
+ @book_index = proc { |item| item.price }
14
+ @book_condition = proc { |item| item.price > 0 }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,443 @@
1
+ # Legacy VCL-based code to be converted into new efficient Ruby code
2
+
3
+ module VCL
4
+
5
+ #type
6
+ # tDuplicates = [:dup_accept, :dup_ignore, :dup_replace]
7
+ # // ������� ����� "������" � ���������� ��������������� ������������ ���������
8
+ #type CustomList = class(tList)
9
+ # procedure clear; override;
10
+ # procedure freeitem(item: pointer); virtual; abstract;
11
+ # procedure freeall; virtual;
12
+ # procedure delete(index: longint); virtual;
13
+ # procedure remove(item: pointer); virtual;
14
+ # end;
15
+ class CustomList < Array
16
+
17
+ def clear
18
+ (0...size).each { freeitem(self[i]) }
19
+ super
20
+ end
21
+
22
+ def freeall
23
+ clear
24
+ end
25
+
26
+ def freeitem item
27
+ end
28
+
29
+ # Standard #delete_at instead of #delete
30
+
31
+ # Deletes ALL items from arr that are equal to obj.?
32
+ # No, we need to remove ONE item by "pointer"
33
+ def remove item #(item: pointer)
34
+ freeitem item
35
+ delete item
36
+ end
37
+ end # class CustomList
38
+
39
+ # // ������� ����� "������������� ������"
40
+ #type tSortedList = class(CustomList)
41
+ # fDuplicates : tDuplicates;
42
+ # constructor create;
43
+ # function checkitem(item: pointer): boolean; virtual; abstract;
44
+ # function compare(item1, item2: pointer): longint; virtual; abstract;
45
+ # function search(item: pointer; var index: longint): boolean; virtual;
46
+ # procedure add(item: pointer); virtual;
47
+ # procedure insert(index: longint; item: pointer); virtual;
48
+ # end;
49
+ class SortedList < CustomList
50
+
51
+ attr_accessor :duplicates
52
+
53
+ def initialize
54
+ @duplicates = :dup_accept
55
+ super
56
+ end
57
+
58
+ # Returns true and item's index if item is in the List
59
+ # Returns false and item's index if item not found
60
+ def search item #(item: pointer; var index: longint): boolean
61
+ result = false
62
+ l = 0
63
+ h = size - 1
64
+ while (l <= h) do
65
+ i = (l + h) >> 1
66
+ case compare(self[i], item)
67
+ when -1
68
+ l = i + 1
69
+ when 1
70
+ h = i - 1
71
+ when 0
72
+ h = i - 1
73
+ result = true
74
+ l = i if @duplicates == :dup_ignore || @duplicates == :dup_replace
75
+ end
76
+ end
77
+ index = l
78
+ [result, index]
79
+ end
80
+
81
+ def add item #(item: pointer)
82
+ if checkitem(item)
83
+ result, index = search(item)
84
+ if result
85
+ case @duplicates
86
+ when :dup_accept
87
+ insert(index, item)
88
+ when :dup_ignore
89
+ freeitem(item)
90
+ when :dup_replace
91
+ freeitem(self[index])
92
+ self[index] = item
93
+ end
94
+ else
95
+ insert(index, item)
96
+ end
97
+ else
98
+ freeitem(item)
99
+ end
100
+ end
101
+
102
+ def insert index, item #(index: longint; item: pointer)
103
+ if checkitem(item)
104
+ super index, item
105
+ end
106
+ end
107
+ end # class SortedList
108
+
109
+ # // ������ ������� �� ����
110
+ #type tOrderBook = class(tSortedList)
111
+ # private
112
+ # fisin_id : longint;
113
+ # fchanged : boolean;
114
+ # public
115
+ # procedure freeitem(item: pointer); override;
116
+ # function checkitem(item: pointer): boolean; override;
117
+ # function compare(item1, item2: pointer): longint; override;
118
+ #
119
+ # procedure add(item: pointer); override;
120
+ # procedure remove(item: pointer); override;
121
+ #
122
+ # property isin_id: longint read fisin_id;
123
+ # property changed: boolean read fchanged write fchanged;
124
+ # end;
125
+ class OrderBook < SortedList
126
+ attr_accessor :isin_id, :changed
127
+
128
+ def initialize isin_id
129
+ @isin_id = isin_id
130
+ super()
131
+ end
132
+
133
+ def checkitem item #(item: pointer): boolean
134
+ item.price > 0
135
+ end
136
+
137
+ def compare item1, item2 #(item1, item2: pointer): longint
138
+ item1.price <=> item2.price
139
+ end
140
+
141
+ def add item #(item: pointer)
142
+ super item
143
+ @changed = true
144
+ end
145
+
146
+ def remove item #(item: pointer)
147
+ super item
148
+ @changed = true
149
+ end
150
+ end # class OrderBook
151
+
152
+ # // ������ ��������
153
+ #type tPriceLists = class(tSortedList)
154
+ # private
155
+ # tmp_ordbook : tOrderBook;
156
+ # public
157
+ # destructor destroy; override;
158
+ # procedure freeitem(item: pointer); override;
159
+ # function checkitem(item: pointer): boolean; override;
160
+ # function compare(item1, item2: pointer): longint; override;
161
+ # function searchadditem(isin_id: longint): tOrderBook;
162
+ # end;
163
+ #
164
+ class OrderBookList < SortedList
165
+
166
+ def checkitem item #(item: pointer): boolean
167
+ item
168
+ end
169
+
170
+ def compare(item1, item2) #(item1, item2: pointer): longint
171
+ item1.isin_id <=> item2.isin_id;
172
+ end
173
+
174
+ def searchadditem(isin_id) #(isin_id: longint): OrderBook
175
+ order_book = OrderBook.new(isin_id)
176
+ exists, idx = search(order_book)
177
+ if exists
178
+ result = self[idx]
179
+ else
180
+ result = order_book
181
+ insert(idx, result)
182
+ end
183
+ result
184
+ end
185
+
186
+ end # class OrderBook
187
+
188
+ # // ����� ������� ���������
189
+ #type OrderList = class(tSortedList)
190
+ # fOrderBooks : tPriceLists;
191
+ # constructor create;
192
+ # destructor destroy; override;
193
+ # procedure freeitem(item: pointer); override;
194
+ # function checkitem(item: pointer): boolean; override;
195
+ # function compare(item1, item2: pointer): longint; override;
196
+ # function searchadditem(isin_id: longint): tOrderBook;
197
+ # function addrecord(isin_id: longint; const id, rev: int64; const price, volume: double; buysell: longint): boolean;
198
+ # function delrecord(const id: int64): boolean;
199
+ # procedure clearbyrev(const rev: int64);
200
+ # end;
201
+ class OrderList < SortedList
202
+ attr_accessor :books
203
+
204
+ def initialize
205
+ super
206
+ @books = OrderBookList.new
207
+ end
208
+
209
+ def freeitem item #(item: pointer)
210
+ item.order_book.remove(item) if item.order_book
211
+ end
212
+
213
+ def checkitem item #(item: pointer): boolean
214
+ item
215
+ end
216
+
217
+ def compare(item1, item2) #: pointer): longint
218
+ item1.id <=> item2.id
219
+ end
220
+
221
+ def searchadditem isin_id #(isin_id: longint): OrderBook
222
+ @books.searchadditem(isin_id)
223
+ end
224
+
225
+ def addrecord isin_id, id, rev, price, volume, buysell #(isin_id: longint; const id, rev: int64; const price, volume: double; buysell: longint): boolean
226
+ item = OrderItem.new
227
+ item.id = id
228
+ result, idx = search(item)
229
+ if result
230
+ item = self[idx]
231
+
232
+ if item.price != price # �������, ��� ���� ����������
233
+ item.order_book.remove(item) if item.order_book # ������� �� �������
234
+ if price > 0
235
+ item.order_book = searchadditem(isin_id) unless item.order_book
236
+ item.order_book.add(item) if item.order_book # ��������� � ������
237
+ else
238
+ item.order_book = nil;
239
+ end
240
+ end
241
+
242
+ item.rev = rev
243
+ item.price = price
244
+ item.volume = volume
245
+ item.buysell = buysell
246
+ else
247
+ item.rev = rev
248
+ item.price = price
249
+ item.volume = volume
250
+ item.buysell = buysell
251
+
252
+ if (item.price > 0)
253
+ item.order_book = searchadditem(isin_id)
254
+ item.order_book.add(item) if item.order_book # ��������� � ������
255
+ else
256
+ item.order_book = nil
257
+ end
258
+ insert(idx, item) # ��������� � ����� �������
259
+ end
260
+ end
261
+
262
+ def delrecord id #(const id: int64): boolean
263
+ item = OrderItem.new
264
+ item.id = id
265
+ result, idx = search(item)
266
+ delete_at(idx) if result # ������� �� ����� �������
267
+ end
268
+
269
+ # Delete all records with rev less than given
270
+ def clearbyrev rev #(const rev: int64)
271
+ (size-1).downto(0) do |i|
272
+ delete_at(i) if self[i].rev < rev # ������� �� ����� �������
273
+ end
274
+ end
275
+
276
+ end #class OrderList
277
+
278
+ # // ������� "������ � �������"
279
+ #type pOrderItem = ^tOrderItem;
280
+ # tOrderItem = record
281
+ # id : int64;
282
+ # rev : int64;
283
+ # price : double; // ����
284
+ # volume : double; // ���-��
285
+ # buysell : longint; // �������(1)/�������(2)
286
+ # order_book : tOrderBook;
287
+ # end;
288
+ class OrderItem
289
+ attr_accessor :id, :rev, :price, :volume, :buysell, :order_book
290
+
291
+ def inspect
292
+ "#{id}:#{price}>#{volume}#{buysell == 1 ? '+' : '-'}"
293
+ end
294
+
295
+ alias to_s inspect
296
+ end
297
+
298
+ #######################
299
+ # New hierarchy:
300
+ #######################
301
+
302
+ # Abstract (equivalent of SortedList)
303
+ # ������� ����� "������������� ������"
304
+ class SortedHash < Hash
305
+
306
+ def free item
307
+ end
308
+
309
+ def check item
310
+ true
311
+ end
312
+
313
+ def index item
314
+ item.object_id
315
+ end
316
+
317
+ # Adds new item only if it passes check...
318
+ # TODO: What to do with the item it should have replaced?!
319
+ def add item
320
+ self[index item] = item if check item
321
+ # check item ? super : free key # delete key
322
+ end
323
+
324
+ def remove item
325
+ free item
326
+ delete index item
327
+ end
328
+
329
+ def clear
330
+ each_value { |item| free item }
331
+ super
332
+ end
333
+ end # SortedHash
334
+
335
+ # Represents DOM (OrderBook) for one security
336
+ # ������ ������� �� ����
337
+ class DOM < SortedHash
338
+ attr_accessor :isin_id, :changed
339
+
340
+ def initialize isin_id
341
+ @isin_id = isin_id
342
+ @changed = false
343
+ super
344
+ end
345
+
346
+ def index item
347
+ item.price
348
+ end
349
+
350
+ def check item
351
+ item.price > 0
352
+ end
353
+
354
+ def add item
355
+ @changed = true # Marking DOM as changed
356
+ super
357
+ end
358
+
359
+ def remove item
360
+ @changed = true # Marking DOM as changed
361
+ super
362
+ end
363
+ end # class DOM
364
+
365
+
366
+ # Represents Hash of DOMs (OrderBooks) indexed by isin_id
367
+ # ������ ��������
368
+ class DOMHash < SortedHash
369
+
370
+ def index item
371
+ item.isin_id
372
+ end
373
+
374
+ # Always return DOM for isin_id, create one on spot if need be
375
+ def [] isin_id
376
+ super || add(DOM.new isin_id)
377
+ end
378
+ end # class DOMHash
379
+
380
+ # Represents Hash of all aggregated orders by (repl) id
381
+ # ����� ������� ���������
382
+ class OrderHash < SortedHash
383
+ attr_accessor :books
384
+
385
+ def index item
386
+ item.id
387
+ end
388
+
389
+ def initialize
390
+ super
391
+ @books = DOMHash.new
392
+ end
393
+
394
+ # We need to clear item from its order book before scrapping it
395
+ def free item
396
+ item.order_book.remove item if item.order_book
397
+ end
398
+
399
+ # Rebooks item to a correct order book, given its price
400
+ def rebook item, book
401
+ if (item.price > 0)
402
+ # item represents new aggr_order with price
403
+ item.order_book = book unless item.order_book
404
+ book.add item # ��������� � ������
405
+ else
406
+ # item clears previous aggr_order for given price
407
+ item.order_book = nil
408
+ end
409
+ end
410
+
411
+ # process_record
412
+ def addrecord isin_id, id, rev, price, volume, buysell
413
+ item = self[id] || OrderItem.new
414
+
415
+ price_changed = item.price != price # �������, ��� ���� ����������
416
+
417
+ item.id = id
418
+ item.rev = rev
419
+ item.price = price
420
+ item.volume = volume
421
+ item.buysell = buysell
422
+
423
+ if price_changed
424
+ if self[id] # item is already here
425
+ item.order_book.remove item if item.order_book # free item - ������� �� �������
426
+ else # new item
427
+ add item
428
+ end
429
+ rebook item, @books[isin_id]
430
+ end
431
+ end
432
+
433
+ def delrecord id
434
+ remove self[id] if self[id]
435
+ end
436
+
437
+ # Clear all records with rev less than given
438
+ def clearbyrev rev #(const rev: int64)
439
+ each_value { |item| remove item if item.rev < rev } # ������� �� ����� �������
440
+ end
441
+ end # class OrderHash
442
+
443
+ end # module