Compare commits

..

2 Commits

Author SHA1 Message Date
0e154f4ced Load from saved state after crash. 2026-02-11 20:29:40 -05:00
b917a0297c Added dashboard values 2026-02-11 20:27:59 -05:00
2 changed files with 43 additions and 10 deletions

View File

@@ -59,7 +59,7 @@ defmodule Core.Bots.DCABot do
require Logger require Logger
@tick_interval 60_000 # 1 minute @tick_interval 60_000 # 1 minute
@drop_threshold 0.04 # 4% price drop triggers auto-buy @drop_threshold 0.03 # 3% price drop triggers auto-buy
@profit_threshold 0.20 # 20% total return triggers auto-sell @profit_threshold 0.20 # 20% total return triggers auto-sell
# Public API # Public API
@@ -156,6 +156,10 @@ defmodule Core.Bots.DCABot do
def init(%{symbol: symbol, restore_state: restore_state}) do def init(%{symbol: symbol, restore_state: restore_state}) do
account_id = Core.Client.account_id() account_id = Core.Client.account_id()
# If no restore_state was explicitly provided, check if there's saved state for this symbol
restore_state =
restore_state || find_saved_state_for_symbol(symbol)
state = state =
if restore_state do if restore_state do
# Restoring from saved state # Restoring from saved state
@@ -175,6 +179,7 @@ defmodule Core.Bots.DCABot do
bot_shares: shares, bot_shares: shares,
bot_cost_basis: cost_basis, bot_cost_basis: cost_basis,
current_return_pct: return_pct, current_return_pct: return_pct,
current_pl_dollars: 0.0,
sell_and_stop_pending: false sell_and_stop_pending: false
} }
else else
@@ -190,6 +195,7 @@ defmodule Core.Bots.DCABot do
bot_shares: 0, bot_shares: 0,
bot_cost_basis: 0.0, bot_cost_basis: 0.0,
current_return_pct: 0.0, current_return_pct: 0.0,
current_pl_dollars: 0.0,
sell_and_stop_pending: false sell_and_stop_pending: false
} }
end end
@@ -265,13 +271,18 @@ defmodule Core.Bots.DCABot do
end end
@impl true @impl true
def handle_call(:position, _from, %{symbol: symbol, bot_shares: bot_shares, bot_cost_basis: bot_cost_basis, current_return_pct: current_return_pct, last_purchase_price: last_purchase_price, sell_and_stop_pending: sell_and_stop_pending} = state) do def handle_call(:position, _from, %{symbol: symbol, bot_shares: bot_shares, bot_cost_basis: bot_cost_basis, current_return_pct: current_return_pct, current_pl_dollars: current_pl_dollars, last_price: last_price, last_purchase_price: last_purchase_price, sell_and_stop_pending: sell_and_stop_pending} = state) do
next_buy_price = if last_purchase_price, do: last_purchase_price * (1 - @drop_threshold), else: nil
position_info = %{ position_info = %{
symbol: symbol, symbol: symbol,
shares: bot_shares, shares: bot_shares,
cost_basis: bot_cost_basis, cost_basis: bot_cost_basis,
avg_price: if(bot_shares > 0, do: bot_cost_basis / bot_shares, else: 0.0), avg_price: if(bot_shares > 0, do: bot_cost_basis / bot_shares, else: 0.0),
current_price: last_price,
next_buy_price: next_buy_price,
return_pct: current_return_pct, return_pct: current_return_pct,
pl_dollars: current_pl_dollars,
last_purchase_price: last_purchase_price, last_purchase_price: last_purchase_price,
sell_and_stop_pending: sell_and_stop_pending sell_and_stop_pending: sell_and_stop_pending
} }
@@ -319,12 +330,14 @@ defmodule Core.Bots.DCABot do
price = quote.last price = quote.last
# Calculate current return # Calculate current return
return_pct = {return_pct, pl_dollars} =
if bot_shares > 0 && bot_cost_basis > 0 do if bot_shares > 0 && bot_cost_basis > 0 do
current_value = bot_shares * price current_value = bot_shares * price
Float.round((current_value - bot_cost_basis) / bot_cost_basis * 100, 2) pct = Float.round((current_value - bot_cost_basis) / bot_cost_basis * 100, 2)
dollars = Float.round(current_value - bot_cost_basis, 2)
{pct, dollars}
else else
0.0 {0.0, 0.0}
end end
# Log price and bot position status # Log price and bot position status
@@ -335,7 +348,7 @@ defmodule Core.Bots.DCABot do
Logger.info("#{symbol} current price: $#{price} | Bot: no position") Logger.info("#{symbol} current price: $#{price} | Bot: no position")
end end
state = %{state | last_price: price, current_return_pct: return_pct} state = %{state | last_price: price, current_return_pct: return_pct, current_pl_dollars: pl_dollars}
# Check if we should auto-sell (position up 20%) # Check if we should auto-sell (position up 20%)
state = maybe_auto_sell(state, price) state = maybe_auto_sell(state, price)
@@ -452,7 +465,8 @@ defmodule Core.Bots.DCABot do
bot_shares: 0, bot_shares: 0,
bot_cost_basis: 0.0, bot_cost_basis: 0.0,
last_purchase_price: nil, last_purchase_price: nil,
current_return_pct: 0.0 current_return_pct: 0.0,
current_pl_dollars: 0.0
} }
# Save state after reset # Save state after reset
@@ -622,4 +636,12 @@ defmodule Core.Bots.DCABot do
false false
end end
end end
defp find_saved_state_for_symbol(symbol) do
state_data = Core.Bots.BotPersistence.load_state()
Enum.find(state_data, fn bot_state ->
bot_state["symbol"] == symbol
end)
end
end end

View File

@@ -306,13 +306,24 @@ defmodule TraderexWeb.DashboardLive do
<:col :let={bot} label="Cost Basis"> <:col :let={bot} label="Cost Basis">
{format_currency(bot.cost_basis)} {format_currency(bot.cost_basis)}
</:col> </:col>
<:col :let={bot} label="Current Price">
{format_currency(Map.get(bot, :current_price))}
</:col>
<:col :let={bot} label="Avg Price"> <:col :let={bot} label="Avg Price">
{format_currency(bot.avg_price)} {format_currency(bot.avg_price)}
</:col> </:col>
<:col :let={bot} label="Next Buy Price">
{format_currency(Map.get(bot, :next_buy_price))}
</:col>
<:col :let={bot} label="Return"> <:col :let={bot} label="Return">
<span class={return_color(bot.return_pct)}> <div class="flex flex-col">
{format_percent_simple(bot.return_pct)} <span class={return_color(bot.return_pct)}>
</span> {format_percent_simple(bot.return_pct)}
</span>
<span class={["text-sm", pl_color(Map.get(bot, :pl_dollars, 0))]}>
{format_currency(Map.get(bot, :pl_dollars, 0))}
</span>
</div>
</:col> </:col>
<:col :let={bot} label="Actions"> <:col :let={bot} label="Actions">
<button <button