Compare commits

...

6 Commits

Author SHA1 Message Date
239f3cb4c1 signal file init
Some checks are pending
CI / test-clj (push) Waiting to run
CI / test-cljs (push) Waiting to run
2025-03-14 00:54:31 -05:00
d59c175629 generic signal instructions 2025-03-14 00:54:22 -05:00
616b33fe20 buy/hold keyword exec stack handling 2025-03-14 00:54:13 -05:00
617b03aeb6 start of stock regression 2025-03-14 00:48:43 -05:00
2ad54d8189 buy,hold,sell comparison functions 2025-03-12 17:43:03 -05:00
4ff98629f9 add instructions, is breaking things tho? 2025-03-12 16:42:28 -05:00
6 changed files with 244 additions and 17 deletions

View File

@ -0,0 +1,92 @@
(ns propeller.problems.regression.stock-regression
(:require [propeller.genome :as genome]
[propeller.push.interpreter :as interpreter]
[propeller.push.state :as state]
[propeller.tools.math :as math]
[propeller.gp :as gp]
#?(:cljs [cljs.reader :refer [read-string]])))
(defn- target-function
"Target function: f(x) = x^3 + 2*x^2 + x + 3"
[x]
(+ (* x x x) (* 2 x x) x 3))
(def train-and-test-data
"Training data: Inputs and outputs with -10 <= x < 11
Test data: Inputs and outputs of -20 <= x < -10 and 11 <= x < 21"
(let [train-inputs (range -10 11)
test-inputs (concat (range -20 -10) (range 11 21))]
{:train (map (fn [x] {:input1 (vector x) :output1 (vector (target-function x))}) train-inputs)
:test (map (fn [x] {:input1 (vector x) :output1 (vector (target-function x))}) test-inputs)}))
(def instructions
"stack-specific instructions, input instructions, close, and constants"
(list :in1
:integer_add
:integer_subtract
:integer_mult
:integer_quot
:integer_eq
:exec_dup
:exec_if
'close
0
1
:buy
:sell
:hold
))
(defn error-function
"Finds the behaviors and errors of an individual. The error is the absolute
deviation between the target output value and the program's selected behavior,
or 1000000 if no behavior is produced. The behavior is here defined as the
final top item on the INTEGER stack."
([argmap data individual]
(let [program (genome/plushy->push (:plushy individual) argmap)
inputs (map (fn [x] (first (:input1 x))) data)
correct-outputs (map (fn [x] (first (:output1 x))) data)
outputs (map (fn [input]
(state/peek-stack
(interpreter/interpret-program
program
(assoc state/empty-state :input {:in1 input})
(:step-limit argmap))
:integer))
inputs)
errors (map (fn [correct-output output]
(if (= output :no-stack-item)
1000000
(math/abs (- correct-output output))))
correct-outputs
outputs)]
(assoc individual
:behaviors outputs
:errors errors
:total-error #?(:clj (apply +' errors)
:cljs (apply + errors))))))
(def integer-argmap
{:instructions instructions
:error-function error-function
:training-data (:train train-and-test-data)
:testing-data (:test train-and-test-data)
:max-generations 300
:population-size 1000
:max-initial-plushy-size 5
:step-limit 200
:parent-selection :lexicase
:tournament-size 5
:umad-rate 0.1
:variation {:umad 1.0 :crossover 0.0}
:elitism false})
(defn -main
"Runs the top-level genetic programming function, giving it a map of
arguments with defaults that can be overridden from the command line
or through a passed map."
[& args]
(gp/gp
(merge
integer-argmap
(apply hash-map (map #(if (string? %) (read-string %) %) args)))))

View File

@ -20,6 +20,36 @@
(fn [stack state] (fn [stack state]
(make-instruction state > [stack stack] :boolean))) (make-instruction state > [stack stack] :boolean)))
;; Pushes :buy onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_buy
"Pushes :buy onto the SIGNAL stack if the first item is greater
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gt_buy"}
(fn [stack state]
(make-instruction state #(if (> %1 %2) :buy nil) [stack stack] :signal)))
;; Pushes :sell onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_sell
"Pushes :sell onto the SIGNAL stack if the first item is greater
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gt_sell"}
(fn [stack state]
(make-instruction state #(if (> %1 %2) :sell nil) [stack stack] :signal)))
;; Pushes :hold onto the SIGNAL stack if the first item is greater than the second
;; item, and acts as a no-op otherwise.
(def _gt_hold
"Pushes :hold onto the SIGNAL stack if the first item is greater
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gt_hold"}
(fn [stack state]
(make-instruction state #(if (> %1 %2) :hold nil) [stack stack] :signal)))
;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than or ;; Pushes TRUE onto the BOOLEAN stack if the second item is greater than or
;; equal to the top item, and FALSE otherwise ;; equal to the top item, and FALSE otherwise
(def _gte (def _gte
@ -30,6 +60,36 @@
(fn [stack state] (fn [stack state]
(make-instruction state >= [stack stack] :boolean))) (make-instruction state >= [stack stack] :boolean)))
;; Pushes :buy onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_buy
"Pushes :buy onto the SIGNAL stack if the first item is greater
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gte_buy"}
(fn [stack state]
(make-instruction state #(if (>= %1 %2) :buy nil) [stack stack] :signal)))
;; Pushes :sell onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_sell
"Pushes :sell onto the SIGNAL stack if the first item is greater
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gte_sell"}
(fn [stack state]
(make-instruction state #(if (>= %1 %2) :sell nil) [stack stack] :signal)))
;; Pushes :hold onto the SIGNAL stack if the first item is greater than or equal to the second
;; item, and acts as a no-op otherwise.
(def _gte_hold
"Pushes :hold onto the SIGNAL stack if the first item is greater
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_gte_hold"}
(fn [stack state]
(make-instruction state #(if (>= %1 %2) :hold nil) [stack stack] :signal)))
;; Pushes TRUE onto the BOOLEAN stack if the second item is less than the top ;; Pushes TRUE onto the BOOLEAN stack if the second item is less than the top
;; item, and FALSE otherwise ;; item, and FALSE otherwise
(def _lt (def _lt
@ -40,6 +100,36 @@
(fn [stack state] (fn [stack state]
(make-instruction state < [stack stack] :boolean))) (make-instruction state < [stack stack] :boolean)))
;; Pushes :buy onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_buy
"Pushes :buy onto the SIGNAL stack if the first item is less
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lt_buy"}
(fn [stack state]
(make-instruction state #(if (< %1 %2) :buy nil) [stack stack] :signal)))
;; Pushes :sell onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_sell
"Pushes :sell onto the SIGNAL stack if the first item is less
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lt_sell"}
(fn [stack state]
(make-instruction state #(if (< %1 %2) :sell nil) [stack stack] :signal)))
;; Pushes :hold onto the SIGNAL stack if the first item is less than the second
;; item, and acts as a no-op otherwise.
(def _lt_hold
"Pushes :hold onto the SIGNAL stack if the first item is less
than the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lt_hold"}
(fn [stack state]
(make-instruction state #(if (< %1 %2) :hold nil) [stack stack] :signal)))
;; Pushes TRUE onto the BOOLEAN stack if the second item is less than or equal ;; Pushes TRUE onto the BOOLEAN stack if the second item is less than or equal
;; to the top item, and FALSE otherwise ;; to the top item, and FALSE otherwise
(def _lte (def _lte
@ -50,6 +140,36 @@
(fn [stack state] (fn [stack state]
(make-instruction state <= [stack stack] :boolean))) (make-instruction state <= [stack stack] :boolean)))
;; Pushes :buy onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_buy
"Pushes :buy onto the SIGNAL stack if the first item is less
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lte_buy"}
(fn [stack state]
(make-instruction state #(if (<= %1 %2) :buy nil) [stack stack] :signal)))
;; Pushes :sell onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_sell
"Pushes :sell onto the SIGNAL stack if the first item is less
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lte_sell"}
(fn [stack state]
(make-instruction state #(if (<= %1 %2) :sell nil) [stack stack] :signal)))
;; Pushes :hold onto the SIGNAL stack if the first item is less than or equal to the second
;; item, and acts as a no-op otherwise.
(def _lte_hold
"Pushes :hold onto the SIGNAL stack if the first item is less
than or equal to the second item, and acts as a no-op otherwise"
^{:stacks #{:signal}
:name "_lte_hold"}
(fn [stack state]
(make-instruction state #(if (<= %1 %2) :hold nil) [stack stack] :signal)))
;; Pushes the sum of the top two items onto the same stack ;; Pushes the sum of the top two items onto the same stack
(def _add (def _add
"Pushes the sum of the top two items onto the same stack" "Pushes the sum of the top two items onto the same stack"
@ -173,11 +293,11 @@ Otherwise, acts as a NOOP"
(fn [stack state] (fn [stack state]
(make-instruction state dec [stack] stack))) (make-instruction state dec [stack] stack)))
;; 2 types x 16 functions = 32 instructions
(generate-instructions (generate-instructions
[:float :integer] [:float :integer]
[_gt _gte _lt _lte _add _subtract _mult _quot _mod _max _min _inc _dec [_gt _gte _lt _lte _add _subtract _mult _quot _mod _max _min _inc _dec
_from_boolean _from_char _from_string]) _from_boolean _from_char _from_string _gt_buy _gt_sell _gt_hold _gte_buy
_gte_sell _gte_hold _lt_buy _lt_sell _lt_hold _lte_buy _lte_sell _lte_hold])
;; ============================================================================= ;; =============================================================================
;; FLOAT Instructions only ;; FLOAT Instructions only

View File

@ -232,9 +232,9 @@
(state/push-to-stack popped-state stack indexed-item)) (state/push-to-stack popped-state stack indexed-item))
state))) state)))
;; 11 types x 13 functions = 143 instructions ;; 12 types x 13 functions = 156 instructions
(generate-instructions (generate-instructions
[:boolean :char :code :exec :float :integer :string [:boolean :char :code :exec :float :integer :string
:vector_boolean :vector_float :vector_integer :vector_string] :vector_boolean :vector_float :vector_integer :vector_string :signal]
[_dup _dup_times _dup_items _empty _eq _flush _pop _rot _shove [_dup _dup_times _dup_items _empty _eq _flush _pop _rot _shove
_stack_depth _swap _yank _yank_dup _deep_dup]) _stack_depth _swap _yank _yank_dup _deep_dup])

View File

@ -0,0 +1,8 @@
(ns propeller.push.instructions.signal
"SIGNAL instructions. Instructions pertaining only to trading signals
are located here."
(:require [propeller.tools.math :as math]
[propeller.push.instructions :refer [def-instruction
generate-instructions
make-instruction]]))

View File

@ -11,6 +11,11 @@
instruction (first (:exec state)) instruction (first (:exec state))
literal-type (instructions/get-literal-type instruction)] ; nil for non-literals literal-type (instructions/get-literal-type instruction)] ; nil for non-literals
(cond (cond
;;
;; Catch the trading signal keywords before the other
;; keyword checks.
(some #(= instruction %) '(:buy :sell :hold))
(state/push-to-stack popped-state :signal instruction)
;; ;;
;; Recognize functional instruction or input instruction ;; Recognize functional instruction or input instruction
(keyword? instruction) (keyword? instruction)

View File

@ -5,19 +5,21 @@
;; Empty push state - all available stacks are empty ;; Empty push state - all available stacks are empty
(defonce ^:no-doc empty-state {:boolean '() (defonce ^:no-doc empty-state {:boolean '()
:char '() :char '()
:code '() :code '()
:exec '() :exec '()
:float '() :float '()
:input {} :input {}
:output {} :output {}
:integer '() :integer '()
:print '("") :print '("")
:string '() :string '()
:vector_boolean '() :vector_boolean '()
:vector_float '() :vector_float '()
:vector_integer '() :vector_integer '()
:vector_string '()}) :vector_string '()
:signal '() ;; stock trading signal (:buy, :sell, or :hold only), long only for now
})
;; All stack types available in a Push state ;; All stack types available in a Push state
(defonce ^:no-doc stacks (set (keys empty-state))) (defonce ^:no-doc stacks (set (keys empty-state)))