sqa 0.0.37 → 0.0.38

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +62 -0
  3. data/docs/api-reference/alphavantageapi.md +105 -105
  4. data/docs/api-reference/apierror.md +2 -2
  5. data/docs/api-reference/index.md +2 -2
  6. data/docs/api-reference/notimplemented.md +2 -2
  7. data/docs/api-reference/sqa.md +12 -12
  8. data/docs/api-reference/sqa_backtest.md +43 -9
  9. data/docs/api-reference/sqa_backtest_results.md +34 -34
  10. data/docs/api-reference/sqa_badparametererror.md +1 -1
  11. data/docs/api-reference/sqa_config.md +31 -31
  12. data/docs/api-reference/sqa_configurationerror.md +1 -1
  13. data/docs/api-reference/sqa_datafetcherror.md +3 -3
  14. data/docs/api-reference/sqa_dataframe.md +63 -36
  15. data/docs/api-reference/sqa_dataframe_alphavantage.md +2 -2
  16. data/docs/api-reference/sqa_dataframe_data.md +18 -18
  17. data/docs/api-reference/sqa_dataframe_yahoofinance.md +2 -2
  18. data/docs/api-reference/sqa_ensemble.md +21 -21
  19. data/docs/api-reference/sqa_fpop.md +8 -8
  20. data/docs/api-reference/sqa_geneticprogram.md +20 -20
  21. data/docs/api-reference/sqa_geneticprogram_individual.md +8 -8
  22. data/docs/api-reference/sqa_marketregime.md +10 -10
  23. data/docs/api-reference/sqa_multitimeframe.md +11 -11
  24. data/docs/api-reference/sqa_patternmatcher.md +9 -9
  25. data/docs/api-reference/sqa_pluginmanager.md +4 -4
  26. data/docs/api-reference/sqa_portfolio.md +84 -27
  27. data/docs/api-reference/sqa_portfolio_position.md +12 -12
  28. data/docs/api-reference/sqa_portfolio_trade.md +16 -16
  29. data/docs/api-reference/sqa_portfoliooptimizer.md +10 -10
  30. data/docs/api-reference/sqa_riskmanager.md +12 -12
  31. data/docs/api-reference/sqa_seasonalanalyzer.md +6 -6
  32. data/docs/api-reference/sqa_sectoranalyzer.md +9 -9
  33. data/docs/api-reference/sqa_stock.md +48 -36
  34. data/docs/api-reference/sqa_strategy.md +8 -8
  35. data/docs/api-reference/sqa_strategy_bollingerbands.md +2 -2
  36. data/docs/api-reference/sqa_strategy_common.md +3 -3
  37. data/docs/api-reference/sqa_strategy_consensus.md +12 -12
  38. data/docs/api-reference/sqa_strategy_ema.md +4 -4
  39. data/docs/api-reference/sqa_strategy_kbs.md +11 -11
  40. data/docs/api-reference/sqa_strategy_macd.md +2 -2
  41. data/docs/api-reference/sqa_strategy_mp.md +4 -4
  42. data/docs/api-reference/sqa_strategy_mr.md +4 -4
  43. data/docs/api-reference/sqa_strategy_random.md +4 -4
  44. data/docs/api-reference/sqa_strategy_rsi.md +4 -4
  45. data/docs/api-reference/sqa_strategy_sma.md +4 -4
  46. data/docs/api-reference/sqa_strategy_stochastic.md +2 -2
  47. data/docs/api-reference/sqa_strategy_volumebreakout.md +2 -2
  48. data/docs/api-reference/sqa_strategygenerator.md +19 -19
  49. data/docs/api-reference/sqa_strategygenerator_pattern.md +17 -17
  50. data/docs/api-reference/sqa_strategygenerator_patterncontext.md +21 -21
  51. data/docs/api-reference/sqa_strategygenerator_profitablepoint.md +27 -27
  52. data/docs/api-reference/sqa_stream.md +18 -18
  53. data/docs/api-reference/sqa_ticker.md +8 -8
  54. data/docs/api-reference/string.md +11 -11
  55. data/docs/file_formats.md +250 -0
  56. data/lib/sqa/backtest.rb +32 -0
  57. data/lib/sqa/data_frame.rb +25 -0
  58. data/lib/sqa/portfolio.rb +54 -0
  59. data/lib/sqa/stock.rb +11 -0
  60. data/lib/sqa/version.rb +1 -1
  61. data/mkdocs.yml +1 -0
  62. metadata +2 -2
  63. data/docs/IMPROVEMENT_PLAN.md +0 -531
@@ -5,7 +5,7 @@
5
5
  Desc: Monkey to the String class.
6
6
 
7
7
  !!! abstract "Source Information"
8
- **Defined in:** `lib/patches/string.rb:10`
8
+ **Defined in:** [`lib/patches/string.rb:10`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L10)
9
9
 
10
10
  **Inherits from:** `Object`
11
11
 
@@ -19,7 +19,7 @@ Convert CamelCase to camel_case
19
19
 
20
20
 
21
21
  ??? info "Source Location"
22
- `lib/patches/string.rb:14`
22
+ [`lib/patches/string.rb:14`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L14)
23
23
 
24
24
  ---
25
25
 
@@ -31,7 +31,7 @@ Convert CamelCase to camel_case
31
31
 
32
32
 
33
33
  ??? info "Source Location"
34
- `lib/patches/string.rb:21`
34
+ [`lib/patches/string.rb:21`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L21)
35
35
 
36
36
  ---
37
37
 
@@ -43,7 +43,7 @@ Convert CamelCase to camel_case
43
43
 
44
44
 
45
45
  ??? info "Source Location"
46
- `lib/patches/string.rb:22`
46
+ [`lib/patches/string.rb:22`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L22)
47
47
 
48
48
  ---
49
49
 
@@ -55,7 +55,7 @@ Convert CamelCase to camel_case
55
55
 
56
56
 
57
57
  ??? info "Source Location"
58
- `lib/patches/string.rb:23`
58
+ [`lib/patches/string.rb:23`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L23)
59
59
 
60
60
  ---
61
61
 
@@ -67,7 +67,7 @@ Convert CamelCase to camel_case
67
67
 
68
68
 
69
69
  ??? info "Source Location"
70
- `lib/patches/string.rb:24`
70
+ [`lib/patches/string.rb:24`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L24)
71
71
 
72
72
  ---
73
73
 
@@ -79,7 +79,7 @@ Convert camel_case to CamelCase
79
79
 
80
80
 
81
81
  ??? info "Source Location"
82
- `lib/patches/string.rb:29`
82
+ [`lib/patches/string.rb:29`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L29)
83
83
 
84
84
  ---
85
85
 
@@ -91,7 +91,7 @@ Convert camel_case to CamelCase
91
91
 
92
92
 
93
93
  ??? info "Source Location"
94
- `lib/patches/string.rb:34`
94
+ [`lib/patches/string.rb:34`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L34)
95
95
 
96
96
  ---
97
97
 
@@ -103,7 +103,7 @@ Convert camel_case to CamelCase
103
103
 
104
104
 
105
105
  ??? info "Source Location"
106
- `lib/patches/string.rb:35`
106
+ [`lib/patches/string.rb:35`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L35)
107
107
 
108
108
  ---
109
109
 
@@ -115,7 +115,7 @@ Convert "CamelCase" into CamelCase
115
115
 
116
116
 
117
117
  ??? info "Source Location"
118
- `lib/patches/string.rb:40`
118
+ [`lib/patches/string.rb:40`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L40)
119
119
 
120
120
  ---
121
121
 
@@ -127,7 +127,7 @@ Convert "CamelCase" into CamelCase
127
127
 
128
128
 
129
129
  ??? info "Source Location"
130
- `lib/patches/string.rb:55`
130
+ [`lib/patches/string.rb:55`](https://github.com/madbomber/sqa/blob/main/lib/patches/string.rb#L55)
131
131
 
132
132
  ---
133
133
 
@@ -0,0 +1,250 @@
1
+ # File Formats
2
+
3
+ This document describes the CSV file formats used by SQA for data import and export.
4
+
5
+ ## Portfolio CSV Formats
6
+
7
+ The `SQA::Portfolio` class supports two CSV file formats for different purposes: positions (holdings) and trade history.
8
+
9
+ ### Portfolio Positions CSV
10
+
11
+ This format is used to save and load current portfolio holdings.
12
+
13
+ **Methods:**
14
+ - `portfolio.save_to_csv(filename)` - Save positions
15
+ - `SQA::Portfolio.load_from_csv(filename)` - Load positions
16
+
17
+ **Schema:**
18
+
19
+ | Column | Type | Description |
20
+ |--------|------|-------------|
21
+ | `ticker` | String | Stock ticker symbol (e.g., 'AAPL', 'MSFT') |
22
+ | `shares` | Integer | Number of shares currently held |
23
+ | `avg_cost` | Float | Average cost per share (cost basis) |
24
+ | `total_cost` | Float | Total cost basis for the entire position |
25
+
26
+ **Example File (`portfolio.csv`):**
27
+
28
+ ```csv
29
+ ticker,shares,avg_cost,total_cost
30
+ AAPL,100,150.0,15000.0
31
+ MSFT,50,300.0,15000.0
32
+ GOOG,25,120.0,3000.0
33
+ ```
34
+
35
+ **Usage:**
36
+
37
+ ```ruby
38
+ # Save current positions to CSV
39
+ portfolio = SQA::Portfolio.new(initial_cash: 100_000)
40
+ portfolio.buy('AAPL', shares: 100, price: 150.0)
41
+ portfolio.buy('MSFT', shares: 50, price: 300.0)
42
+ portfolio.save_to_csv('my_portfolio.csv')
43
+
44
+ # Load positions from CSV
45
+ loaded_portfolio = SQA::Portfolio.load_from_csv('my_portfolio.csv')
46
+ loaded_portfolio.position('AAPL').shares # => 100
47
+ ```
48
+
49
+ **Notes:**
50
+ - The CSV includes only current positions (open holdings)
51
+ - Cash balance is NOT saved in the CSV (set via `initial_cash` parameter when loading)
52
+ - Use this format for portfolio snapshots and position tracking
53
+
54
+ ---
55
+
56
+ ### Trade History CSV
57
+
58
+ This format is used to export the complete log of all buy and sell transactions.
59
+
60
+ **Methods:**
61
+ - `portfolio.save_trades_to_csv(filename)` - Export trade history
62
+
63
+ **Schema:**
64
+
65
+ | Column | Type | Description |
66
+ |--------|------|-------------|
67
+ | `date` | Date | Trade execution date (YYYY-MM-DD format) |
68
+ | `ticker` | String | Stock ticker symbol |
69
+ | `action` | Symbol | Trade type: `buy` or `sell` |
70
+ | `shares` | Integer | Number of shares traded |
71
+ | `price` | Float | Price per share at execution |
72
+ | `total` | Float | Total transaction value (shares × price) |
73
+ | `commission` | Float | Commission paid for the trade |
74
+
75
+ **Example File (`trades.csv`):**
76
+
77
+ ```csv
78
+ date,ticker,action,shares,price,total,commission
79
+ 2024-01-15,AAPL,buy,100,150.0,15000.0,1.0
80
+ 2024-01-20,MSFT,buy,50,300.0,15000.0,1.0
81
+ 2024-02-05,AAPL,sell,50,160.0,8000.0,1.0
82
+ 2024-02-15,GOOG,buy,25,120.0,3000.0,1.0
83
+ 2024-03-01,MSFT,sell,25,310.0,7750.0,1.0
84
+ ```
85
+
86
+ **Usage:**
87
+
88
+ ```ruby
89
+ # Execute some trades
90
+ portfolio = SQA::Portfolio.new(initial_cash: 100_000, commission: 1.0)
91
+ portfolio.buy('AAPL', shares: 100, price: 150.0, date: Date.parse('2024-01-15'))
92
+ portfolio.buy('MSFT', shares: 50, price: 300.0, date: Date.parse('2024-01-20'))
93
+ portfolio.sell('AAPL', shares: 50, price: 160.0, date: Date.parse('2024-02-05'))
94
+
95
+ # Export complete trade history
96
+ portfolio.save_trades_to_csv('trade_history.csv')
97
+ ```
98
+
99
+ **Notes:**
100
+ - This format is write-only (no corresponding `load_trades_from_csv()` method)
101
+ - Use for audit trails, tax reporting, and performance analysis
102
+ - Each row represents a single executed trade
103
+ - The `total` column does NOT include commission (commission is tracked separately)
104
+
105
+ ---
106
+
107
+ ## Stock Data CSV Format
108
+
109
+ Stock price and volume data is stored in CSV format with the following schema:
110
+
111
+ **Schema:**
112
+
113
+ | Column | Type | Description |
114
+ |--------|------|-------------|
115
+ | `timestamp` | String | Date in YYYY-MM-DD format |
116
+ | `open_price` | Float | Opening price |
117
+ | `high_price` | Float | Highest price during the period |
118
+ | `low_price` | Float | Lowest price during the period |
119
+ | `close_price` | Float | Closing price |
120
+ | `adj_close_price` | Float | Adjusted closing price (accounts for splits/dividends) |
121
+ | `volume` | Integer | Trading volume |
122
+
123
+ **Example File (`aapl.csv`):**
124
+
125
+ ```csv
126
+ timestamp,open_price,high_price,low_price,close_price,adj_close_price,volume
127
+ 2023-01-03,130.28,130.90,124.17,125.07,124.38,112117500
128
+ 2023-01-04,126.89,128.66,125.08,126.36,125.66,89113600
129
+ 2023-01-05,127.13,127.77,124.76,125.02,124.33,80962700
130
+ ```
131
+
132
+ **Location:**
133
+ - Stock CSV files are stored in `~/sqa_data/` by default (configurable via `SQA::Config`)
134
+ - File naming convention: `{ticker}.csv` (e.g., `aapl.csv`, `msft.csv`)
135
+
136
+ **Data Ordering:**
137
+ - **CRITICAL:** Data MUST be in ascending chronological order (oldest first, newest last)
138
+ - This ordering is required for TA-Lib compatibility
139
+ - Index [0] = oldest data point, Index [last] = newest data point
140
+
141
+ **Usage:**
142
+
143
+ ```ruby
144
+ # Stock data is automatically loaded/saved
145
+ stock = SQA::Stock.new(ticker: 'AAPL')
146
+ stock.df.to_csv('aapl_export.csv') # Export to custom location
147
+
148
+ # Load from custom CSV
149
+ df = SQA::DataFrame.load(source: 'path/to/custom.csv')
150
+ ```
151
+
152
+ **Notes:**
153
+ - Data is automatically fetched from Alpha Vantage or Yahoo Finance on first load
154
+ - Updates are appended and deduplicated using `concat_and_deduplicate!`
155
+ - Use `adj_close_price` for calculations that need to account for corporate actions
156
+
157
+ ---
158
+
159
+ ## Stock Metadata JSON Format
160
+
161
+ Stock metadata (company information) is stored in JSON format alongside CSV files.
162
+
163
+ **Location:** `~/sqa_data/{ticker}.json`
164
+
165
+ **Example File (`aapl.json`):**
166
+
167
+ ```json
168
+ {
169
+ "ticker": "aapl",
170
+ "name": "Apple Inc.",
171
+ "exchange": "NASDAQ",
172
+ "source": "alpha_vantage",
173
+ "indicators": {},
174
+ "overview": {
175
+ "symbol": "AAPL",
176
+ "asset_type": "Common Stock",
177
+ "name": "Apple Inc.",
178
+ "exchange": "NASDAQ",
179
+ "currency": "USD",
180
+ "country": "USA",
181
+ "sector": "TECHNOLOGY",
182
+ "industry": "ELECTRONIC COMPUTERS",
183
+ "market_capitalization": 2500000000000,
184
+ "pe_ratio": 28.5,
185
+ "eps": 6.05,
186
+ "dividend_per_share": 0.92,
187
+ "dividend_yield": 0.0055
188
+ }
189
+ }
190
+ ```
191
+
192
+ **Usage:**
193
+
194
+ ```ruby
195
+ stock = SQA::Stock.new(ticker: 'AAPL')
196
+ stock.data.overview['market_capitalization'] # => 2500000000000
197
+ stock.data.overview['pe_ratio'] # => 28.5
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Configuration File Format
203
+
204
+ SQA supports YAML and TOML configuration files.
205
+
206
+ **Location:** `~/.sqa.yml` or `~/.sqa.toml`
207
+
208
+ **Example YAML (`~/.sqa.yml`):**
209
+
210
+ ```yaml
211
+ data_dir: ~/sqa_data
212
+ lazy_update: false
213
+ log_level: info
214
+ plotting_library: gnuplot
215
+ ```
216
+
217
+ **Example TOML (`~/.sqa.toml`):**
218
+
219
+ ```toml
220
+ data_dir = "~/sqa_data"
221
+ lazy_update = false
222
+ log_level = "info"
223
+ plotting_library = "gnuplot"
224
+ ```
225
+
226
+ **Available Configuration Options:**
227
+
228
+ | Option | Type | Default | Description |
229
+ |--------|------|---------|-------------|
230
+ | `data_dir` | String | `~/sqa_data` | Directory for storing stock data files |
231
+ | `lazy_update` | Boolean | `false` | If true, skip automatic data updates |
232
+ | `log_level` | Symbol | `:info` | Logging level (`:debug`, `:info`, `:warn`, `:error`) |
233
+ | `plotting_library` | Symbol | `:gnuplot` | Plotting library to use |
234
+
235
+ **Usage:**
236
+
237
+ ```ruby
238
+ # Load custom configuration
239
+ config = SQA::Config.new(data_dir: '/path/to/data', lazy_update: true)
240
+ SQA.init(config)
241
+ ```
242
+
243
+ ---
244
+
245
+ ## See Also
246
+
247
+ - [API Reference - Portfolio](api-reference/sqa_portfolio.md)
248
+ - [API Reference - Stock](api-reference/sqa_stock.md)
249
+ - [API Reference - DataFrame](api-reference/sqa_dataframe.md)
250
+ - [Data Frame Documentation](data_frame.md)
data/lib/sqa/backtest.rb CHANGED
@@ -99,6 +99,38 @@ class SQA::Backtest
99
99
 
100
100
  # Run the backtest
101
101
  # @return [Results] Backtest results
102
+ #
103
+ # @example Run backtest with RSI strategy
104
+ # stock = SQA::Stock.new(ticker: 'AAPL')
105
+ # backtest = SQA::Backtest.new(
106
+ # stock: stock,
107
+ # strategy: SQA::Strategy::RSI,
108
+ # initial_capital: 10_000,
109
+ # commission: 1.0
110
+ # )
111
+ # results = backtest.run
112
+ # puts results.summary
113
+ # # => Total Return: 15.5%
114
+ # # Sharpe Ratio: 1.2
115
+ # # Max Drawdown: -8.3%
116
+ # # Win Rate: 65%
117
+ #
118
+ # @example Backtest with custom date range
119
+ # backtest = SQA::Backtest.new(
120
+ # stock: stock,
121
+ # strategy: SQA::Strategy::MACD,
122
+ # start_date: '2023-01-01',
123
+ # end_date: '2023-12-31'
124
+ # )
125
+ # results = backtest.run
126
+ # results.total_return # => 0.155 (15.5%)
127
+ #
128
+ # @example Access equity curve for plotting
129
+ # results = backtest.run
130
+ # backtest.equity_curve.each do |point|
131
+ # puts "#{point[:date]}: $#{point[:value]}"
132
+ # end
133
+ #
102
134
  def run
103
135
  # Get data for the backtest period
104
136
  df = @stock.df.data
@@ -132,6 +132,23 @@ class SQA::DataFrame
132
132
  #
133
133
  # NOTE: TA-Lib requires data in ascending (oldest-first) order. Using descending: true
134
134
  # will produce a warning and force ascending order to prevent silent calculation errors.
135
+ #
136
+ # @example Merge new data with deduplication
137
+ # stock = SQA::Stock.new(ticker: 'AAPL')
138
+ # df = stock.df
139
+ # df.size # => 252
140
+ #
141
+ # # Fetch recent data (may have overlapping dates)
142
+ # new_df = SQA::DataFrame::AlphaVantage.recent('AAPL', from_date: Date.today - 7)
143
+ # df.concat_and_deduplicate!(new_df)
144
+ # # Duplicates removed, data sorted ascending (oldest first)
145
+ # df.size # => 255 (only 3 new unique dates added)
146
+ #
147
+ # @example Maintains TA-Lib compatibility
148
+ # df.concat_and_deduplicate!(new_df) # Sorted ascending automatically
149
+ # prices = df["adj_close_price"].to_a
150
+ # rsi = SQAI.rsi(prices, period: 14) # Works correctly with ascending data
151
+ #
135
152
  def concat_and_deduplicate!(other_df, sort_column: "timestamp", descending: false)
136
153
  # Enforce ascending order for TA-Lib compatibility
137
154
  if descending
@@ -184,6 +201,14 @@ class SQA::DataFrame
184
201
  #
185
202
  # @param path_to_file [String, Pathname] Path to output CSV file
186
203
  # @return [void]
204
+ #
205
+ # @example Save stock data to CSV
206
+ # stock = SQA::Stock.new(ticker: 'AAPL')
207
+ # stock.df.to_csv('aapl_prices.csv')
208
+ #
209
+ # @example Export with custom path
210
+ # df.to_csv(Pathname.new('data/exports/prices.csv'))
211
+ #
187
212
  def to_csv(path_to_file)
188
213
  @data.write_csv(path_to_file)
189
214
  end
data/lib/sqa/portfolio.rb CHANGED
@@ -52,6 +52,19 @@ class SQA::Portfolio
52
52
  # @param price [Float] Price per share
53
53
  # @param date [Date] Date of trade
54
54
  # @return [Trade] The executed trade
55
+ #
56
+ # @example Buy 10 shares of AAPL
57
+ # portfolio = SQA::Portfolio.new(initial_cash: 10_000, commission: 1.0)
58
+ # trade = portfolio.buy('AAPL', shares: 10, price: 150.0)
59
+ # trade.action # => :buy
60
+ # trade.total # => 1500.0
61
+ # portfolio.cash # => 8499.0 (10_000 - 1500 - 1.0 commission)
62
+ #
63
+ # @example Buy multiple stocks
64
+ # portfolio.buy('AAPL', shares: 10, price: 150.0)
65
+ # portfolio.buy('MSFT', shares: 5, price: 300.0)
66
+ # portfolio.positions.size # => 2
67
+ #
55
68
  def buy(ticker, shares:, price:, date: Date.today)
56
69
  raise BadParameterError, "Shares must be positive" if shares <= 0
57
70
  raise BadParameterError, "Price must be positive" if price <= 0
@@ -95,6 +108,19 @@ class SQA::Portfolio
95
108
  # @param price [Float] Price per share
96
109
  # @param date [Date] Date of trade
97
110
  # @return [Trade] The executed trade
111
+ #
112
+ # @example Sell entire position for profit
113
+ # portfolio = SQA::Portfolio.new(initial_cash: 10_000, commission: 1.0)
114
+ # portfolio.buy('AAPL', shares: 10, price: 150.0)
115
+ # trade = portfolio.sell('AAPL', shares: 10, price: 160.0)
116
+ # trade.total # => 1600.0
117
+ # portfolio.cash # => 8498.0 + 1599.0 = 10097.0 (after commissions)
118
+ #
119
+ # @example Partial sale
120
+ # portfolio.buy('AAPL', shares: 100, price: 150.0)
121
+ # portfolio.sell('AAPL', shares: 50, price: 160.0) # Sell half
122
+ # portfolio.position('AAPL').shares # => 50
123
+ #
98
124
  def sell(ticker, shares:, price:, date: Date.today)
99
125
  raise BadParameterError, "Shares must be positive" if shares <= 0
100
126
  raise BadParameterError, "Price must be positive" if price <= 0
@@ -145,6 +171,18 @@ class SQA::Portfolio
145
171
  # Calculate total portfolio value
146
172
  # @param current_prices [Hash] Hash of ticker => current_price
147
173
  # @return [Float] Total portfolio value (cash + positions)
174
+ #
175
+ # @example Calculate portfolio value with current prices
176
+ # portfolio = SQA::Portfolio.new(initial_cash: 10_000)
177
+ # portfolio.buy('AAPL', shares: 10, price: 150.0)
178
+ # portfolio.buy('MSFT', shares: 5, price: 300.0)
179
+ #
180
+ # current_prices = { 'AAPL' => 160.0, 'MSFT' => 310.0 }
181
+ # portfolio.value(current_prices) # => 10_000 - 1500 - 1500 + 1600 + 1550 = 10_150
182
+ #
183
+ # @example Without current prices (uses avg_cost)
184
+ # portfolio.value # Uses purchase prices if no current prices provided
185
+ #
148
186
  def value(current_prices = {})
149
187
  positions_value = @positions.sum do |ticker, pos|
150
188
  current_price = current_prices[ticker] || pos.avg_cost
@@ -186,6 +224,22 @@ class SQA::Portfolio
186
224
  # Get summary statistics
187
225
  # @param current_prices [Hash] Hash of ticker => current_price
188
226
  # @return [Hash] Summary statistics
227
+ #
228
+ # @example Get portfolio performance summary
229
+ # portfolio = SQA::Portfolio.new(initial_cash: 10_000, commission: 1.0)
230
+ # portfolio.buy('AAPL', shares: 10, price: 150.0)
231
+ # portfolio.sell('AAPL', shares: 5, price: 160.0)
232
+ #
233
+ # summary = portfolio.summary({ 'AAPL' => 165.0 })
234
+ # summary[:initial_cash] # => 10_000.0
235
+ # summary[:current_cash] # => 8798.0
236
+ # summary[:positions_count] # => 1
237
+ # summary[:total_value] # => 9623.0
238
+ # summary[:profit_loss_percent] # => -3.77%
239
+ # summary[:total_trades] # => 2
240
+ # summary[:buy_trades] # => 1
241
+ # summary[:sell_trades] # => 1
242
+ #
189
243
  def summary(current_prices = {})
190
244
  {
191
245
  initial_cash: @initial_cash,
data/lib/sqa/stock.rb CHANGED
@@ -131,6 +131,17 @@ class SQA::Stock
131
131
  # Silently handles errors since overview data is optional.
132
132
  #
133
133
  # @return [void]
134
+ #
135
+ # @example Update stock metadata from API
136
+ # stock = SQA::Stock.new(ticker: 'AAPL')
137
+ # stock.update # Fetches latest company overview data
138
+ # stock.data.overview['market_capitalization'] # => 2500000000000
139
+ # stock.data.overview['pe_ratio'] # => 28.5
140
+ #
141
+ # @example Update is safe if API fails
142
+ # stock.update # No error raised if API is unavailable
143
+ # # Warning logged but stock remains usable with cached data
144
+ #
134
145
  def update
135
146
  begin
136
147
  merge_overview
data/lib/sqa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SQA
4
- VERSION = '0.0.37'
4
+ VERSION = '0.0.38'
5
5
  end
data/mkdocs.yml CHANGED
@@ -177,6 +177,7 @@ nav:
177
177
  - Overview: concepts/index.md
178
178
  - DataFrames: data_frame.md
179
179
  - Trading Strategies: strategy.md
180
+ - File Formats: file_formats.md
180
181
  - Technical Indicators:
181
182
  - Overview: indicators/index.md
182
183
  - Indicator Details: indicators.md
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.37
4
+ version: 0.0.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
@@ -426,7 +426,6 @@ files:
426
426
  - data/talk_talk.json
427
427
  - develop_summary.md
428
428
  - docs/.gitignore
429
- - docs/IMPROVEMENT_PLAN.md
430
429
  - docs/advanced/backtesting.md
431
430
  - docs/advanced/ensemble.md
432
431
  - docs/advanced/fpop.md
@@ -507,6 +506,7 @@ files:
507
506
  - docs/data-sources/index.md
508
507
  - docs/data_frame.md
509
508
  - docs/factors_that_impact_price.md
509
+ - docs/file_formats.md
510
510
  - docs/finviz.md
511
511
  - docs/fx_pro_bit.md
512
512
  - docs/genetic_programming.md