iron_warbler 2.0.7.43 → 2.0.7.45

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/iron_warbler/utils.scss +9 -9
  3. data/app/controllers/iro/positions_controller.rb +1 -0
  4. data/app/controllers/iro/purses_controller.rb +3 -1
  5. data/app/controllers/iro/strategies_controller.rb +8 -3
  6. data/app/views/iro/positions/_form.haml +1 -1
  7. data/app/views/iro/positions/{_gameui_long_debit_call_spread.haml-trash → _gameui_spread.haml} +9 -8
  8. data/app/views/iro/positions/_header_spread.haml +12 -0
  9. data/app/views/iro/positions/_new.haml +4 -0
  10. data/app/views/iro/positions/done/_gameui_short_debit_put_spread.haml +1 -0
  11. data/app/views/iro/positions/done/_header_short_debit_put_spread.haml +1 -0
  12. data/app/views/iro/purses/_form.haml +3 -3
  13. data/app/views/iro/purses/index.haml +3 -2
  14. data/app/views/iro/strategies/_form_spread.haml +69 -0
  15. data/app/views/iro/strategies/{_form.haml → _form_wheel.haml} +12 -12
  16. data/app/views/iro/strategies/_header.haml +2 -1
  17. data/app/views/iro/strategies/_table.haml +3 -5
  18. data/app/views/iro/strategies/edit.haml +1 -1
  19. data/app/views/iro/strategies/index.haml +2 -1
  20. data/app/views/iro/strategies/new.haml +1 -1
  21. data/app/views/layouts/iro/application.haml +1 -1
  22. data/config/routes.rb +2 -0
  23. data/lib/iron_warbler.rb +2 -1
  24. data/lib/tasks/iro_tasks.rake +6 -0
  25. metadata +20 -30
  26. data/app/models/iro/alert.rb +0 -63
  27. data/app/models/iro/datapoint.rb +0 -187
  28. data/app/models/iro/date.rb +0 -10
  29. data/app/models/iro/option.rb +0 -151
  30. data/app/models/iro/option_black_scholes.rb +0 -149
  31. data/app/models/iro/position.rb +0 -299
  32. data/app/models/iro/priceitem.rb +0 -93
  33. data/app/models/iro/purse.rb +0 -72
  34. data/app/models/iro/stock.rb +0 -134
  35. data/app/models/iro/strategy.rb +0 -264
  36. data/app/views/iro/positions/_gameui_short_debit_put_spread.haml +0 -1
  37. data/app/views/iro/positions/_gameui_short_debit_put_spread.haml-trash +0 -40
  38. data/app/views/iro/positions/_header_short_debit_put_spread.haml +0 -1
  39. data/app/views/iro/positions/roll.haml-trash +0 -42
  40. /data/app/views/iro/positions/{_gameui_covered_call.haml-bk → done/_gameui_covered_call.haml-bk} +0 -0
  41. /data/app/views/iro/positions/{_gameui_long_credit_put_spread.haml → done/_gameui_long_credit_put_spread.haml} +0 -0
  42. /data/app/views/iro/positions/{_gameui_long_debit_call_spread.haml → done/_gameui_long_debit_call_spread.haml} +0 -0
  43. /data/app/views/iro/positions/{_gameui_short_credit_call_spread.haml → done/_gameui_short_credit_call_spread.haml} +0 -0
  44. /data/app/views/iro/positions/{_header_long_credit_put_spread.haml → done/_header_long_credit_put_spread.haml} +0 -0
  45. /data/app/views/iro/positions/{_header_long_debit_call_spread.haml → done/_header_long_debit_call_spread.haml} +0 -0
  46. /data/app/views/iro/positions/{_header_short_credit_call_spread.haml → done/_header_short_credit_call_spread.haml} +0 -0
  47. /data/app/views/iro/positions/{roll-cc.haml-bk → done/roll-cc.haml-bk} +0 -0
  48. /data/app/views/iro/purses/{gameui.haml-bk → done/gameui.haml-bk} +0 -0
  49. /data/app/views/iro/purses/{gameui.haml-bk2 → done/gameui.haml-bk2} +0 -0
  50. /data/app/views/iro/stocks/{_grid_is_long.haml → _grid_long.haml} +0 -0
  51. /data/app/views/iro/stocks/{_grid_is_short.haml → _grid_short.haml} +0 -0
@@ -1,187 +0,0 @@
1
-
2
- ##
3
- ## Datapoints are at most daily!
4
- ## See Priceitem for intra-day data
5
- ##
6
- class Iro::Datapoint
7
- include Mongoid::Document
8
- include Mongoid::Timestamps
9
- store_in collection: 'iro_datapoints'
10
-
11
- field :kind
12
- validates :kind, presence: true
13
- index({ kind: -1 })
14
- KIND_CRYPTO = 'CRYPTO'
15
- KIND_STOCK = 'STOCK'
16
- KIND_OPTION = 'OPTION' ## but not PUT or CALL
17
- KIND_CURRENCY = 'CURRENCY'
18
- KIND_TREASURY = 'TREASURY'
19
-
20
- field :symbol ## ticker, but use 'symbol' here
21
- ## crypto
22
- SYMBOL_BTC = 'BTC'
23
- SYMBOL_ETH = 'ETH'
24
- ## currencies
25
- SYMBOL_JPY = 'JPY'
26
- SYMBOL_COP = 'COP'
27
- SUMBOL_EUR = 'EUR'
28
- ## treasuries
29
- SYMBOL_T1MO = 'T1MO'
30
- SYMBOL_T2MO = 'T2MO'
31
- SYMBOL_T3MO = 'T3MO'
32
- SYMBOL_T4MO = 'T4MO'
33
- SYMBOL_T6MO = 'T6MO'
34
- SYMBOL_T1YR = 'T1YR'
35
- SYMBOL_T2YR = 'T2YR'
36
- SYMBOL_T3YR = 'T3YR'
37
- SYMBOL_T5YR = 'T5YR'
38
- SYMBOL_T7YR = 'T7YR'
39
- SYMBOL_T10YR = 'T10YR'
40
- SYMBOL_T20YR = 'T20YR'
41
- SYMBOL_T30YR = 'T30YR'
42
-
43
- field :date, type: Date
44
- index({ kind: -1, date: -1 })
45
- validates :date, uniqueness: { scope: [ :symbol ] }
46
-
47
- field :quote_at, type: DateTime
48
- index({ kind: -1, quote_at: -1 })
49
- validates :quote_at, uniqueness: { scope: [ :kind, :symbol ] } ## scope-by-kind is unnecessary here? _vp_ 2024-08-08
50
-
51
- field :open, type: Float
52
- field :high, type: Float
53
- field :low, type: Float
54
- def close; value; end
55
- def close= a; value= a; end
56
-
57
- field :value, type: Float
58
- validates :value, presence: true
59
-
60
-
61
- field :volume, type: Integer
62
-
63
-
64
- def self.test_0trash
65
- add_fields = { '$addFields': {
66
- 'date_string': {
67
- '$dateToString': { 'format': "%Y-%m-%d", 'date': "$created_at" }
68
- }
69
- } }
70
- # group = { '$group': {
71
- # '_id': "$date_string",
72
- # 'my_doc': { '$first': "$$ROOT" }
73
- # } }
74
- group = { '$group': {
75
- '_id': "$date",
76
- 'my_doc': { '$first': "$$ROOT" }
77
- } }
78
- lookup = { '$lookup': {
79
- 'from': 'iro_dates',
80
- 'localField': 'date_string',
81
- 'foreignField': 'date',
82
- 'as': 'dates',
83
- } }
84
- lookup_merge = { '$replaceRoot': {
85
- 'newRoot': { '$mergeObjects': [
86
- { '$arrayElemAt': [ "$dates", 0 ] }, "$$ROOT"
87
- ] }
88
- } }
89
- match = { '$match': {
90
- 'kind': 'some-type',
91
- 'created_at': {
92
- '$gte': '2023-12-01'.to_datetime,
93
- '$lte': '2023-12-31'.to_datetime,
94
- }
95
- } }
96
-
97
- outs = Iro::Datapoint.collection.aggregate([
98
- add_fields,
99
- lookup, lookup_merge,
100
- match,
101
-
102
- { '$sort': { 'date_string': 1 } },
103
- group,
104
- # { '$replaceRoot': { 'newRoot': "$my_doc" } },
105
- # { '$project': { '_id': 0, 'date_string': 1, 'value': 1 } },
106
- ])
107
-
108
- puts! 'result'
109
- pp outs.to_a
110
- # puts! outs.to_a, 'result'
111
- end
112
-
113
- def self.test
114
- lookup = { '$lookup': {
115
- 'from': 'iro_datapoints',
116
- 'localField': 'date',
117
- 'foreignField': 'date',
118
- 'pipeline': [
119
- { '$sort': { 'value': -1 } },
120
- ],
121
- 'as': 'datapoints',
122
- } }
123
- lookup_merge = { '$replaceRoot': {
124
- 'newRoot': { '$mergeObjects': [
125
- { '$arrayElemAt': [ "$datapoints", 0 ] }, "$$ROOT"
126
- ] }
127
- } }
128
-
129
-
130
- match = { '$match': {
131
- 'date': {
132
- '$gte': '2023-12-25',
133
- '$lte': '2023-12-31',
134
- }
135
- } }
136
-
137
- group = { '$group': {
138
- '_id': "$date",
139
- 'my_doc': { '$first': "$$ROOT" }
140
- } }
141
-
142
- outs = Iro::Date.collection.aggregate([
143
- match,
144
-
145
- lookup,
146
- lookup_merge,
147
-
148
- group,
149
- { '$replaceRoot': { 'newRoot': "$my_doc" } },
150
- # { '$replaceRoot': { 'newRoot': "$my_doc" } },
151
-
152
-
153
- { '$project': { '_id': 0, 'date': 1, 'value': 1 } },
154
- { '$sort': { 'date': 1 } },
155
- ])
156
-
157
- puts! 'result'
158
- pp outs.to_a
159
- # puts! outs.to_a, 'result'
160
- end
161
-
162
- def self.import_stock symbol:, path:
163
- csv = CSV.read(path, headers: true)
164
- csv.each do |row|
165
- flag = create({
166
- kind: KIND_STOCK,
167
- symbol: symbol,
168
- date: row['Date'],
169
- quote_at: row['Date'],
170
-
171
- volume: row['Volume'],
172
-
173
- open: row['Open'],
174
- high: row['High'],
175
- low: row['Low'],
176
- value: row['Close'],
177
- })
178
- if flag.persisted?
179
- print '^'
180
- else
181
- puts flag.errors.messages
182
- end
183
- end
184
- puts 'ok'
185
- end
186
-
187
- end
@@ -1,10 +0,0 @@
1
-
2
- class Iro::Date
3
- include Mongoid::Document
4
- # include Mongoid::Timestamps
5
- store_in collection: 'iro_dates'
6
-
7
- field :date
8
-
9
- end
10
-
@@ -1,151 +0,0 @@
1
-
2
- class Iro::Option
3
- include Mongoid::Document
4
- include Mongoid::Timestamps
5
- include Mongoid::Paranoia
6
- include Iro::OptionBlackScholes
7
- store_in collection: 'iro_options'
8
-
9
- attr_accessor :recompute
10
-
11
- belongs_to :stock, class_name: 'Iro::Stock', inverse_of: :strategies
12
- def ticker; stock.ticker; end
13
- # field :ticker
14
- # validates :ticker, presence: true
15
-
16
- field :symbol
17
- ## each option can be a leg in a position, no uniqueness
18
- # validates :symbol, uniqueness: true, presence: true
19
-
20
- field :put_call, type: :string # 'PUT' or 'CALL'
21
- validates :put_call, presence: true
22
-
23
- field :delta, type: :float
24
-
25
- field :strike, type: :float
26
- validates :strike, presence: true
27
-
28
- field :expires_on, type: :date
29
- validates :expires_on, presence: true
30
- def self.expirations_list full: false, n: 5
31
- out = [[nil,nil]]
32
- day = Date.today - 5.days
33
- n.times do
34
- next_exp = day.next_occurring(:thursday).next_occurring(:friday)
35
- if !next_exp.workday?
36
- next_exp = Time.previous_business_day( next_exp )
37
- end
38
-
39
- out.push([ next_exp.strftime('%b %e'), next_exp.strftime('%Y-%m-%d') ])
40
- day = next_exp
41
- end
42
- return out
43
- # [
44
- # [ nil, nil ],
45
- # [ 'Mar 22', '2024-03-22'.to_date ],
46
- # [ 'Mar 28', '2024-03-28'.to_date ],
47
- # [ 'Apr 5', '2024-04-05'.to_date ],
48
- # [ 'Mar 12', '2024-03-12'.to_date ],
49
- # [ 'Mar 19', '2024-03-19'.to_date ],
50
- # ]
51
- end
52
-
53
- field :begin_price, type: :float
54
- field :begin_delta, type: :float
55
- field :end_price, type: :float
56
- field :end_delta, type: :float
57
-
58
-
59
- has_one :outer, class_name: 'Iro::Position', inverse_of: :outer
60
- has_one :inner, class_name: 'Iro::Position', inverse_of: :inner
61
-
62
- field :last, type: :float
63
-
64
- ## for TDA
65
- def symbol
66
- if !self[:symbol]
67
- p_c_ = put_call == 'PUT' ? 'P' : 'C'
68
- strike_ = strike.to_i == strike ? strike.to_i : strike
69
- sym = "#{stock.ticker}_#{expires_on.strftime("%m%d%y")}#{p_c_}#{strike_}" # XYZ_011819P45
70
- self[:symbol] = sym
71
- save
72
- end
73
- self[:symbol]
74
- end
75
-
76
- before_save :sync, if: ->() { !Rails.env.test? } ## do not sync in test
77
- def sync
78
- out = Tda::Option.get_quote({
79
- contractType: put_call,
80
- strike: strike,
81
- expirationDate: expires_on,
82
- ticker: ticker,
83
- })
84
- puts! out, 'option sync'
85
- self.end_price = ( out.bid + out.ask ) / 2 rescue 0
86
- self.end_delta = out.delta if out.delta
87
- # self.save
88
- end
89
-
90
- def self.max_pain hash
91
- outs = {}
92
-
93
- %w| put call |.each do |contractType|
94
- dates = hash["#{contractType}ExpDateMap"]
95
- dates.each do |_date, strikes| ## _date="2023-02-10:5"
96
- date = _date.split(':')[0].to_date.to_s
97
- outs[date] ||= {
98
- 'all' => {},
99
- 'call' => {},
100
- 'put' => {},
101
- 'summary' => {},
102
- }
103
-
104
- strikes.each do |_strike, _v| ## _strike="18.5"
105
- strike = _strike.to_f
106
-
107
- ## calls
108
- mem_c = 0
109
- strikes.keys.reverse.each do |_key|
110
- if _key == _strike
111
- break
112
- end
113
- key = _key.to_f
114
- tmp = hash["callExpDateMap"][_date][_key][0]['openInterest'] * ( key - strike )
115
- mem_c += tmp
116
- end
117
- outs[date]['call'][_strike] = mem_c
118
-
119
- ## puts
120
- mem_p = 0
121
- strikes.keys.each do |_key|
122
- if _key == _strike
123
- break
124
- end
125
- key = _key.to_f
126
- tmp = hash["putExpDateMap"][_date][_key][0]['openInterest'] * ( strike - key )
127
- mem_p += tmp
128
- end
129
- outs[date]['put'][_strike] = mem_p
130
- outs[date]['all'][_strike] = mem_c + mem_p
131
-
132
- end
133
- end
134
- end
135
-
136
- ## compute summary
137
- outs.each do |date, types|
138
- all = types['all']
139
- outs[date]['summary'] = { 'value' => all.keys[0] }
140
- all.each do |strike, amount|
141
- if amount < all[ outs[date]['summary']['value'] ]
142
- outs[date]['summary']['value'] = strike
143
- end
144
- end
145
- end
146
-
147
- return outs
148
- end
149
-
150
-
151
- end
@@ -1,149 +0,0 @@
1
-
2
- require 'distribution'
3
- N = Distribution::Normal
4
-
5
- module Iro::OptionBlackScholes
6
-
7
- ##
8
- ## black-scholes pricing
9
- ##
10
-
11
- =begin
12
- ##
13
- ##
14
- ##
15
- annual to daily:
16
-
17
- AR = ((DR + 1)^365 – 1) x 100
18
-
19
- ##
20
- ##
21
- ##
22
- From: https://www.investopedia.com/articles/optioninvestor/07/options_beat_market.asp
23
-
24
- K :: strike price
25
- S_t :: last
26
- r :: risk-free rate
27
- t :: time to maturity
28
-
29
- C = S_t N( d1 ) - K e^-rt N( d2 )
30
-
31
- d1 = ln( St / K ) + (r + theta**2 / 2 )t
32
- /{ theta_s * sqrt( t ) }
33
-
34
- d2 = d1 - theta_s sqrt( t )
35
-
36
- ##
37
- ## From: https://en.wikipedia.org/wiki/Black%E2%80%93Scholes_model
38
- ##
39
-
40
- D :: e^(rt) # discount factor
41
- F :: e^(rt) S # forward price of underlying
42
-
43
- C(F,t) = D[ N(d1)F - N(d2)K ]
44
-
45
- d1 = ln(F/K) + stdev**2 t / 2
46
- /{ stdev sqrt(t) }
47
- d2 = d1 - stdev sqrt(t)
48
-
49
- ##
50
- ## From: https://www.daytrading.com/options-pricing-models
51
- ##
52
- C0 = S0N(d1) – Xe-rtN(d2)
53
-
54
- C0 = current call premium
55
- S0 = current stock price
56
- N(d1) = the probability that a value in a normal distribution will be less than d
57
- N(d2) = the probability that the option will be in the money by expiration
58
- X = strike price of the option
59
- T = time until expiration (expressed in years)
60
- r = risk-free interest rate
61
- e = 2.71828, the base of the natural logarithm
62
- ln = natural logarithm function
63
- σ = standard deviation of the stock’s annualized rate of return (compounded continuously)
64
- d1 = ln(S0/X) + (r + σ2/2)Tσ√T
65
-
66
- d2 = d1 – σ√T
67
-
68
- Note that:
69
-
70
- Xe-rt = X/ert = the present value of the strike price using a continuously compounded interest rate
71
-
72
- ##
73
- ## From: https://www.wallstreetmojo.com/black-scholes-model/
74
- ##
75
-
76
-
77
- ## init
78
- require 'distribution'
79
- N = Distribution::Normal
80
- stock = Iro::Stock.find_by ticker: 'NVDA'
81
- strike = 910.0
82
- r = Iro::Option.rate_daily
83
- stdev = 91.0
84
- t = 7.0
85
- expires_on = '2024-03-22'
86
-
87
- =end
88
- def d1
89
- last = stock.last
90
- r = self.class.rate_annual
91
-
92
- out = Math.log( last / strike ) + ( r + stdev**2 / 2 ) * t
93
- out = out /( stdev * Math.sqrt(t) )
94
- return out
95
- end
96
- def d2
97
- last = stock.last
98
- r = self.class.rate_annual
99
-
100
- out = d1 - stdev * Math.sqrt( t )
101
- return out
102
- end
103
- def t
104
- # t = 1.0 / 365 * Date.today.business_days_until( expires_on )
105
- t = 1.0 / 365 * (expires_on - Date.today).to_i
106
- end
107
- def stdev
108
- recompute = nil
109
- stock.stdev( recompute: recompute )
110
- end
111
- def call_price
112
- last = stock.last
113
- r = self.class.rate_annual
114
-
115
- out = N.cdf( d1 ) * last - N.cdf( d2 ) * strike * Math::E**( -1 * r * t )
116
- return out
117
- end
118
-
119
- def put_price
120
- last = stock.last
121
- r = self.class.rate_annual
122
-
123
- out = N.cdf(-d2) * strike * exp(-r*t) - N.cdf(-d1) * last
124
- return out
125
- end
126
-
127
-
128
- def self.rate_annual
129
- 0.05
130
- end
131
-
132
- =begin
133
- # test
134
-
135
- inn = 100
136
- n.times { inn = inn*(1.0+out) }
137
- inn
138
-
139
- =end
140
- def self.rate_daily
141
- n = 250.0 # days
142
- # n = 12 # months
143
-
144
- out = (1.0+self.rate_annual)**(1.0/n) - 1.0
145
- puts! out, 'rate_daily'
146
- return out
147
- end
148
-
149
- end