iron_warbler 2.0.7.43 → 2.0.7.45

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