///|
pub(all) enum MarketRefreshAction {
RunNow
WaitForBarClose
AwaitExternalSnapshot
ReviewTimeframe
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn MarketRefreshAction::label(self : MarketRefreshAction) -> String {
match self {
RunNow => "run_now"
WaitForBarClose => "wait_for_bar_close"
AwaitExternalSnapshot => "await_external_snapshot"
ReviewTimeframe => "review_timeframe"
}
}
///|
pub(all) struct MarketRefreshInput {
request : MarketSourceRequest
now_ms : Int64
newest_bar_open_ms : Int64?
head_bar_closed : Bool
user_refresh_ms : Int
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct MarketRefreshPlan {
request : MarketSourceRequest
timeframe_seconds : Int?
effective_refresh_ms : Int
snapshot_cache_ttl_seconds : Int
wait_seconds : Int
action : MarketRefreshAction
safe_to_request_snapshot : Bool
can_run_analysis_now : Bool
summary : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn market_refresh_input(
request : MarketSourceRequest,
now_ms? : Int64 = 0L,
newest_bar_open_ms? : Int64,
head_bar_closed? : Bool = true,
user_refresh_ms? : Int = 1000,
) -> MarketRefreshInput {
{
request,
now_ms: if now_ms == 0L {
request.captured_at_ms
} else {
now_ms
},
newest_bar_open_ms,
head_bar_closed,
user_refresh_ms,
}
}
///|
pub fn timeframe_seconds(timeframe : String) -> Int? {
match timeframe.trim().to_owned().to_lower() {
"1m" => Some(60)
"2m" => Some(120)
"3m" => Some(180)
"5m" => Some(300)
"15m" => Some(900)
"30m" => Some(1800)
"1h" => Some(3600)
"2h" => Some(7200)
"4h" => Some(14400)
"1d" => Some(86400)
"1w" => Some(604800)
_ => None
}
}
///|
fn http_poll_source(source : @domain.DataSource) -> Bool {
source is AkShare || source is Yfinance
}
///|
fn effective_refresh_interval_ms(
source : @domain.DataSource,
timeframe : String,
user_ms : Int,
) -> Int {
let mut interval = user_ms
if interval < 500 {
interval = 500
}
if http_poll_source(source) && interval < 2500 {
interval = 2500
}
if http_poll_source(source) &&
timeframe.trim().to_owned().to_lower() == "1d" &&
interval < 3000 {
interval = 3000
}
interval
}
///|
fn snapshot_cache_ttl_seconds(timeframe : String) -> Int {
match timeframe.trim().to_owned().to_lower() {
"1m" => 4
"1d" | "1w" | "1mth" => 12
_ => 8
}
}
///|
fn seconds_until_close(
now_ms : Int64,
open_ms : Int64,
duration_seconds : Int,
) -> Int {
let duration_ms = duration_seconds.to_int64() * 1000L
let elapsed_ms = now_ms - open_ms
if elapsed_ms == 0L {
duration_seconds
} else {
let remainder = elapsed_ms.mod(duration_ms)
if remainder == 0L {
if elapsed_ms > 0L {
0
} else {
duration_seconds
}
} else {
let remaining_ms = duration_ms - remainder
((remaining_ms + 999L) / 1000L).to_int()
}
}
}
///|
fn forming_wait_seconds(input : MarketRefreshInput, duration : Int?) -> Int {
match (input.head_bar_closed, input.newest_bar_open_ms, duration) {
(false, Some(open_ms), Some(seconds)) =>
seconds_until_close(input.now_ms, open_ms, seconds)
_ => 0
}
}
///|
fn refresh_action(
input : MarketRefreshInput,
duration : Int?,
wait_seconds : Int,
) -> MarketRefreshAction {
match duration {
None => ReviewTimeframe
Some(_) =>
if !input.head_bar_closed && wait_seconds > 0 {
WaitForBarClose
} else if input.request.data_source is Fixture {
RunNow
} else {
AwaitExternalSnapshot
}
}
}
///|
fn refresh_summary(plan : MarketRefreshPlan) -> String {
match plan.action {
RunNow =>
"refresh plan ready: fixture evidence can run analysis now for \{plan.request.symbol} \{plan.request.timeframe}"
WaitForBarClose =>
"refresh plan waiting \{plan.wait_seconds}s for closed-bar evidence before requesting \{plan.request.symbol} \{plan.request.timeframe}"
AwaitExternalSnapshot =>
"refresh plan ready for external snapshot request; analysis awaits submitted evidence for \{plan.request.symbol} \{plan.request.timeframe}"
ReviewTimeframe =>
"refresh plan needs timeframe review for \{plan.request.timeframe}"
}
}
///|
pub fn prepare_market_refresh_plan(
input : MarketRefreshInput,
) -> MarketRefreshPlan {
let duration = timeframe_seconds(input.request.timeframe)
let wait_seconds = forming_wait_seconds(input, duration)
let action = refresh_action(input, duration, wait_seconds)
let plan : MarketRefreshPlan = {
request: input.request,
timeframe_seconds: duration,
effective_refresh_ms: effective_refresh_interval_ms(
input.request.data_source,
input.request.timeframe,
input.user_refresh_ms,
),
snapshot_cache_ttl_seconds: snapshot_cache_ttl_seconds(
input.request.timeframe,
),
wait_seconds,
action,
safe_to_request_snapshot: action is RunNow ||
action is AwaitExternalSnapshot,
can_run_analysis_now: action is RunNow,
summary: "",
}
{ ..plan, summary: refresh_summary(plan) }
}