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,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