{"id":22955143,"url":"https://github.com/tuukkaviitanen/mlops-demo","last_synced_at":"2026-05-09T01:02:49.424Z","repository":{"id":231841388,"uuid":"782833175","full_name":"tuukkaviitanen/mlops-demo","owner":"tuukkaviitanen","description":"Crypto predictor demo about using GitHub Actions as a MLOps tool","archived":false,"fork":false,"pushed_at":"2024-12-22T18:21:07.000Z","size":3955,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T00:41:39.187Z","etag":null,"topics":["cryptocurrency","flask","machine-learning","python"],"latest_commit_sha":null,"homepage":"https://crypto-predictor.tuukka.net","language":"Jupyter Notebook","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/tuukkaviitanen.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-06T06:52:52.000Z","updated_at":"2024-12-22T18:20:51.000Z","dependencies_parsed_at":"2024-12-02T13:26:17.573Z","dependency_job_id":"da26b737-76a9-4938-9028-d3c22ff07317","html_url":"https://github.com/tuukkaviitanen/mlops-demo","commit_stats":null,"previous_names":["tuukkaviitanen/mlops-demo"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/tuukkaviitanen/mlops-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuukkaviitanen%2Fmlops-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuukkaviitanen%2Fmlops-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuukkaviitanen%2Fmlops-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuukkaviitanen%2Fmlops-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tuukkaviitanen","download_url":"https://codeload.github.com/tuukkaviitanen/mlops-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuukkaviitanen%2Fmlops-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32803625,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"ssl_error","status_checked_at":"2026-05-08T08:22:45.650Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["cryptocurrency","flask","machine-learning","python"],"created_at":"2024-12-14T16:28:03.088Z","updated_at":"2026-05-09T01:02:49.408Z","avatar_url":"https://github.com/tuukkaviitanen.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Crypto Predict API\n\n\u003e Demo about using [GitHub Actions](https://docs.github.com/en/actions) as a [MLOps](https://www.databricks.com/glossary/mlops) tool\n\n- **Running live at: https://crypto-predictor.tuukka.net**\n  - Has [Swagger UI](https://swagger.io/tools/swagger-ui/) API documentation\n\n## Latest Bitcoin forecast\n\n![Bitcoin forecast](https://crypto-predictor.tuukka.net/bitcoin/forecast)\n\n## Summary\n\n- Teaches a machine learning model from real-time data, that predicts the price of [Bitcoin](https://bitcoin.org/en/) at a specific time\n- Wraps the model inside a [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview) [API](https://www.ibm.com/topics/api)\n- Creates a [Docker](https://www.docker.com/) image out of the API with descriptive tags\n- ~~Deploys the latest image to [Render](https://render.com/)~~\n- Does all of this in a GitHub Actions workflow every 24 hours to always keep the model trained with the latest data\n\n## Model training\n\n- Uses real-time updating history data from [Alpaca Markets](https://alpaca.markets/) [Market Data API](https://docs.alpaca.markets/docs/about-market-data-api) using their [official Python SDK](https://github.com/alpacahq/alpaca-py)\n- Teaches a [machine learning model](https://www.coursera.org/articles/machine-learning-models) with [Facebook prophet](https://facebook.github.io/prophet/)\n- Saves the model as a [pickle](https://docs.python.org/3/library/pickle.html) binary-file on the disk\n\n## API\n\n- [Flask API](https://flask.palletsprojects.com/en/3.0.x/) served with [Waitress](https://docs.pylonsproject.org/projects/waitress/en/stable/index.html)\n- The function is to use the model from any other application using the API over [HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview)\n  - This [microservice structure](https://microservices.io/) allows the usage of the model even when the user application is created using different technologies\n  - This also allows the model to be retrained or updated without needing to update the user applications\n- The API is run loads the binary model at startup\n- Listens to `/bitcoin` endpoint that takes `date` query parameter\n  - Example: `/bitcoin?date=2024-06-01`\n  - Uses the `date` parameter to run it through the model and returns the predicted price in the following [JSON](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON) format:\n    ```\n    {\n      \"date\": \"2025-01-01T00:00:00Z\",\n      \"prediction\": 56771.779953588826,\n      \"prediction_low\": 53746.06414094623,\n      \"prediction_high\": 60034.61668451781\n    }\n    ```\n- [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) is enabled, so the API can be called from other web applications\n  - [Flask-Cors](https://flask-cors.readthedocs.io/en/latest/) is used to simplify this process\n\n## Dockerfile\n\n- Uses [Multi-stage dockerfile](https://docs.docker.com/build/building/multi-stage/)\n- Trains the model in the build stage\n- Only model binary, API python file and the API's dependencies are applied to the final image\n  - This reduces the final image size and potentially reduces the [attack surface](https://www.fortinet.com/resources/cyberglossary/attack-surface)\n\n## GitHub Actions workflow\n\n- Runs on push to main branch and on a [CRON schedule](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule) every 24 hours\n- Tags a new commit with a [semantic version](https://www.geeksforgeeks.org/introduction-semantic-versioning/)\n  - If run with a schedule, commit is already tagged, so the existing version is just read\n- Builds a [Docker image](https://docs.docker.com/guides/docker-concepts/the-basics/what-is-an-image/) using the [Dockerfile](Dockerfile) and publishes the image to [GitHub Container Registry (ghcr.io)](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) under this GitHub repository\n  - The image is tagged with the following tags:\n    - `latest` for finding the latest version of the image\n    - The semantic version of the commit (e.g. `0.3.1`) for finding the latest image of this specific commit\n    - The semantic version combined with the timestamp of the build (e.g. `0.3.1-20240518121047`) for having a unique identifier for each build, that also clearly states the software version and the build time\n  - It's important to note that only one image can hold a specific tag at a time, and other images holding the same tags will lose those tags\n- ~~Triggers [Render](https://render.com/) deployment for the application~~\n  - ~~The Render web service is configured to run the image with the `latest` tag so it will always deploy the newly built image~~\n    - ~~This image deployment allows most of the configuring to be done in the repository and just minimal setup at Render side~~\n\n## Setup\n\n### Dependencies\n\n- [Python](https://www.python.org/) 3.x\n- Required Python dependencies\n  - For running train script ([train.py](train.py))\n    - [alpaca-py](https://pypi.org/project/alpaca-py/)\n    - [prophet](https://pypi.org/project/prophet/)\n  - For running the API ([api.py](api.py))\n    - [Flask](https://pypi.org/project/Flask/)\n    - [waitress](https://pypi.org/project/waitress/)\n    - [pandas](https://pypi.org/project/pandas/)\n    - [prophet](https://pypi.org/project/prophet/)\n    - [flask-cors](https://pypi.org/project/Flask-Cors/)\n  - For running the [Jupyter notebook](https://realpython.com/jupyter-notebook-introduction/) machine learning experiment ([train.ipynb](train.ipynb))\n    - [pandas](https://pypi.org/project/pandas/)\n    - [prophet](https://pypi.org/project/prophet/)\n    - [plotly](https://pypi.org/project/plotly/)\n    - [statsmodels](https://pypi.org/project/statsmodels/)\n    - [alpaca-py](https://pypi.org/project/alpaca-py/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuukkaviitanen%2Fmlops-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftuukkaviitanen%2Fmlops-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuukkaviitanen%2Fmlops-demo/lists"}