Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Base Python image
FROM python:3.10-slim

# Set up environment
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
curl \
git \
libatlas-base-dev \
libglib2.0-0 \
libgomp1 \
liblapack-dev \
libpq-dev \
gcc \
&& rm -rf /var/lib/apt/lists/*

# Install Python packages
RUN pip install --upgrade pip && pip install \
jupyter \
pandas \
numpy==1.23.5 \
matplotlib \
seaborn \
requests \
openai \
griptape \
scikit-learn \
statsmodels \
prophet \
plotly

# Set working directory
WORKDIR /workspace/griptape

# Copy local code
COPY . /workspace/griptape

# Expose ports (Jupyter + optional API-related ports)
EXPOSE 8888

# Default command
CMD ["./docker_start.sh"]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../../docker_common/bashrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
source ./docker_name.sh

FULL_IMAGE_NAME=${FULL_IMAGE_NAME:-$IMAGE_NAME}
CONTAINER_PATH="/workspace"


echo "🔄 Running container from image: $FULL_IMAGE_NAME"
echo "📦 Container name: $CONTAINER_NAME"
echo "📁 Mounting local path: $HOST_PATH → $CONTAINER_PATH"

echo "docker run --rm -ti \
--name \"$CONTAINER_NAME\" \
-p 8888:8888 \
-v $(pwd):/workspace/griptape \
$FULL_IMAGE_NAME"

docker run --rm -ti \
--name "$CONTAINER_NAME" \
-p 8888:8888 \
-v $(pwd):/workspace/griptape \
$FULL_IMAGE_NAME
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source ./docker_name.sh

echo "Building Docker image: $IMAGE_NAME"
docker build -t $IMAGE_NAME .
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
source ./docker_name.sh

echo "Stopping container (if running)..."
docker stop $CONTAINER_NAME 2>/dev/null || true

echo "Removing container..."
docker rm $CONTAINER_NAME 2>/dev/null || true

echo "Removing image..."
docker rmi $IMAGE_NAME 2>/dev/null || true
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
source ./docker_name.sh

echo "Executing into container: $CONTAINER_NAME"
docker exec -it $CONTAINER_NAME bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Set project root
GIT_ROOT=$(git rev-parse --show-toplevel)

# Optional: Load shared utils if you have them
if [ -f "$GIT_ROOT/docker_common/utils.sh" ]; then
source "$GIT_ROOT/docker_common/utils.sh"
fi

# Define Docker image and container names for reuse
export IMAGE_NAME="griptape-bitcoin-project"
export CONTAINER_NAME="griptape-bitcoin-container"
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
source ./docker_name.sh

DOCKERHUB_USER=your_dockerhub_username_here

echo "Tagging and pushing image to DockerHub: $DOCKERHUB_USER/$IMAGE_NAME"
docker tag $IMAGE_NAME $DOCKERHUB_USER/$IMAGE_NAME
docker push $DOCKERHUB_USER/$IMAGE_NAME
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
echo "🚀 Running main.py to update data..."
python pipeline/main.py

echo "✅ Data update complete. Launching Jupyter Notebook..."
jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root --NotebookApp.token='' --NotebookApp.password=''
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../../docker_common/etc_sudoers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
../../../docker_common/install_jupyter_extensions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.seasonal import seasonal_decompose
from prophet import Prophet
from typing import Tuple, Dict

class BitcoinAnalysisTool:
def load_and_clean_data(self, csv_path: str) -> pd.DataFrame:
df = pd.read_csv(csv_path)
df.columns = [col.strip().lower() for col in df.columns]
if 'price_usd' in df.columns:
df = df.drop(columns=['price_usd'])
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
df = df.sort_index()
return df

def plot_price_series(self, df: pd.DataFrame) -> None:
plt.figure(figsize=(12, 5))
plt.plot(df, label='Price')
plt.title('Bitcoin Price Over Time')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.show()

def plot_rolling_stats(self, df: pd.DataFrame, window: int = 30) -> None:
rolling_mean = df.rolling(window=window).mean()
rolling_std = df.rolling(window=window).std()
plt.figure(figsize=(12, 5))
plt.plot(df, label='Price')
plt.plot(rolling_mean, label=f'Rolling Mean ({window}d)')
plt.plot(rolling_std, label=f'Rolling Std ({window}d)')
plt.title('Rolling Statistics')
plt.legend()
plt.grid(True)
plt.show()

def decompose_series(self, df: pd.DataFrame, period: int = 30) -> None:
decomp = seasonal_decompose(df, model='additive', period=period)
decomp.plot()
plt.show()

def adf_test(self, df: pd.DataFrame) -> Dict[str, float]:
series = df.dropna().iloc[:, 0]
result = adfuller(series)
return {
"adf_statistic": result[0],
"p_value": result[1],
"is_stationary": result[1] < 0.05
}

def run_prophet_forecast(self, df: pd.DataFrame, periods: int = 30) -> Tuple[pd.DataFrame, pd.DataFrame]:
df_prophet = df.reset_index()[['date', 'price']].rename(columns={'date': 'ds', 'price': 'y'})
model = Prophet()
model.fit(df_prophet)
future = model.make_future_dataframe(periods=periods)
forecast = model.predict(future)
return df_prophet, forecast

def plot_prophet_forecast(self, df_prophet: pd.DataFrame, forecast: pd.DataFrame) -> None:
df_prophet['ds'] = pd.to_datetime(df_prophet['ds'])
forecast['ds'] = pd.to_datetime(forecast['ds'])
plt.figure(figsize=(12, 5))
plt.plot(df_prophet['ds'], df_prophet['y'], label='Actual')
plt.plot(forecast['ds'], forecast['yhat'], label='Prophet Forecast')
plt.title('Prophet Forecast vs Actual')
plt.xlabel('Date')
plt.ylabel('Price')
plt.legend()
plt.grid(True)
plt.show()

def generate_data_summary_report(self, df: pd.DataFrame) -> str:
start_date = df.index.min().strftime("%Y-%m-%d")
end_date = df.index.max().strftime("%Y-%m-%d")
stats = df.describe().T
summary = (
f"📊 Bitcoin Price Data Summary\n"
f"- Rows: {len(df)}\n"
f"- Date Range: {start_date} to {end_date}\n"
f"- Mean Price: {stats['mean'].values[0]:,.2f}\n"
f"- Std Dev: {stats['std'].values[0]:,.2f}\n"
f"- Min Price: {stats['min'].values[0]:,.2f}\n"
f"- Max Price: {stats['max'].values[0]:,.2f}\n"
)
return summary

def generate_stationarity_report(self, df: pd.DataFrame) -> str:
result = self.adf_test(df)
report = (
f"📈 Stationarity Test (ADF)\n"
f"- ADF Statistic: {result['adf_statistic']:.4f}\n"
f"- p-value: {result['p_value']:.4f}\n"
f"- Conclusion: {'✅ Series is stationary.' if result['is_stationary'] else '❌ Series is not stationary.'}"
)
return report

def generate_forecast_report(self, forecast: pd.DataFrame, periods: int = 30) -> str:
forecast_tail = forecast.tail(periods)
min_forecast = forecast_tail["yhat"].min()
max_forecast = forecast_tail["yhat"].max()
mean_forecast = forecast_tail["yhat"].mean()

report = (
f"📅 Prophet Forecast Summary (Next {periods} Days)\n"
f"- Forecast Range: {forecast_tail['ds'].min().date()} to {forecast_tail['ds'].max().date()}\n"
f"- Min Forecast Price: {min_forecast:,.2f}\n"
f"- Max Forecast Price: {max_forecast:,.2f}\n"
f"- Avg Forecast Price: {mean_forecast:,.2f}"
)
return report


Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "5f25eb16",
"metadata": {},
"outputs": [],
"source": [
"from analysis import BitcoinAnalysisTool"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1ab4535",
"metadata": {},
"outputs": [],
"source": [
"tool = BitcoinAnalysisTool()\n",
"df = tool.load_and_clean_data(\"/workspace/griptape/warehouse/bitcoin_prices.csv\")\n",
"tool.plot_price_series(df)\n",
"tool.plot_rolling_stats(df)\n",
"print(tool.adf_test(df))\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "690f809d",
"metadata": {},
"outputs": [],
"source": [
"\n",
"df_prophet, forecast = tool.run_prophet_forecast(df)\n",
"tool.plot_prophet_forecast(df_prophet, forecast)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a74783e6",
"metadata": {},
"outputs": [],
"source": [
"# 🔹 Generate and display the data summary report\n",
"data_summary = tool.generate_data_summary_report(df)\n",
"print(data_summary)\n",
"\n",
"# 🔹 Generate and display the stationarity report (ADF test)\n",
"stationarity_report = tool.generate_stationarity_report(df)\n",
"print(stationarity_report)\n",
"\n",
"# 🔹 Generate and display the forecast summary report\n",
"forecast_report = tool.generate_forecast_report(forecast)\n",
"print(forecast_report)\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
last_updated: '2025-04-30'
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# main.py
import yaml
from utils import BitcoinTool
from analysis import BitcoinAnalysisTool # <- contains reporting functions

def main():
# Load config
with open("pipeline/config.yaml", "r") as f:
config = yaml.safe_load(f)

# Update data pipeline
tool = BitcoinTool(config=config)
print(tool.run({})) # This performs data update

# Load the updated data
analysis = BitcoinAnalysisTool()
df = analysis.load_and_clean_data("warehouse/bitcoin_prices.csv") # replace with actual path key

# Run forecast
df_prophet, forecast = analysis.run_prophet_forecast(df)

# Generate reports
data_report = analysis.generate_data_summary_report(df)
forecast_report = analysis.generate_forecast_report(forecast)

# Output reports
print(data_report)
print(forecast_report)

if __name__ == "__main__":
main()
Loading
Loading