ib-technical-analysis 0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ #