Class: RVGP::Fakers::FakeFeed

Inherits:
Faker::Base
  • Object
show all
Extended by:
FakerHelpers
Defined in:
lib/rvgp/fakers/fake_feed.rb

Overview

Contains faker implementations that produce CSV files, for use as a data feed

Defined Under Namespace

Classes: ParameterLengthError

Class Method Summary collapse

Class Method Details

.basic_checking(from: ::Date.today, to: from + DEFAULT_LENGTH_IN_DAYS, expense_descriptions: nil, income_descriptions: nil, deposit_average: RVGP::Journal::Commodity.from_symbol_and_amount('$', 6000), deposit_stddev: 500.0, withdrawal_average: RVGP::Journal::Commodity.from_symbol_and_amount('$', 300), withdrawal_stddev: 24.0, post_count: DEFAULT_POST_COUNT, starting_balance: RVGP::Journal::Commodity.from_symbol_and_amount('$', 5000), deposit_ratio: 0.05, entries: []) ⇒ String

Generates a basic csv feed string, that resembles those offered by banking institutions

Parameters:

  • from (Date) (defaults to: ::Date.today)

    The date to start generated feed from

  • to (Date) (defaults to: from + DEFAULT_LENGTH_IN_DAYS)

    The date to end generated feed

  • income_descriptions (Array) (defaults to: nil)

    Strings containing the pool of available income descriptions, for use in random selection

  • expense_descriptions (Array) (defaults to: nil)

    Strings containing the pool of available expense descriptions, for use in random selection

  • deposit_average (RVGP::Journal::Commodity) (defaults to: RVGP::Journal::Commodity.from_symbol_and_amount('$', 6000))

    The average deposit amount

  • deposit_stddev (Float) (defaults to: 500.0)

    The stand deviation, on random deposits

  • withdrawal_average (RVGP::Journal::Commodity) (defaults to: RVGP::Journal::Commodity.from_symbol_and_amount('$', 300))

    The average withdrawal amount

  • withdrawal_stddev (Float) (defaults to: 24.0)

    The stand deviation, on random withdrawals

  • starting_balance (RVGP::Journal::Commodity) (defaults to: RVGP::Journal::Commodity.from_symbol_and_amount('$', 5000))

    The balance of the account, before generating the transactions in the feed

  • post_count (Numeric) (defaults to: DEFAULT_POST_COUNT)

    The number of transactions to generate, in this csv feed

  • deposit_ratio (Float) (defaults to: 0.05)

    The odds ratio, for a given transaction, to be a deposit

  • entries (Array) (defaults to: [])

    An array of Array’s, that are appended to the generated entries (aka ‘lines’)

Returns:

  • (String)

    A CSV, containing the generated transactions



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/rvgp/fakers/fake_feed.rb', line 53

def basic_checking(from: ::Date.today,
                   to: from + DEFAULT_LENGTH_IN_DAYS,
                   expense_descriptions: nil,
                   income_descriptions: nil,
                   deposit_average: RVGP::Journal::Commodity.from_symbol_and_amount('$', 6000),
                   deposit_stddev: 500.0,
                   withdrawal_average: RVGP::Journal::Commodity.from_symbol_and_amount('$', 300),
                   withdrawal_stddev: 24.0,
                   post_count: DEFAULT_POST_COUNT,
                   starting_balance: RVGP::Journal::Commodity.from_symbol_and_amount('$', 5000),
                   deposit_ratio: 0.05,
                   entries: [])

  running_balance = starting_balance.dup

  entry_to_row = lambda do |entry|
    FEED_COLUMNS.map do |column|
      if column == 'RunningBalance'
        deposit = entry['Deposit (+)']
        withdrawal = entry['Withdrawal (-)']

        running_balance = withdrawal.nil? ? running_balance + deposit : running_balance - withdrawal
      else
        entry[column]
      end
    end
  end

  # Newest to oldest:
  to_csv do |csv|
    dates = entries_over_date_range from, to, post_count

    dates.each_with_index do |date, i|
      # If there are any :entries to insert, in this date, do that now:
      entries.each do |entry|
        csv << entry_to_row.call(entry) if entry['Date'] <= date && (i.zero? || entry['Date'] > dates[i - 1])
      end

      accumulator, mean, stddev, type, description = *(
        if Faker::Boolean.boolean true_ratio: deposit_ratio
          [:+, deposit_average.to_f, deposit_stddev, 'ACH',
           format('%s DIRECT DEP',
                  income_descriptions ? income_descriptions.sample : Faker::Company.name.upcase)]
        else
          [:-, withdrawal_average.to_f, withdrawal_stddev, 'VISA',
           expense_descriptions ? expense_descriptions.sample : Faker::Company.name.upcase]
        end)

      amount = RVGP::Journal::Commodity.from_symbol_and_amount(
        DEFAULT_CURRENCY.symbol,
        Faker::Number.normal(mean: mean, standard_deviation: stddev)
      )

      running_balance = running_balance.send accumulator, amount

      amounts = [nil, amount]
      csv << ([date, type, description] + (accumulator == :- ? amounts.reverse : amounts) + [running_balance])
    end

    # Are there any more entries? If so, sort 'em and push them:
    entries.each { |entry| csv << entry_to_row.call(entry) if entry['Date'] > dates.last }
  end
end

.personal_checking(from: ::Date.today, to: from + DEFAULT_LENGTH_IN_DAYS, expense_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')], income_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')], monthly_expenses: {}, opening_liability_balance: '$ 0.00'.to_commodity, opening_asset_balance: '$ 0.00'.to_commodity, liability_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')], liabilities_by_month: months_in_range(from, to).map.with_index do |_, n| RVGP::Journal::Commodity.from_symbol_and_amount('$', 200 + ((n + 1) * 800)) end, assets_by_month: months_in_range(from, to).map.with_index do |_, n| RVGP::Journal::Commodity.from_symbol_and_amount('$', 500 * ((n + 1) * 5)) end) ⇒ String

Generates a basic csv feed string, that resembles those offered by banking institutions. Unlike #basic_checking, this faker supports a set of parameters that will better conform the output to a typical model of commerence for an employee with a paycheck and living expenses. As such, the parameters are a bit different, and suited to plotting aesthetics.

Parameters:

  • from (Date) (defaults to: ::Date.today)

    The date to start generated feed from

  • to (Date) (defaults to: from + DEFAULT_LENGTH_IN_DAYS)

    The date to end generated feed

  • income_sources (Array) (defaults to: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')])

    Strings containing the pool of income companies, to use for growing our assets

  • expense_sources (Array) (defaults to: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')])

    Strings containing the pool of available expense companies, to use for shrinking our assets

  • opening_liability_balance (RVGP::Journal::Commodity) (defaults to: '$ 0.00'.to_commodity)

    The opening balance of the liability account, preceeding month zero

  • opening_asset_balance (RVGP::Journal::Commodity) (defaults to: '$ 0.00'.to_commodity)

    The opening balance of the asset account, preceeding month zero

  • liability_sources (Array) (defaults to: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')])

    Strings containing the pool of available liability sources (aka ‘companies’)

  • liabilities_by_month (Array) (defaults to: months_in_range(from, to).map.with_index do |_, n| RVGP::Journal::Commodity.from_symbol_and_amount('$', 200 + ((n + 1) * 800)) end)

    An array of RVGP::Journal::Commodity entries, indicatiing the liability balance for a month with offset n, from the from date

  • assets_by_month (Array) (defaults to: months_in_range(from, to).map.with_index do |_, n| RVGP::Journal::Commodity.from_symbol_and_amount('$', 500 * ((n + 1) * 5)) end)

    An array of RVGP::Journal::Commodity entries, indicating the asset balance for a month with offset n, from the from date

Returns:

  • (String)

    A CSV, containing the generated transactions



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rvgp/fakers/fake_feed.rb', line 137

def personal_checking(from: ::Date.today,
                      to: from + DEFAULT_LENGTH_IN_DAYS,
                      expense_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')],
                      income_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')],
                      monthly_expenses: {},
                      opening_liability_balance: '$ 0.00'.to_commodity,
                      opening_asset_balance: '$ 0.00'.to_commodity,
                      liability_sources: [Faker::Company.name.tr('^a-zA-Z0-9 ', '')],
                      liabilities_by_month: months_in_range(from, to).map.with_index do |_, n|
                        RVGP::Journal::Commodity.from_symbol_and_amount('$', 200 + ((n + 1) * 800))
                      end,
                      assets_by_month: months_in_range(from, to).map.with_index do |_, n|
                        RVGP::Journal::Commodity.from_symbol_and_amount('$', 500 * ((n + 1) * 5))
                      end)

  num_months_in_range = ((to.year * 12) + to.month) - ((from.year * 12) + from.month) + 1

  ['liabilities_by_month', liabilities_by_month.length,
   'assets_by_month', assets_by_month.length].each_slice(2) do |attr, length|
    raise ParameterLengthError.new(attr, num_months_in_range, length) unless num_months_in_range == length
  end

  monthly_expenses.each_pair do |company, expenses_by_month|
    unless num_months_in_range == expenses_by_month.length
      attr = format('monthly_expenses: %s', company)
      raise ParameterLengthError.new(attr, num_months_in_range, expenses_by_month.length)
    end
  end

  liability_balance = opening_liability_balance
  asset_balance = opening_asset_balance

  # Newest to oldest:
  to_csv do |csv|
    months_in_range(from, to).each_with_index do |first_of_month, i|
      expected_liability = liabilities_by_month[i]

      # Let's adjust the liability to suit
      csv << if expected_liability > liability_balance
               # We need to borrow some more money:
               deposit = (expected_liability - liability_balance).abs
               asset_balance += deposit
               liability_balance += deposit
               [first_of_month, 'ACH', liability_sources.sample, nil, deposit, asset_balance]
             elsif expected_liability < liability_balance
               # We want to pay off our balance:
               payment = (expected_liability - liability_balance).abs
               asset_balance -= payment
               liability_balance -= payment
               [first_of_month, 'ACH', liability_sources.sample, payment, nil, asset_balance]
             end

      expected_assets = assets_by_month[i]

      monthly_expenses.each_pair do |company, expenses_by_month|
        asset_balance -= expenses_by_month[i]
        csv << [first_of_month, 'VISA', company, expenses_by_month[i], nil, asset_balance]
      end

      # Let's adjust the assets to suit
      if expected_assets > asset_balance
        # We need a paycheck:

        deposit = expected_assets - asset_balance
        asset_balance += deposit
        csv << [first_of_month, 'ACH', income_sources.sample, nil, deposit, asset_balance]
      elsif expected_assets < asset_balance
        # We need to generate some expenses:
        payment = asset_balance - expected_assets
        asset_balance -= payment
        csv << [first_of_month, 'VISA', expense_sources.sample, payment, nil, asset_balance]
      end
    end
  end
end