{"id":46146384,"url":"https://github.com/echoboomer/ticker","last_synced_at":"2026-03-02T07:24:02.120Z","repository":{"id":61623962,"uuid":"534718521","full_name":"echoboomer/ticker","owner":"echoboomer","description":null,"archived":false,"fork":false,"pushed_at":"2022-09-09T16:27:33.000Z","size":23,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-06-20T16:34:46.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/echoboomer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-09T16:22:54.000Z","updated_at":"2024-06-20T16:34:46.870Z","dependencies_parsed_at":"2022-10-19T18:45:22.793Z","dependency_job_id":null,"html_url":"https://github.com/echoboomer/ticker","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/echoboomer/ticker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echoboomer%2Fticker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echoboomer%2Fticker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echoboomer%2Fticker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echoboomer%2Fticker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/echoboomer","download_url":"https://codeload.github.com/echoboomer/ticker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echoboomer%2Fticker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29994904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-02T01:47:34.672Z","status":"online","status_checked_at":"2026-03-02T02:00:07.342Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-02T07:24:01.383Z","updated_at":"2026-03-02T07:24:02.112Z","avatar_url":"https://github.com/echoboomer.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ticker\n\n`ticker` is a simple web API that fetches upstream data regarding stocks using the stock symbol.\n\nThe referenced upstream API in this example is https://www.alphavantage.co/.\n\n- [ticker](#ticker)\n  - [Setup](#setup)\n    - [Docker](#docker)\n    - [Source](#source)\n  - [Environment Variables](#environment-variables)\n  - [Documentation](#documentation)\n  - [Usage](#usage)\n  - [Deployment](#deployment)\n  - [Resiliency](#resiliency)\n  - [Caveats](#caveats)\n\n## Setup\n\n### Docker\n\nA Dockerfile is provided for building locally. The image is already built and available on Docker Hub via `eb129/ticker:v0.1.1`.\n\nIf you wish to build it yourself:\n\n```bash\n$ docker build . -t ticker:v0.1.0\n```\n\nYou can adjust the image name and tag as you see fit, but be sure to update those when attempting to run via Docker.\n\n### Source\n\nAlternatively, you can build the binary locally without the image and run:\n\n```bash\n$ make build\n$ bin/ticker_darwin_amd64\n```\n\nThe application runs on port `8080` - you can adjust this in `main.go` if you see fit.\n\nRunning this from the root of the repository will work since `.env` is available if you have populated it. You can optionally use the `ticker_linux_amd64` build depending on your environment. If you require compatibility for `arm` arch, you may adjust the contents of the Makefile.\n\n## Environment Variables\n\nThe following environment variables are required via the `.env` file if running locally. Alternatively, the `.env` will be skipped if it isn't present and environment variables will be referenced directly. See the section on deployment below for additional details. All of these are required to be set.\n\n- `APIKEY` - The API key for the alphavantage API.\n- `SYMBOL` - The stock symbol to look up - i.e. `MSFT`.\n- `NDAYS` - When using the `/avg` endpoint, this is the number of days to look back for data.\n\n## Documentation\n\nSwagger docs are available via http://localhost:8080/swagger/index.html when the API is running.\n\n## Usage\n\nOnce the API is available either locally or through deployment to Kubernetes, you can use the following calls to test. The endpoint will need to be adjusted depending on where you run it.\n\n```bash\n$ curl -X 'GET' \\\n  'http://localhost:8080/api/v1/stock' \\\n  -H 'accept: application/json'\n```\n\nThis will return the full range of data available for the stock, bypassing `NDAYS`.\n\n```bash\n$ curl -X 'GET' \\\n  'http://localhost:8080/api/v1/stock/avg' \\\n  -H 'accept: application/json'\n```\n\nThis will return any items greater than or equal to the `NDAYS` value - meaning, anything older will not be returned. This also returns the average close price over that same time period.\n\n## Deployment\n\nKustomize manifests are provided for deployment. You can switch to the `deploy/kubernetes/ticker/overlays/development` folder and run the following commands to work with the files.\n\nYou must have a Kubernetes cluster running and configured for the `apply` step. This was tested in development using `minikube`.\n\n```bash\n$ kubectl kustomize .\n...outputs rendered manifests\n```\n\n```bash\n$ kubectl apply -k .\n...applies to cluster\n```\n\nOnce the application is deployed, you can port-forward directly to the service if you wish to test locally:\n\n```bash\n$ kubectl port-forward -n ticker service/ticker 8080:8080\n$ curl...\n```\n\n**Note:** The manifests use a secret referencing a file called `.env.api` in the `base/` folder that will generate a `Secret` using `secretGenerator`. For the sake of this demonstration, `SYMBOL` and `NDAYS` are provided as literals for the `configMapGenerator`. You must provide your API key in `.env.api` and you may adjust the literals in the `configMapGenerator` as you wish.\n\n## Resiliency\n\nA `HorizontalPodAutoscaler` and a `PodDisruptionBudget` are provided here as they are fundamental constructs for managing resiliency in a real-world application. A base health check (liveness/readiness) is also provided. These settings would need to be adjusted in production to reflect the following requirements:\n\n- Enough `Pods` at baseline to support realistic traffic on the `HPA` in terms of min/max settings.\n- Appropriate configuration of the `PDB` to guarantee the application remains available during events like rollouts, cluster upgrades, etc.\n- Appropriate configuration of `RollingUpdate` configuration on the `Deployment` to tolerate graceful rollouts.\n- Appropriate configuration of health checks.\n\nThe configuration provided here is oversimplified, but this would warrant additional consideration when deploying an application.\n\n## Caveats\n\n- In production, you'd want to use a real data store for rate limiting implementation. This uses an in-memory store so restarting the app negates the usefulness of rate limiting. The rate limiting implemenation is 5/minute - the alphavantage API also instates 500/day, but that wasn't implemented here.\n- Parsing of sensitive data, like the API key or anything passed in from `.env`, is done in the function calls to save time. In a real-world application, I'd recommend using a struct to instantiate the data so it can be reused properly.\n- This was tested on `minikube` using `minikube v1.25.2 on Darwin 12.4`.\n- I would recommend writing tests for this application in real-world usage.\n- `Ingress` configuration, in my opinion, is beyond the scope of this exercise. You may need to add annotations or adjust the manifest for the `Ingress` depending on your environment.\n- `HorizontalPodAutoscaler` functionality only works if metrics are available via the API. This is beyond the scope of this exercise.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechoboomer%2Fticker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fechoboomer%2Fticker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechoboomer%2Fticker/lists"}