ib-technical-analysis 0.2

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/bin/history.irb ADDED
@@ -0,0 +1,186 @@
1
+ exit
2
+ exit
3
+ exit
4
+ exit
5
+ exit
6
+ exit
7
+ exit
8
+ exit
9
+ exit
10
+ exit#
11
+ exit
12
+ exit
13
+ exit
14
+ exit
15
+ exit
16
+ exit
17
+ q
18
+ exit
19
+ exit
20
+ exit
21
+ exit
22
+ exit
23
+ exit
24
+ exit
25
+ exit
26
+ exit
27
+ exit
28
+ ATS.keys
29
+ ATS.first
30
+ p ATS.first
31
+ p ATS.first
32
+ puts ATS.first
33
+ puts ATS.first.result
34
+ puts ATS.first
35
+ irb ATS.first
36
+ @momentum
37
+ methods
38
+ @momentum
39
+ exit
40
+ puts ATS.first
41
+ puts ATS.first.result
42
+ p ATS.first
43
+ p ATS.first.result
44
+ p ATS.first..call :result
45
+ p= ATS.first
46
+ p
47
+ purs p.result
48
+ p= ATS[:"mini-dax"]
49
+ puts p.result
50
+ z= const_set
51
+ Base
52
+ Base.const_set 'G'
53
+ Base.const_set 'G', 6
54
+ G
55
+ z= Base.const_set 'G', 6
56
+ z
57
+ IB::Base::G
58
+ z= IB.const_set 'GU', 6
59
+ GU
60
+ z= 'jleor'
61
+ z.capitalize
62
+ exit
63
+ Dax
64
+ Russel
65
+ exit
66
+ ATS.keys
67
+ ATS[:dax]
68
+ puts Russel.result
69
+ puts Russell.result
70
+ puts Russell.result[:result]
71
+ puts Russell.result.map{|y| y[:result]}
72
+ puts dax.result.map{|y| y[:result]}
73
+ puts Dax.result.map{|y| y[:result]}
74
+ print Dax.result.map{|y| y[:result]}
75
+ print Dax.result.map{|y| y[:result].to_d}
76
+ print Dax.result.map{|y| y[:result].to_f}
77
+ print Russell.result.map{|y| y[:result].to_f}
78
+ exit
79
+ exit
80
+ Dax.result
81
+ puts Dax.result
82
+ exit
83
+ puts Dax.result
84
+ puts Dax.result.result
85
+ puts Dax.result
86
+ print Dax.result.result
87
+ print Dax.result.result.to_i
88
+ print Russell.result.result.to_i
89
+ exti
90
+ extit
91
+ exit
92
+ print Dax.raw_data.result.to_i
93
+ print Dax.contract
94
+ print exit
95
+ print Dax.raw_data.result.to_i
96
+ print Dax.contract
97
+ print Dax.current_price_signal
98
+ print Dax.current_price_signal
99
+ Dax.current_price_signal
100
+ exit
101
+ print Dax.current_price_signal
102
+ Dax.current_price_signal
103
+ print Dax.contract
104
+ Dax.contract
105
+ Dax.contract ; nil
106
+ irb Dax
107
+ @contract
108
+ exit
109
+ Dax.contract.to_human
110
+ Dax.contract.to_human
111
+ exit
112
+ exit
113
+ IB::BAR_SIZES
114
+ IB::BAR_SIZES.keys
115
+ exit
116
+ Dax.contract.to_human
117
+ Dax.raw_data.first
118
+ exit
119
+ i = IB::Bar.new
120
+ i.methods
121
+ require 'technical-analysis'
122
+ z.capitalize
123
+ z= 'jleor'
124
+ z = Symbols::Futures.mini_dax
125
+ zz= z.eod duration: '50 D'
126
+ zz= z.eod( duration: '50 D').each
127
+ zz= z.calculate( :ema, period: 15 , use: :close )
128
+ require 'ib-technical-analysis'
129
+ TechnicalAnalysis::Momentum::Lane
130
+ zz= z.eod duration: '50 D'
131
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
132
+ exit
133
+ z = Symbols::Futures.mini_dax
134
+ zz= z.eod duration: '50 D'
135
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
136
+ exit
137
+ z = Symbols::Futures.mini_dax
138
+ zz= z.eod duration: '50 D'
139
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
140
+ exit
141
+ exit
142
+ exit
143
+ z = Symbols::Futures.mini_dax
144
+ zz= z.eod duration: '50 D'
145
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
146
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz.close
147
+ exit
148
+ z = Symbols::Futures.mini_dax
149
+ zz= z.eod duration: '50 D'
150
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz.close
151
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
152
+ exit
153
+ z = Symbols::Futures.mini_dax
154
+ zz= z.eod duration: '50 D'
155
+ indicator = TechnicalAnalysis::MovingAverage::KaMA.new period: 30, fast: 5, slow: 15, data: zz
156
+ zz.first
157
+ zz.first.pivot
158
+ exit
159
+ exit
160
+ exit
161
+ Dax.result
162
+ Russell
163
+ exit
164
+ Dax
165
+ Dax
166
+ Dax.result
167
+ Dax.raw_data.result
168
+ Dax.raw_data.result.to_i
169
+ exit
170
+ z = Symbols::Futures.mini_dax
171
+ zz= z.eod duration: '50 D'
172
+ Symbols::Stocks.all
173
+ z= Symbols::Stocks.msft
174
+ zz= z.eod duration: '50 D'
175
+ z.close
176
+ zz.close
177
+ Symbols::Forex.all
178
+ IB::BAR_SIZES
179
+ IB::BAR_SIZES.key(hour)
180
+ IB::BAR_SIZES.key(:hour)
181
+ IB::BAR_SIZES.keys
182
+ IB::BAR_SIZES.values
183
+ exit
184
+ exit
185
+ TradingSystem::Base
186
+ exit
@@ -0,0 +1,38 @@
1
+ require_relative 'lib/technical_analysis/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "ib-technical-analysis"
5
+ spec.version = TechnicalAnalysis::VERSION
6
+ spec.authors = ["Hartmut Bischoff"]
7
+ spec.email = ["topofocus@gmail.com"]
8
+
9
+ spec.summary = %q{.Tools to perform technical analysis on financial data .}
10
+ spec.homepage = "https://ib-ruby.github.io/ib-doc/"
11
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.0.0")
12
+
13
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.cm/ib-ruby/ib-technical-analysis"
17
+ spec.metadata["changelog_uri"] = "https://github.cm/ib-ruby/ib-technical-analysis/changelog.md"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+
29
+ spec.add_dependency "ib-api"
30
+ spec.add_dependency "ib-extensions"
31
+ spec.add_development_dependency "bundler", "~> 2.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_development_dependency 'rspec-collection_matchers'
34
+ spec.add_development_dependency 'rspec-its'
35
+
36
+ spec.add_development_dependency "guard"
37
+ spec.add_development_dependency "guard-rspec"
38
+ end
@@ -0,0 +1,149 @@
1
+ require 'dry/core/class_attributes'
2
+
3
+ module TechnicalAnalysis
4
+ module BarCalculation
5
+
6
+ def true_range previous_close
7
+ [
8
+ (high - low),
9
+ (high - previous_close).abs,
10
+ (low - previous_close).abs
11
+ ].max
12
+ end
13
+
14
+ def typical_price
15
+ ( high + low + close ) / 3.0
16
+ end
17
+ end
18
+
19
+
20
+
21
+ module MovingAverage
22
+ EMA = Struct.new :time, :value
23
+ WMA = Struct.new :time, :value
24
+
25
+
26
+ # Calculates the exponential moving average (EMA) for the data over the given period
27
+ # https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
28
+ #
29
+ #
30
+ # z = Symbols::Futures.mini_dax.eod( duration: '30 d').each
31
+ # e= nil
32
+ # ema= z.map{|y| e= TechnicalAnalysis::MovingAverage.ema( y.close, z.map(&:close), 30, e ) }
33
+ #
34
+ # or
35
+ #
36
+ # EMA = Struct.new :time, :ema
37
+ # e = nil
38
+ # ema = z.map do |y|
39
+ # EMA.new y.time,
40
+ # e = TechnicalAnalysis::MovingAverage.ema y.close, z.map(&:close), 30, e
41
+ # end
42
+ #
43
+
44
+ def self.ema current_value, data, period, prev_ema
45
+ if prev_ema.nil?
46
+ data.sum / data.size.to_f # Average
47
+ else
48
+ (current_value - prev_ema) * (2.0 / (period + 1.0)) + prev_ema
49
+ end
50
+ end
51
+
52
+ # Calculates the weighted moving average
53
+ #
54
+ # Parameter is the data-array.
55
+ #
56
+ # Takes an optional Block. Specify a method name that returns the data item
57
+ #
58
+ # z = Symbols::Futures.mini_dax.eod( duration: '30 d').each
59
+ # TechnicalAnalysis::ArrayCalculation.wma( z ){ :close }
60
+ #
61
+ def self.wma(data)
62
+ intermediate_values = []
63
+ data.each_with_index do |datum, i|
64
+ datum = datum.send yield if block_given?
65
+ intermediate_values << datum * (i + 1) / (data.size * (data.size + 1) / 2).to_f
66
+ end
67
+ intermediate_values.sum
68
+ end
69
+ end
70
+ end
71
+
72
+ ## reopen IB::Bar and include BarCalculation ( as Mixin )
73
+
74
+ module IB
75
+ class Bar
76
+ include TechnicalAnalysis::BarCalculation
77
+ end
78
+ # z= Symbols::Futures.mini_dax.eod( duration: '30 d').each
79
+ #
80
+ # loop do
81
+ # bar= z.next
82
+ # puts bar.time, z.peek.true_range(bar.close)
83
+ # end.
84
+
85
+ # z.rewind
86
+ #
87
+ # z.map{ |y| x.typical_price }
88
+ #
89
+ end # module
90
+
91
+ module TASupport
92
+ refine Enumerator do
93
+ #
94
+ # first include
95
+ # using TASupport
96
+ # in your script.
97
+ #
98
+ # After fetching stock-data from a file, a database or the broker and
99
+ # converting it into an Enumerator (simply using array.each)
100
+ #
101
+ # calculate
102
+ #
103
+ # is just performed on the object
104
+ #
105
+ # The result is returned as array of structs.
106
+ #
107
+ # z = Symbols::Futures.mini_dax.eod( duration: '50 d').each
108
+ # z.calculate { :close }
109
+ #
110
+ # zz= z.calculate( :ema ){ :typical_price }
111
+ #
112
+ # zz= z.calculate( :ema, period: 3 ) { :close }
113
+ # zz.first
114
+ # => #<struct TechnicalAnalysis::MovingAverage::EMA time=Wed, 10 Mar 2021, value=0.149441e5
115
+ def calculate indicator= :ema, **params
116
+ struct = TechnicalAnalysis::MovingAverage.send :const_get, indicator.to_s.upcase
117
+ buffer, start = nil, []
118
+ choice = if block_given?
119
+ yield
120
+ elsif peek.respond_to?(:time)
121
+ :close
122
+ else
123
+ nil
124
+ end
125
+ data = choice.nil? ? self.to_a : map{|y| y.send choice }
126
+ period = params[:period] || 30
127
+ calc_ema = ->(item){ buffer= TechnicalAnalysis::MovingAverage.ema item, data, period, buffer }
128
+
129
+ if peek.respond_to? :time
130
+ map{ | d |
131
+ value = case indicator
132
+ when :ema
133
+ calc_ema[ d.send choice ]
134
+ when :wma
135
+ TechnicalAnalysis::MovingAverage.wma start << d.send( choice )
136
+ end
137
+ struct.new d.time , value
138
+ }#map
139
+ else
140
+ case indicator
141
+ when :ema
142
+ map{ | d | calc_ema[ choice.nil? ? d : d.send( choice ) ] }
143
+ when :wma
144
+ map{ |d| TechnicalAnalysis::MovingAverage.wma( start << choice.nil? ? d : d.send( choice ) ) }
145
+ end # case
146
+ end # branch
147
+ end # def
148
+ end # refine
149
+ end # module
data/lib/ib/bar.rb ADDED
@@ -0,0 +1,23 @@
1
+ module IB
2
+ class Bar
3
+ def true_range previous_close
4
+ [
5
+ (high - low),
6
+ (high - previous_close).abs,
7
+ (low - previous_close).abs
8
+ ].max
9
+ end
10
+
11
+ def typical_price
12
+ ( high + low + close ) / 3.0
13
+ end
14
+
15
+ def pivot
16
+ { pp: p=( high + close + low ).to_f / 3 ,
17
+ r1: ( 2 * p ).to_f - low.to_f,
18
+ r2: p + ( high - low ).to_f,
19
+ s1: ( 2 * p ).to_f - high.to_f,
20
+ s2: p - ( high - low ).to_f }
21
+ end
22
+ end
23
+ end
data/lib/ta_support.rb ADDED
@@ -0,0 +1,157 @@
1
+
2
+ module TASupport
3
+ refine Enumerator do
4
+ #
5
+ # first include
6
+ # using TASupport
7
+ # in your script.
8
+ #
9
+ # After fetching stock-data from a file, a database or the broker and
10
+ # converting it into an Enumerator (simply using array.each)
11
+ #
12
+ # calculate
13
+ #
14
+ # is just performed on the object
15
+ #
16
+ # The result is returned as array of structs.
17
+ #
18
+ # z = Symbols::Futures.mini_dax.eod( duration: '150 d').each
19
+ # z.calculate use: :close
20
+ #
21
+ # zz= z.calculate :ema, use: :typical_price
22
+ #
23
+ # zz= z.calculate( :ema, period: 3, use: :close
24
+ # zz.first
25
+ # => #<struct TechnicalAnalysis::MovingAverage::EMA time=Wed, 10 Mar 2021, value=0.149441e5
26
+ #
27
+ # Input-data are converted to float and then applied to the indicator-calculations
28
+ #
29
+ # A block can be provided to execute commands after calculating each single indicator value
30
+ # This is provided to enable backtests on the data
31
+ #
32
+ # The block gets the input-data **and** the calculated indicator struct.
33
+ #
34
+ # i.e.
35
+ # buffer=[]
36
+ # zz= z.calculate( :ema, use: :typical_price ) do | raw, struct |
37
+ # buffer << struct.value
38
+ # buffer.shift if buffer.size >2
39
+ # momentum_indicator = (buffer.first - buffer.last) <=> 0
40
+ # crossing = case momentum_indicator
41
+ # when +1
42
+ # buffer.first > raw.close && buffer.last < raw.close
43
+ # when -1
44
+ # buffer.first < raw.close && buffer.last > raw.close
45
+ # end
46
+ # buy_or_sell = momentum_indicator == 1 ? "buy" : "sell"
47
+ # puts "#{buy_or_sell}-Signal: EMA-Indicator-Crossing @ #{struct.time}" if crossing
48
+ # end
49
+ #
50
+ #
51
+ def calculate indicator= :ema, **params
52
+ struct = if indicator.to_s[-2,2]=='ma'
53
+ TechnicalAnalysis::MovingAverage.send :const_get, indicator.to_s.upcase
54
+ elsif indicator.to_s[-2,2]=='si' || indicator== :lane
55
+ TechnicalAnalysis::Momentum.send :const_get, indicator.to_s.upcase
56
+ end
57
+ buffer, start, default_value = nil, [], nil
58
+
59
+ ## strict-mode
60
+ strict_mode = params[:strict] || false
61
+
62
+ choice = if params[:use].present?
63
+ params[:use]
64
+ elsif peek.respond_to?(:time)
65
+ :close
66
+ else
67
+ nil
68
+ end
69
+ ## fill in defaults
70
+ case indicator
71
+ when :ema, :wma, :sma, :rsi
72
+ period = params[:period] || 15
73
+ when :kama
74
+ period = params[:period] || 15
75
+ fast = params[:fast] || 2
76
+ slow= params[:slow] || 30
77
+ when :tsi
78
+ high = params[:high] || 25
79
+ low = params[:low] || 13
80
+ when :lane
81
+ period = params[:period] || 10
82
+ fast = params[:fast] || 3
83
+ slow= params[:slow] || 3
84
+
85
+ end
86
+
87
+ # start (array of processed values) is updated in every iteration
88
+ # applies to indicators
89
+ # kama
90
+ # sma
91
+ # wma
92
+ #
93
+ a = peek
94
+ date_field = if a.respond_to? :time
95
+ :time
96
+ elsif a.respond_to? :date_time
97
+ :date_time
98
+ elsif a.respond_to? :date
99
+ :date
100
+ else
101
+ nil
102
+ end
103
+ indicator_method = case indicator
104
+ when :sma, :simple_ma
105
+ TechnicalAnalysis::MovingAverage::SimpleMA.new period: period, strict: strict_mode
106
+ when :ema, :exp_ma
107
+ TechnicalAnalysis::MovingAverage::ExpMA.new period: period, strict: strict_mode
108
+ when :wma
109
+ TechnicalAnalysis::MovingAverage::Wma.new period: period, strict: strict_mode
110
+ when :kama
111
+ TechnicalAnalysis::MovingAverage::KaMA.new period: period, strict: strict_mode,
112
+ fast: fast, slow: slow
113
+ when :rsi
114
+ TechnicalAnalysis::Momentum::Rsi.new period: period
115
+ when :tsi
116
+ TechnicalAnalysis::Momentum::Tsi.new low: low, high: high
117
+ when :lane
118
+ TechnicalAnalysis::Momentum::Lane.new slow: slow, fast: fast, period: period,
119
+ take: choice
120
+ end
121
+ ## iterate across the enumerator and return the result of the calculations
122
+ map.with_index { | d, i |
123
+ # central point to convert to float
124
+ unless indicator == :lane
125
+ raw_data = if date_field.present? || choice.present?
126
+ d.send(choice).to_f
127
+ else
128
+ d.to_f
129
+ end
130
+ indicator_method.add_item(raw_data)
131
+ else
132
+ indicator_method.add_item(d)
133
+ end
134
+
135
+ next if indicator_method.current.nil? # creates a nil entry
136
+ value = indicator_method.current # return this value
137
+ ## data-format of the returned array-elements
138
+ result = if date_field.present?
139
+ struct.new d.send(date_field), value
140
+ else
141
+ value
142
+ end
143
+ # expose the input-data and the calculated indicator to the block
144
+ yielder = yield( d, result) if block_given?
145
+ yielder.nil? ? result : yielder # return the expression from the block if present
146
+ }.compact # map
147
+ end # def
148
+ end
149
+ end
150
+
151
+ ## notes on Enumerators
152
+ #- z:= An Enumerator
153
+ #- z.size == z.count
154
+ #- z.entries == z.to_a
155
+ #- z.take n returns an array of the first n elements
156
+ #- z.sum, if enumerator-objects define a "+" method
157
+ #