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
143 changes: 129 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ env:

jobs:
test:
name: Build and Test
name: Test (Elixir ${{matrix.elixir}} / OTP ${{matrix.otp}})
runs-on: ubuntu-latest

strategy:
matrix:
elixir: ["1.18.4"]
otp: ["28.0"]

services:
postgres:
image: timescale/timescaledb:latest-pg16
Expand All @@ -31,46 +36,156 @@ jobs:
ports:
- 5432:5432

redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}
elixir-version: ${{ matrix.elixir }}
otp-version: ${{ matrix.otp }}

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-v2-${{ hashFiles('**/mix.lock') }}
key: ${{ runner.os }}-mix-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-v2-

- name: Clean dependencies
run: mix deps.clean --all || true
${{ runner.os }}-mix-${{ matrix.otp }}-${{ matrix.elixir }}-

- name: Install dependencies
run: mix deps.get

- name: Compile dependencies
run: mix deps.compile

- name: Compile application
run: mix compile
run: mix compile --warnings-as-errors

- name: Check formatting
run: mix format --check-formatted

- name: Create and migrate database
- name: Run Credo
run: mix credo --strict

- name: Create test database
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/binance_trading_test
run: |
mix ecto.create
mix ecto.migrate
run: mix ecto.create

- name: Run migrations
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/binance_trading_test
run: mix ecto.migrate

- name: Run tests
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/binance_trading_test
run: mix test
REDIS_URL: redis://localhost:6379/0
run: mix test --cover --warnings-as-errors

- name: Generate coverage report
run: mix coveralls.html
continue-on-error: true

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
files: ./cover/excoveralls.json
fail_ci_if_error: false
continue-on-error: true

dialyzer:
name: Dialyzer
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-

- name: Restore PLT cache
uses: actions/cache@v4
with:
path: priv/plts
key: ${{ runner.os }}-plt-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-plt-${{ env.OTP_VERSION }}-${{ env.ELIXIR_VERSION }}-

- name: Install dependencies
run: mix deps.get

- name: Run Dialyzer
run: mix dialyzer --format github
continue-on-error: true

security:
name: Security Audit
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: ${{ env.ELIXIR_VERSION }}
otp-version: ${{ env.OTP_VERSION }}

- name: Install dependencies
run: mix deps.get

- name: Check for retired dependencies
run: mix hex.audit
continue-on-error: true

build-docker:
name: Build Docker Image
runs-on: ubuntu-latest
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build development image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.dev
push: false
tags: binance-trading-dev:latest
cache-from: type=gha
cache-to: type=gha,mode=max
38 changes: 38 additions & 0 deletions FORMAT_BEFORE_MERGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Форматирование кода перед merge

## Быстрый способ - одна команда:

```bash
mix format
git add .
git commit -m "chore: auto-format code with mix format"
git push
```

## Если у вас Docker:

```bash
docker-compose run app mix format
git add .
git commit -m "chore: auto-format code with mix format"
git push
```

## Или через make (если есть в Makefile):

```bash
make format
git add .
git commit -m "chore: auto-format code with mix format"
git push
```

## После форматирования:

Все файлы будут автоматически приведены к стандарту Elixir formatter.
Затем можно будет делать merge в master без проблем с CI.

## Файлы, которые нужно отформатировать:

Список файлов был предоставлен в сообщении об ошибке CI.
`mix format` автоматически обработает их все.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ defmodule DashboardWeb.CoreComponents do
)
end

defp hide(js \\ %JS{}, selector) do
defp hide(js, selector) do
JS.hide(js,
to: selector,
time: 200,
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard_web/lib/dashboard_web/gettext.ex
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
defmodule DashboardWeb.Gettext do
use Gettext, otp_app: :dashboard_web
use Gettext.Backend, otp_app: :dashboard_web
end
3 changes: 1 addition & 2 deletions apps/dashboard_web/lib/dashboard_web/live/history_live.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule DashboardWeb.HistoryLive do
use DashboardWeb, :live_view

alias SharedData.{Trading}

alias SharedData.Helpers.DecimalHelper

@impl true
Expand Down
5 changes: 2 additions & 3 deletions apps/dashboard_web/lib/dashboard_web/live/portfolio_live.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule DashboardWeb.PortfolioLive do
use DashboardWeb, :live_view

alias SharedData.{Accounts, Trading}

alias SharedData.Helpers.DecimalHelper

@impl true
Expand Down Expand Up @@ -154,7 +153,7 @@ defmodule DashboardWeb.PortfolioLive do
|> assign(total_pnl: Decimal.new(0))
end

defp calculate_value(balance) do
defp calculate_value(_balance) do
# TODO: Calculate USDT value based on current prices
# For now, return dash
"-"
Expand Down
4 changes: 1 addition & 3 deletions apps/dashboard_web/lib/dashboard_web/live/settings_live.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
defmodule DashboardWeb.SettingsLive do
use DashboardWeb, :live_view

alias SharedData.{Accounts, Trading}

@impl true
def mount(_params, _session, socket) do
Expand All @@ -22,7 +20,7 @@ defmodule DashboardWeb.SettingsLive do
end

@impl true
def handle_event("activate_strategy", %{"id" => strategy_id}, socket) do
def handle_event("activate_strategy", %{"id" => _strategy_id}, socket) do
# TODO: Implement strategy activation
{:noreply, put_flash(socket, :info, "Strategy activation requested")}
end
Expand Down
7 changes: 3 additions & 4 deletions apps/dashboard_web/lib/dashboard_web/live/trading_live.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule DashboardWeb.TradingLive do
use DashboardWeb, :live_view

alias SharedData.{Accounts, Trading}

alias SharedData.Helpers.DecimalHelper

@impl true
Expand All @@ -28,7 +27,7 @@ defmodule DashboardWeb.TradingLive do
end

@impl true
def handle_info({:execution_report, data}, socket) do
def handle_info({:execution_report, _data}, socket) do
# Reload orders when execution report received
{:noreply, load_data(socket)}
end
Expand All @@ -42,7 +41,7 @@ defmodule DashboardWeb.TradingLive do
def handle_info(_, socket), do: {:noreply, socket}

@impl true
def handle_event("cancel_order", %{"id" => order_id}, socket) do
def handle_event("cancel_order", %{"id" => _order_id}, socket) do
# TODO: Implement order cancellation
{:noreply, put_flash(socket, :info, "Order cancellation requested")}
end
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard_web/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defmodule DashboardWeb.MixProject do
{:phoenix_live_dashboard, "~> 0.8"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.20"},
{:jason, "~> 1.4"},
{:plug_cowboy, "~> 2.6"},
{:shared_data, in_umbrella: true},
Expand Down
2 changes: 1 addition & 1 deletion apps/data_collector/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule DataCollector.MixProject do

defp deps do
[
{:binance, "~> 1.0"},
{:binance, "~> 2.0"},
{:websockex, "~> 0.4"},
# httpoison is already included as a dependency of binance (~> 1.4)
{:jason, "~> 1.4"},
Expand Down
5 changes: 4 additions & 1 deletion apps/shared_data/.formatter.exs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.gitignore
[
import_deps: [:ecto, :ecto_sql],
inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
]
2 changes: 1 addition & 1 deletion apps/shared_data/lib/shared_data/pubsub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ defmodule SharedData.PubSub do
## Topics

### Market Data
- `market:#{symbol}` - Ticker and trade updates for a specific symbol
- `market:\#{symbol}` - Ticker and trade updates for a specific symbol
- Messages: `{:ticker, data}`, `{:trade, data}`
- Publishers: BinanceWebSocket
- Subscribers: MarketData, Trader, TradingLive
Expand Down
3 changes: 2 additions & 1 deletion apps/trading_engine/lib/trading_engine/risk_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ defmodule TradingEngine.RiskManager do
defp check_position_size(_, _), do: :ok

@spec check_daily_loss(map()) :: :ok | {:error, String.t()}
defp check_daily_loss(state) do
defp check_daily_loss(_state) do
# This would need to query database for today's trades
# For now, simplified implementation
# TODO: Implement actual daily loss check using @max_daily_loss
:ok
end

Expand Down
8 changes: 4 additions & 4 deletions apps/trading_engine/lib/trading_engine/strategies/naive.ex
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,20 @@ defmodule TradingEngine.Strategies.Naive do
Decimal.sub(current_price, state.last_price),
state.last_price
)
Decimal.compare(price_change, Decimal.minus(state.buy_down_interval)) == :lt

Decimal.compare(price_change, Decimal.negate(state.buy_down_interval)) == :lt
end

defp should_sell?(_current_price, %{position: nil}), do: false

defp should_sell?(current_price, state) do
entry_price = state.position.entry_price

price_change = Decimal.div(
Decimal.sub(current_price, entry_price),
entry_price
)

Decimal.compare(price_change, state.sell_up_interval) == :gt
end
end
2 changes: 0 additions & 2 deletions apps/trading_engine/lib/trading_engine/trader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ defmodule TradingEngine.Trader do
require Logger

alias DataCollector.BinanceClient
alias TradingEngine.OrderManager
alias TradingEngine.PositionTracker
alias TradingEngine.RiskManager
alias SharedData.{Config, Types}

Expand Down
Loading