Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Learn Quant Strategy

learn_quant_strategy_fork.livemd

Learn Quant Strategy

Mix.install(
  [
    {:req, "~> 0.4.5"},
    {:explorer, "~> 0.7.1"},
    {:kino_explorer, "~> 0.1.12"},
    {:kino_vega_lite, "~> 0.1.10"},
    {:timex, "~> 3.7"}
  ],
  config: [nx: [default_backend: EXLA.Backend]]
)

Nifty 200 CSV

resp = Req.get!("https://nsearchives.nseindia.com/content/indices/ind_nifty200list.csv")
input = Kino.Input.file("File")
value = Kino.Input.read(input)
path = Kino.Input.file_path(value.file_ref)
data = File.read!(path) |> Jason.decode!()

stock history

# ticker = "JIOFIN.NS"
# range = "1mo"
# interval = "15m"
# metrics = "close"

# url =
#   "https://query1.finance.yahoo.com/v8/finance/chart/#{ticker}?metrics=#{metrics}&interval=#{interval}&range=#{range}"

# IO.puts(url)
# resp = Req.get!(url)

require Explorer.DataFrame, as: DF
require Explorer.Series, as: S

# quote_data =
#   resp.body["chart"]["result"]
#   |> List.first()
#   |> get_in(["indicators", "quote"])
#   |> List.first()
#   |> DF.new()
quote_data =
  data
  |> DF.new()
  |> DF.mutate(
    price_change:
      S.subtract(
        close,
        S.shift(close, 1)
      )
  )
  |> DF.mutate(
    gain: if(S.greater(price_change, 0), do: price_change, else: 0),
    loss: if(S.less(price_change, 0), do: -price_change, else: 0)
  )
  |> DF.mutate(
    avg_gain: S.window_mean(gain, 14, min_periods: 1),
    avg_loss: S.window_mean(loss, 14, min_periods: 1)
  )
indexes = S.from_list(0..(S.size(quote_data["gain"]) - 1) |> Enum.to_list())
quote_data = DF.put(quote_data, :index, indexes)

quote_data_first_13 =
  DF.slice(quote_data, 0..12)

quote_data_at_14 =
  quote_data
  |> DF.mutate(
    avg_gain: S.window_mean(gain, 14, min_periods: 14),
    avg_loss: S.window_mean(loss, 14, min_periods: 14)
  )
  |> DF.slice(13..13)

# # 14 : 0.15714285714285633

quote_data_after_14 =
  quote_data_at_14
  |> DF.concat_rows(quote_data |> DF.slice(14..-1//1))
  |> DF.group_by(:index)
  |> DF.summarise(prev_avg_gain: S.at(gain, 1))
  |> DF.mutate_with(
    &[
      avg_gain: S.divide(S.add(&1["gain"], S.multiply(13, S.shift(&1["avg_gain"], 1))), 14),
      avg_loss: S.divide(S.add(&1["loss"], S.multiply(13, S.shift(&1["avg_loss"], 1))), 14)
    ]
  )
  |> DF.slice(1..-1//1)

# quote_data =
#   quote_data_first_13
#   |> DF.concat_rows(quote_data_at_14)
#   |> DF.concat_rows(quote_data_after_14)

#   |> DF.mutate(rs: S.divide(avg_gain, avg_loss))
#   |> DF.mutate(rs: if(S.is_nan(rs), do: 0, else: rs))
#   |> DF.mutate(rsi: S.subtract(100, S.divide(100, S.add(1, rs))))

# timestamp_data =
#   resp.body["chart"]["result"]
#   |> List.first()
#   |> Map.take(["timestamp"])
#   |> Enum.reduce(%{"timestamp" => [], "datetime" => []}, fn {_, data}, acc ->
#     %{
#       acc
#       | "timestamp" => data,
#         "datetime" =>
#           Enum.map(data, fn timestamp ->
#             {:ok, datetime} = DateTime.from_unix(timestamp)
#             # "#{DateTime.shift_zone!(datetime, "Asia/Kolkata")}"
#             "#{datetime}"
#           end)
#     }
#   end)
#   |> DF.new()

# data = DF.concat_columns([timestamp_data, quote_data])
VegaLite.new(width: 750, height: 300)
|> VegaLite.data_from_values(data, only: ["price_change", "rsi"])
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "price_change", type: :nominal)
|> VegaLite.encode_field(:y, "rsi", type: :nominal)