Skip to content
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
81de478
Dockerized
claytondukes Mar 7, 2024
20d8236
always restart container
Mar 21, 2024
5b71555
updated coinbase api
claytondukes Mar 15, 2025
1aa7cbb
.
Jun 27, 2025
80bb941
feat: implement limit order support to reduce fees
claytondukes Jun 27, 2025
12be3e3
feat: implement limit order support to reduce fees
claytondukes Jun 27, 2025
6a6e65d
fix: use limit_order_gtc with end_time to prevent stuck orders
claytondukes Jun 27, 2025
a70c7aa
fix: use limit_order_gtd with end_time to prevent stuck orders
claytondukes Jun 27, 2025
7e9a3ed
fix: use limit_order_gtd with end_time to prevent stuck orders
claytondukes Jun 27, 2025
c08e8f8
fix: round prices to 2 decimal places to fix precision error
claytondukes Jun 27, 2025
8e131e4
refactor: switch from order_timeout_hours to order_timeout_seconds wi…
claytondukes Jun 27, 2025
9007bcf
update: configure schedule template with limit orders using 0.01% dis…
claytondukes Jun 27, 2025
cc7f955
docs: update README with correct default limit price percentage
claytondukes Jun 27, 2025
7024aa8
feat: post-only limit orders with fallback; docs and env updates
claytondukes Oct 17, 2025
94ce222
Merge pull request #1 from claytondukes/feat/limit-orders
claytondukes Oct 17, 2025
8c7e3f0
Updating per copilot pr feedback
claytondukes Oct 17, 2025
c2cfe26
Merge pull request #2 from claytondukes/feat/limit-orders
claytondukes Oct 17, 2025
f8a4c39
fix: address copilot feedback - order message, logging config, cleanup
claytondukes Oct 17, 2025
3ece2d4
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
0cf26ef
fix: preserve Decimal precision for price/size calculations, use keyw…
claytondukes Oct 17, 2025
f31bbab
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
5b5b087
refactor: optimize product fetching, extract rounding helper, update …
claytondukes Oct 17, 2025
ad239e6
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
f1b062d
fix: handle SDK typed responses, ensure logs dir, improve docs
claytondukes Oct 17, 2025
d2a5e8e
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
da40648
refactor: logging, Decimal precision, and scheduler cleanup
claytondukes Oct 17, 2025
e03b4e9
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
f190d4c
build(docker): noninteractive tzdata and layer-caching; add .dockerig…
claytondukes Oct 17, 2025
2e58f6f
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
7b69c65
fix(orders): log fallback market order id and improve Decimal quantiz…
claytondukes Oct 17, 2025
cf5d2ce
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
db0c6c7
fix(auth): remove duplicate dotenv load; update example usage; log fa…
claytondukes Oct 17, 2025
0c2b089
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
499b436
docs: Advanced Trade branding and frequencies; chore: robust dotenv i…
claytondukes Oct 17, 2025
1d90813
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
8939b06
refactor(runtime): TZ-aware startup & Docker logs; fix(auth): market …
claytondukes Oct 17, 2025
fe9580e
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
48d4125
refactor(runtime): TZ-aware startup; unify logging; improve order val…
claytondukes Oct 17, 2025
ed4bae6
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
a8149f1
feat(runtime): env-configurable schedule file; remove scheduler runner
claytondukes Oct 17, 2025
a22c1b5
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
173c7a3
fix(orders): poll to terminal after cancel and recompute remaining be…
claytondukes Oct 17, 2025
cd8c881
Merge branch 'feat/limit-orders'
claytondukes Oct 17, 2025
c8339ad
feat(repricing): add maker repricing before fallback; wire schedule o…
claytondukes Oct 19, 2025
f5d2e6b
refactor: use RepriceConfig; add safe_float;
claytondukes Oct 19, 2025
c4094e2
fix(repricing): use RepriceConfig at all call sites; harden None fall…
claytondukes Oct 19, 2025
75a34a4
fix(repricing): wait before first reprice; post-only nudge; success-o…
claytondukes Oct 19, 2025
4f227cf
refactor(repricing): log refresh only on success; add error extractor…
claytondukes Oct 19, 2025
baa25b1
pr fix for decimal
claytondukes Oct 19, 2025
e7c22e0
pr fix for decimal
claytondukes Oct 19, 2025
5ec0c44
style(repricing): use REPRICE_POLL_SLEEP for polling; clamp repricer …
claytondukes Oct 19, 2025
6bfef7e
pr fix for decimal
claytondukes Oct 19, 2025
c461235
Merge pull request #3 from claytondukes/feat/reprice-before-fallback
claytondukes Oct 19, 2025
1a59e0a
feat(orders,scheduler), add 'once', GTC limit price
claytondukes Oct 22, 2025
536fed3
fix(scheduler,orders): improve 'once' behavior and add absolute price…
claytondukes Oct 23, 2025
c7d4d10
fix: improve exception handling and logging
claytondukes Oct 23, 2025
76f3cff
refactor: simplify code and improve documentation
claytondukes Oct 23, 2025
98c0555
fix: improve time_in_force handling and documentation
claytondukes Oct 23, 2025
8b0c085
fix: prevent duplicate execution and improve error visibility
claytondukes Oct 23, 2025
4e155e5
fix: correct time comparison and log message placement
claytondukes Oct 23, 2025
aaabb3e
fix: validate post_only absolute price and improve logging
claytondukes Oct 23, 2025
dbaf57f
Merge pull request #4 from claytondukes/feat/add-gtc-orders
claytondukes Oct 23, 2025
e499b36
fix: clarify 'once' schedule log message
claytondukes Oct 23, 2025
e9ce608
Merge pull request #5 from claytondukes/feat/add-gtc-orders
claytondukes Oct 23, 2025
8ff105d
updated gitignore
Oct 31, 2025
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
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
venv/
__pycache__/
*.pyc
.env
.git
.gitignore
16 changes: 16 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Coinbase Advanced Trade credentials
COINBASE_API_KEY="organizations/<org-id>/apiKeys/<key-id>"
# Paste the EC private key exactly as provided by Coinbase Advanced Trade.
# Both multiline PEM and \n-escaped formats are supported.
COINBASE_API_SECRET="-----BEGIN EC PRIVATE KEY-----\n...your-private-key...\n-----END EC PRIVATE KEY-----\n"

# Timezone used by the scheduler (container/local process)
# Example: America/New_York (Eastern Time)
TZ=America/New_York

# Verbose SDK logging (optional). Accepted truthy values:
# 1, true, yes, on, debug (case-insensitive)
COINBASE_VERBOSE=false

# Schedule file to load (default: schedule.json)
SCHEDULE_FILE=schedule.json
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
schedule.json*
schedule-test.json*

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down Expand Up @@ -134,5 +137,3 @@ example_*.json
*.pkl
test_script.py

schedule.json
test_json.json
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM python:3.11-slim

ENV DEBIAN_FRONTEND=noninteractive

# Install tzdata non-interactively and clean apt lists
RUN apt-get update -yqq \
&& apt-get install -yqq --no-install-recommends tzdata \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /usr/src/app

# Leverage layer caching for dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy the rest of the source
COPY . .
213 changes: 185 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,215 @@
# crypto-dca-bot
# Crypto DCA Bot

Tool to automate scheduled cryptocurrency purchases on Coinbase Advanced Trader (Previously branded as Coinbase Pro).
A tool to automate scheduled cryptocurrency purchases on Coinbase Advanced
Trade (previously branded as Coinbase Pro).

Frequently used for DCA (Dollar-Cost Averaging) investment strategies.
Frequently used for DCA (Dollar-Cost Averaging) investment strategies to reduce
market volatility impact by making regular, fixed-amount purchases regardless of
price.

I used to buy crypto on a weekly basis, but I found the mainstream providers charged a lot of fees for this service. I wanted to automate this process and reduce the fees I was paying by executing these trades through their trade platforms instead which have much lower fees.
## Features

## Notes
- Schedule cryptocurrency purchases on a seconds, hourly, daily, weekly, or monthly basis
- Configure purchases for multiple currency pairs
- Specify the exact time and day for transactions
- Supports post-only limit (maker) and market (taker) orders; optional
maker-first with fallback
- Lower fees compared to standard Coinbase purchases
- Flexible deployment options (local Python or Docker)

- Originally started working on Binance and connecting to their APIs, but due to some personal consumer issues I've had with them, will be switching focus to Coinbase Advanced Trader.
## Background

## Guide
Using the Coinbase Advanced Trade API automates time-based cryptocurrency
purchases and helps minimize fees. Executing trades directly through Advanced
Trade reduces transaction costs compared to recurring buys on the standard
Coinbase platform.

This guide assumes you have some knowledge of setting up a Python environment and running Python scripts.
## Installation

1. Create a Coinbase account and generate an API key & secret with the following permissions:
```
wallet:accounts:read
wallet:buys:create
wallet:orders:read
wallet:transactions:read
wallet:user:read
```
### Prerequisites

2. Create a `.env` file in the root directory of the project and add the following variables with these API details:
- Python 3.11+
- Coinbase account with API access
- The required Python packages:

```text
python-dotenv
requests
schedule
coinbase-advanced-py
```
COINBASE_API_KEY=
COINBASE_API_SECRET=

### Setup

1. Clone the repository or download the source code
2. Install dependencies:

```bash
pip install -r requirements.txt
```

3. Rename or copy the `schedule_template.json` to `schedule.json` and add your schedule details.
## Configuration Guide

The time set is in 24 hour format and is based on the local time of the machine running the script.
Basic familiarity with Python environments and script execution is assumed.

**NOTE:** Currently, you can only schedule market order BUY side transactions e.g. `BTC/GBP` means you are buying `BTC` with the `quote_currency_amount` of `GBP`.
1. Create a Coinbase account and generate an API key & secret with the following
permissions:

```
```text
View permissions
Trade permissions
```

Select both View and Trade permissions when creating the API key in Coinbase
Advanced.

2. Create a `.env` file in the project root and add the following variables with
API details from Coinbase Advanced Trade:

```text
COINBASE_API_KEY="organizations/your-org-id/apiKeys/your-key-id"
COINBASE_API_SECRET="-----BEGIN EC PRIVATE KEY-----\n...your private key...\n-----END EC PRIVATE KEY-----\n"
```

**Note:** The Coinbase Advanced Trade API uses a different format for API
keys than the previous version. Use the full
`organizations/<org-id>/apiKeys/<key-id>` format as shown above.

3. Create `schedule.json` from `schedule-sample.json` and define the schedule.

Times are in 24‑hour format and interpreted in the process timezone.

Each transaction can be configured as a limit (maker) or market (taker) order.
Limit orders help reduce fees and eliminate price slippage. By default, the bot
uses limit orders with a price set slightly below market price to prefer maker
execution. For example, `BTC/USDC` indicates buying BTC with the specified USDC
amount.

```json
[
{
"frequency": "daily",
"day_of_week": null,
"time": "10:30",
"currency_pair": "ETH/GBP",
"quote_currency_amount": 1
"currency_pair": "ETH/USDC",
"quote_currency_amount": 1,
"order_type": "limit",
"limit_price_pct": 0.01,
"order_timeout_seconds": 600
},
{
"frequency": "weekly",
"day_of_week": "wednesday",
"time": "15:45",
"currency_pair": "BTC/GBP",
"quote_currency_amount": 1
"currency_pair": "BTC/USDC",
"quote_currency_amount": 1,
"order_type": "market"
}
]
```

3. Run `main.py` script.
## Running the Bot

### Method 1: Python Script

Start the bot:

```bash
python main.py
```

The bot starts and executes trades according to the defined schedule.

### Method 2: Docker Deployment

The project includes Docker configuration for easy deployment and automated
restarts.

1. Docker and Docker Compose must be installed.
2. Start the container:

```bash
docker compose build
docker compose up -d
```

The container runs the bot in the background with automatic restarts. View
logs with:

```bash
docker compose logs -f --tail=100
```

## Notes

- The bot runs indefinitely, executing trades based on the schedule
configuration.
- All transactions are logged for reference and troubleshooting.
- For limit orders, the price is set at a percentage (`limit_price_pct`) of the
current market price (default: 0.01%) to ensure orders execute as maker orders
with lower fees.
- Limit orders can be configured with an auto-cancellation time using
`order_timeout_seconds`. This setting determines how long (in seconds) a limit
order remains active before being auto-cancelled if not filled. Default is 600
seconds (10 minutes). This prevents stuck orders.
- If `order_type` is not specified, limit orders are used by default. To use
market orders, set `order_type` to "market".

## Environment (.env)

A `.env` file in the project root is loaded by Docker Compose via `env_file:
.env`.

```env
# Coinbase Advanced Trade credentials
COINBASE_API_KEY="organizations/<org-id>/apiKeys/<key-id>"
# Paste the EC private key exactly as provided by Coinbase Advanced Trade.
# Both multiline PEM and \n-escaped formats are supported.
COINBASE_API_SECRET="-----BEGIN EC PRIVATE KEY-----\n...your-key...\n-----END EC PRIVATE KEY-----\n"

# Timezone used by the scheduler (container/local process)
# Example: America/New_York (Eastern Time)
TZ=America/New_York

# Verbose SDK logging (optional). Accepted truthy values:
# 1, true, yes, on, debug (case-insensitive)
COINBASE_VERBOSE=false
```

## Maker-first with quick fallback

- **Post-only limit orders** keep fees low by resting on the book.
- Set a small discount using `limit_price_pct` (percent-of-100), e.g. `0.01`
(0.01%), `0.3` (0.3%), `0.4` (0.4%).
- A short `order_timeout_seconds` (for example, `300–600`) guarantees completion
via fallback-to-market after expiry.

Example schedule entry (maker-first):

```json
{
"frequency": "daily",
"time": "15:45",
"currency_pair": "BTC/USDC",
"quote_currency_amount": 20,
"order_type": "limit",
"limit_price_pct": 0.01,
"post_only": true,
"order_timeout_seconds": 600
}
```

## Timezone behavior

- The scheduler interprets `time` in the process timezone.
- In Docker, set `TZ` in `.env` (for example, `TZ=America/New_York`) so jobs run
in that timezone.
- On bare Python, the host system timezone determines interpretation; configure
as needed.

## Schedule files

- The bot loads `schedule.json` by default in `main.py`.
- Keep your full plan in `schedule-full.json` if desired, then copy to
`schedule.json` for deployment, or change the loader in `main.py` to point to
your preferred file.
Loading